Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/actions/setup-pixi/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

name: Setup pinned pixi
description: Install the repository's canonical pixi version without installing an environment

outputs:
pixi-version:
description: The canonical pixi version installed by this action
value: ${{ steps.version.outputs.pixi-version }}

runs:
using: composite
steps:
- name: Read canonical pixi version
id: version
shell: bash --noprofile --norc -euo pipefail {0}
run: |
source "${GITHUB_WORKSPACE}/ci/pixi-version.env"
if [[ ! "${PIXI_VERSION:-}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::ci/pixi-version.env must set PIXI_VERSION in vMAJOR.MINOR.PATCH form"
exit 1
fi
echo "pixi-version=${PIXI_VERSION}" >> "${GITHUB_OUTPUT}"

- name: Setup pixi ${{ steps.version.outputs.pixi-version }}
# Pinned to a commit SHA; install logic lives in the action and is
# auditable/pinned (vs. a curl|bash of an unverified installer).
uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6
with:
pixi-version: ${{ steps.version.outputs.pixi-version }}
run-install: false
23 changes: 12 additions & 11 deletions .github/workflows/ci-pixi-lockfile-freshness-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
# Remediation for both: regenerate and commit the lockfile with that pixi
# version, e.g.
# pixi lock --manifest-path <path>
# Keep this check advisory during its initial rollout. Before making it
# required, expose a stable aggregate check, account for the path filters, and
# record the decision in #2804 after the workflow has operational history.
# See #2298.

name: "CI: pixi lockfile freshness check"
Expand All @@ -28,6 +31,8 @@ on:
- "**/pixi.lock"
- "**/pyproject.toml"
- "ci/tools/list_pixi_workspaces.py"
- "ci/pixi-version.env"
- ".github/actions/setup-pixi/action.yml"
- ".github/workflows/ci-pixi-lockfile-freshness-check.yml"
push:
# `pull_request` already covers PRs, including those from forks: this check
Expand All @@ -43,9 +48,7 @@ defaults:
shell: bash --noprofile --norc -xeuo pipefail {0}

env:
# Keep in sync with ci-pixi-source-test.yml. Must be >=0.71.0 so the
# committed lockfile format (v7) matches what this job checks against.
PIXI_VERSION: "v0.73.0"
REFRESH_WORKFLOW_URL: "https://github.com/NVIDIA/cuda-python/actions/workflows/ci-pixi-lockfile-refresh.yml"

permissions: {}

Expand Down Expand Up @@ -92,17 +95,13 @@ jobs:
persist-credentials: false

- name: Setup pixi
# Pinned to a commit SHA; install logic lives in the action and is
# auditable/pinned (vs. a curl|bash of an unverified installer).
uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6
with:
pixi-version: ${{ env.PIXI_VERSION }}
run-install: false
id: setup-pixi
uses: ./.github/actions/setup-pixi

- name: Check lockfile is current with its manifest
run: |
if ! pixi lock --check --manifest-path "${{ matrix.manifest }}"; then
echo "::error::Lockfile is stale for '${{ matrix.manifest }}'. Regenerate with: pixi lock --manifest-path ${{ matrix.manifest }}"
echo "::error::Lockfile is stale for '${{ matrix.manifest }}'. Regenerate with: pixi lock --manifest-path ${{ matrix.manifest }}. For default-branch dependency drift, maintainers can run ${REFRESH_WORKFLOW_URL}"
exit 1
fi

Expand All @@ -112,8 +111,10 @@ jobs:
# is invisible: the check prints "Updated lock file" and still passes,
# while every later pixi run keeps rewriting the committed file (#2298).
- name: Check lockfile is byte-for-byte canonical
env:
PIXI_VERSION: ${{ steps.setup-pixi.outputs.pixi-version }}
run: |
if ! git diff --exit-code -- "${{ matrix.lockfile }}"; then
echo "::error::pixi ${PIXI_VERSION} rewrote ${{ matrix.lockfile }} during the check, so the committed bytes are not what it generates. Regenerate with pixi ${PIXI_VERSION}: pixi lock --manifest-path ${{ matrix.manifest }}"
echo "::error::pixi ${PIXI_VERSION} rewrote ${{ matrix.lockfile }} during the check, so the committed bytes are not what it generates. Regenerate with pixi ${PIXI_VERSION}: pixi lock --manifest-path ${{ matrix.manifest }}. For default-branch dependency drift, maintainers can run ${REFRESH_WORKFLOW_URL}"
exit 1
fi
82 changes: 41 additions & 41 deletions .github/workflows/ci-pixi-lockfile-refresh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ defaults:
run:
shell: bash --noprofile --norc -xeuo pipefail {0}

env:
# Keep in sync with ci-pixi-source-test.yml.
PIXI_VERSION: "v0.73.0"

permissions: {}

jobs:
Expand Down Expand Up @@ -71,7 +67,7 @@ jobs:
contents: write
pull-requests: write
concurrency:
group: pixi-lock-refresh-${{ matrix.id }}
group: pixi-lock-refresh-${{ matrix.branch_key }}
cancel-in-progress: false
strategy:
fail-fast: false
Expand All @@ -83,75 +79,75 @@ jobs:
fetch-depth: 0
ref: ${{ github.event.repository.default_branch }}

# GitHub suppresses workflow-triggering events for pushes and PRs made
# with GITHUB_TOKEN, so a refresh PR opened with the default token cannot
# reach a green required-check state on its own. An App token restores
# normal event delivery; without one the PR needs a manual nudge, which
# the PR body and a run warning spell out.
- name: Mint App token for the refresh PR
id: app-token
if: ${{ vars.PIXI_LOCK_REFRESH_APP_ID != '' }}
uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2.2.2
with:
app-id: ${{ vars.PIXI_LOCK_REFRESH_APP_ID }}
private-key: ${{ secrets.PIXI_LOCK_REFRESH_APP_PRIVATE_KEY }}

- name: Setup pixi
# Pinned to a commit SHA; install logic lives in the action and is
# auditable/pinned (vs. a curl|bash of an unverified installer).
uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6
with:
pixi-version: ${{ env.PIXI_VERSION }}
run-install: false
id: setup-pixi
uses: ./.github/actions/setup-pixi

- name: Update lockfile without installing
run: pixi update --no-install --manifest-path "${{ matrix.manifest }}"

# 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: Canonicalize the updated lockfile
run: pixi lock --check --manifest-path "${{ matrix.manifest }}"

- name: Verify canonicalization is byte-stable
env:
LOCKFILE: ${{ matrix.lockfile }}
MANIFEST: ${{ matrix.manifest }}
PIXI_VERSION: ${{ steps.setup-pixi.outputs.pixi-version }}
run: |
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

- name: Compose PR body
id: pr-body
env:
WORKSPACE_ID: ${{ matrix.id }}
MANIFEST: ${{ matrix.manifest }}
HAS_APP_TOKEN: ${{ steps.app-token.outputs.token != '' }}
PIXI_VERSION: ${{ steps.setup-pixi.outputs.pixi-version }}
run: |
body_path="${RUNNER_TEMP}/pixi-lock-refresh-body.md"
{
echo "Automated lockfile refresh for \`${WORKSPACE_ID}\`" \
"(\`pixi update --no-install --manifest-path ${MANIFEST}\`)."
echo "Automated lockfile refresh for \`${WORKSPACE_ID}\` using pixi" \
"\`${PIXI_VERSION}\` (\`pixi update --no-install\`, followed by" \
"byte-stable \`pixi lock --check\` canonicalization)."
echo
echo "This PR is workspace-scoped so other packages are not forced to review"
echo "unrelated solver churn. See #2298."
echo
if [ "${HAS_APP_TOKEN}" != "true" ]; then
echo "> [!IMPORTANT]"
echo "> No refresh App token is configured, so this PR was opened with"
echo "> \`GITHUB_TOKEN\` and GitHub will not deliver its \`pull_request\` events."
echo "> Required checks stay pending until a maintainer nudges the branch, by"
echo "> closing and reopening the PR or pushing an empty commit to it."
echo
fi
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}"

if [ "${HAS_APP_TOKEN}" != "true" ]; then
echo "::warning::Opened with GITHUB_TOKEN; the refresh PR's checks must be triggered manually."
fi

- name: Open or update a lockfile-only PR
id: cpr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ steps.app-token.outputs.token || github.token }}
token: ${{ github.token }}
add-paths: ${{ matrix.lockfile }}
commit-message: |
chore: refresh pixi.lock for ${{ matrix.id }}

