Repository navigation
CI: pixi lockfile refresh #7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # | |
| # SPDX-License-Identifier: Apache-2.0 | |
| # Scheduled (and on-demand) pixi lockfile refresh. All workspaces are updated | |
| # in one job and, when anything changes, one PR. See #2298 and #2849. | |
| name: "CI: pixi lockfile refresh" | |
| on: | |
| schedule: | |
| # Monday 03:17 Eastern. GitHub handles the DST offset for the IANA zone, | |
| # and a non-zero minute avoids the start-of-hour window where GitHub | |
| # documents scheduled runs being delayed or dropped. | |
| - cron: "17 3 * * 1" | |
| timezone: "America/New_York" | |
| workflow_dispatch: {} | |
| defaults: | |
| run: | |
| shell: bash --noprofile --norc -xeuo pipefail {0} | |
| permissions: {} | |
| jobs: | |
| refresh: | |
| name: pixi update (all workspaces) | |
| if: ${{ github.repository_owner == 'nvidia' }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 195 | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| concurrency: | |
| group: pixi-lock-refresh | |
| cancel-in-progress: false | |
| steps: | |
| - name: Checkout ${{ github.event.repository.name }} | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.repository.default_branch }} | |
| - name: Setup pixi | |
| id: setup-pixi | |
| uses: ./.github/actions/setup-pixi | |
| # Pixi 0.73.0's update and lock-read paths can serialize the same solve | |
| # differently (#2804). Canonicalize with the same operation used by the | |
| # freshness workflow before opening the PR. | |
| - name: Update and canonicalize all lockfiles | |
| id: update | |
| env: | |
| PIXI_VERSION: ${{ steps.setup-pixi.outputs.pixi-version }} | |
| WORKSPACE_TIMEOUT: 30m | |
| run: | | |
| workspaces="$(python3 ci/tools/list_pixi_workspaces.py)" | |
| workspace_rows="$( | |
| jq -ce ' | |
| if type != "array" or length == 0 then | |
| error("workspace inventory must be a non-empty array") | |
| elif any(.[]; | |
| type != "object" | |
| or (.manifest | type != "string") | |
| or (.lockfile | type != "string") | |
| or .manifest == "" | |
| or .lockfile == "" | |
| ) then | |
| error("each workspace must have non-empty string manifest and lockfile fields") | |
| else | |
| .[] | |
| end | |
| ' <<<"${workspaces}" | |
| )" | |
| while IFS= read -r workspace; do | |
| manifest="$(jq -r '.manifest' <<<"${workspace}")" | |
| lockfile="$(jq -r '.lockfile' <<<"${workspace}")" | |
| echo "::group::pixi update (${manifest})" | |
| workspace_status=0 | |
| # The single-quoted script expands its variables in the child shell. | |
| # shellcheck disable=SC2016 | |
| timeout --kill-after=1m "${WORKSPACE_TIMEOUT}" \ | |
| bash --noprofile --norc -xeuo pipefail -c ' | |
| manifest="$1" | |
| lockfile="$2" | |
| pixi_version="$3" | |
| pixi update --no-install --manifest-path "${manifest}" | |
| pixi lock --check --manifest-path "${manifest}" | |
| canonical_blob="$(git hash-object "${lockfile}")" | |
| pixi lock --check --manifest-path "${manifest}" | |
| verified_blob="$(git hash-object "${lockfile}")" | |
| if [ "${canonical_blob}" != "${verified_blob}" ]; then | |
| echo "::error::pixi ${pixi_version} rewrote ${lockfile} on a second consecutive pixi lock --check; refusing to open an unstable refresh PR." | |
| exit 1 | |
| fi | |
| ' bash "${manifest}" "${lockfile}" "${PIXI_VERSION}" \ | |
| || workspace_status=$? | |
| if ((workspace_status == 124 || workspace_status == 137)); then | |
| echo "::error::Timed out after ${WORKSPACE_TIMEOUT} while refreshing '${manifest}'." | |
| elif ((workspace_status != 0)); then | |
| echo "::error::Failed to refresh '${manifest}' (exit ${workspace_status})." | |
| fi | |
| echo "::endgroup::" | |
| if ((workspace_status != 0)); then | |
| exit "${workspace_status}" | |
| fi | |
| done <<<"${workspace_rows}" | |
| { | |
| echo "lockfiles<<EOF" | |
| jq -r '.[].lockfile' <<<"${workspaces}" | |
| echo "EOF" | |
| } >> "${GITHUB_OUTPUT}" | |
| - name: Compose PR body | |
| id: pr-body | |
| env: | |
| PIXI_VERSION: ${{ steps.setup-pixi.outputs.pixi-version }} | |
| run: | | |
| body_path="${RUNNER_TEMP}/pixi-lock-refresh-body.md" | |
| { | |
| echo "Automated lockfile refresh for all workspaces using pixi" \ | |
| "\`${PIXI_VERSION}\` (\`pixi update --no-install\`, followed by" \ | |
| "byte-stable \`pixi lock --check\` canonicalization)." | |
| echo | |
| echo "Lockfiles included in the refresh:" | |
| python3 ci/tools/list_pixi_workspaces.py \ | |
| | jq -r '.[] | "- `\(.lockfile)`"' | |
| echo | |
| echo "See #2298 and #2849." | |
| echo | |
| echo "> [!IMPORTANT]" | |
| echo "> This PR was opened with \`GITHUB_TOKEN\`. A maintainer with write access must:" | |
| echo ">" | |
| echo "> 1. Select **Approve workflows to run** in the merge box." | |
| echo "> 2. Assign themselves to this PR. The human-generated \`assigned\` event" | |
| echo "> creates the required **PR has assignee, labels, and milestone** check" | |
| echo "> and gives the PR a clear owner." | |
| echo | |
| echo "If the diff looks wrong, close this PR and leave the branch deleted; the" | |
| echo "next scheduled run opens a fresh one if an update is still needed." | |
| } > "${body_path}" | |
| echo "path=${body_path}" >> "${GITHUB_OUTPUT}" | |
| - name: Open or update a lockfile-only PR | |
| id: cpr | |
| uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 | |
| with: | |
| token: ${{ github.token }} | |
| add-paths: ${{ steps.update.outputs.lockfiles }} | |
| commit-message: "chore: refresh pixi lockfiles" | |
| signoff: true | |
| sign-commits: true | |
| branch: ci/pixi-lock-refresh/all | |
| delete-branch: true | |
| title: "chore: refresh pixi lockfiles" | |
| labels: | | |
| CI/CD | |
| dependencies | |
| body-path: ${{ steps.pr-body.outputs.path }} | |
| author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> | |
| committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> | |
| - name: Warn about required maintainer action | |
| if: ${{ steps.cpr.outputs.pull-request-operation == 'created' || steps.cpr.outputs.pull-request-operation == 'updated' }} | |
| run: echo "::warning::The refresh PR requires a maintainer to approve its workflow runs and assign themselves to create the required metadata check." | |
| - name: Summarize | |
| run: | | |
| { | |
| echo "### pixi lock refresh (all workspaces)" | |
| echo "" | |
| echo "- Operation: \`${{ steps.cpr.outputs.pull-request-operation }}\`" | |
| if [ -n "${{ steps.cpr.outputs.pull-request-url }}" ]; then | |
| echo "- PR: ${{ steps.cpr.outputs.pull-request-url }}" | |
| else | |
| echo "- No lockfile diff; PR not opened." | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" |