Scheduled pixi update --no-install for this workspace only.
signoff: true
sign-commits: true
branch: ci/pixi-lock-refresh/${{ matrix.id }}
branch: ci/pixi-lock-refresh/${{ matrix.branch_key }}
delete-branch: true
title: "chore: refresh pixi.lock (${{ matrix.id }})"
labels: |
Expand All @@ -161,6 +157,10 @@ jobs:
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: |
{
Expand Down
30 changes: 5 additions & 25 deletions .github/workflows/ci-pixi-source-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ on:
- "cuda_bindings/tests/cython/**"
- "cuda_core/tests/cython/**"
- "ci/versions.yml"
- "ci/pixi-version.env"
- ".github/actions/setup-pixi/action.yml"
- ".github/workflows/ci-pixi-source-test.yml"
workflow_dispatch:
inputs:
Expand All @@ -60,13 +62,6 @@ defaults:
shell: bash --noprofile --norc -xeuo pipefail {0}

env:
# keep in sync with the version developers run locally. Must be >=0.71.0:
# older pixi re-ran the editable source build on every `pixi run`, recompiling
# all Cython extensions (#2138). The fix (content-addressed source-build cache,
# prefix-dev/pixi#6285 + #6123) also bumps the pixi.lock format to v7.
# Keep in sync with ci-pixi-lockfile-refresh.yml and
# ci-pixi-lockfile-freshness-check.yml.
PIXI_VERSION: "v0.73.0"
# Install from the committed lockfile only. Covers nested `pixi run` calls
# from root pixi.toml tasks; a stale lock must fail rather than being
# refreshed in CI (#2298). Equivalent to `pixi run --locked`.
Expand All @@ -91,12 +86,7 @@ jobs:
fetch-depth: 0

- name: Setup pixi
# Pinned to a commit SHA; install logic lives in the action and is
# auditable/pinned (vs. a curl|bash of an unverified installer).
uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6
with:
pixi-version: ${{ env.PIXI_VERSION }}
run-install: false
uses: ./.github/actions/setup-pixi

- name: Source-build + import + cython-placement smoke
env:
Expand Down Expand Up @@ -152,12 +142,7 @@ jobs:
fetch-depth: 0

- name: Setup pixi
# Pinned to a commit SHA; install logic lives in the action and is
# auditable/pinned (vs. a curl|bash of an unverified installer).
uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6
with:
pixi-version: ${{ env.PIXI_VERSION }}
run-install: false
uses: ./.github/actions/setup-pixi

- name: Build cu13, then cu12, then cu13 again in one checkout
run: |
Expand Down Expand Up @@ -217,12 +202,7 @@ jobs:
continue-on-error: true

- name: Setup pixi
# Pinned to a commit SHA; install logic lives in the action and is
# auditable/pinned (vs. a curl|bash of an unverified installer).
uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6
with:
pixi-version: ${{ env.PIXI_VERSION }}
run-install: false
uses: ./.github/actions/setup-pixi

- name: pixi run test
run: pixi run -e "${{ inputs.cuda-env || 'cu13' }}" test
Loading
Loading