From e7badefed3d4b31bbda53d423454309a50e42d0d Mon Sep 17 00:00:00 2001 From: Sowmya Viswanathan Date: Sun, 24 May 2026 12:56:07 -0700 Subject: [PATCH 1/5] feat(prerelease-setup): add composite action for pre-release workflow setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracts ~100 lines of duplicated setup steps from loft-sh/loft-enterprise .github/workflows/prerelease-checks.yaml (prerelease-vcluster + prerelease-aicloud jobs) into a single composite action. The action performs: free disk space, checkout, setup-go (cache on, go-version-file: go.mod), setup-kubectl, setup-helm, OIDC AWS Login (role-session-name is the only AWS input that differs between the two jobs), resolve+validate the standalone vCluster base/upgrade and platform base/RC versions (with curl/jq fallbacks to "latest release" / "latest pre-release"), download the vCluster CLI matching the resolved base, and verify kubectl/helm/vcluster are on PATH. Resolved versions are written to GITHUB_OUTPUT and mirrored to GITHUB_ENV so the consumer's downstream test step (run-ginkgo) keeps reading them as env vars exactly as in the inlined version. The action does NOT include EC2 provisioning (aws-test-infra) or test execution (run-ginkgo) — those remain inline in the consumer because only the AI Cloud job needs EC2 and both jobs run different test directories. Mirrors the convention from PR #139 (aws-test-infra). Refs ENGQA-1042. --- .github/actions/prerelease-setup/README.md | 121 +++++++++++++ .github/actions/prerelease-setup/action.yml | 182 ++++++++++++++++++++ 2 files changed, 303 insertions(+) create mode 100644 .github/actions/prerelease-setup/README.md create mode 100644 .github/actions/prerelease-setup/action.yml diff --git a/.github/actions/prerelease-setup/README.md b/.github/actions/prerelease-setup/README.md new file mode 100644 index 0000000..5933e2a --- /dev/null +++ b/.github/actions/prerelease-setup/README.md @@ -0,0 +1,121 @@ +# Pre-Release Setup + +Shared setup for the `loft-sh/loft-enterprise` pre-release workflow +(`.github/workflows/prerelease-checks.yaml`). Replaces ~100 lines of +duplicated setup steps that lived inline in both the `prerelease-vcluster` +and `prerelease-aicloud` jobs. + +The action performs, in order: + +1. Free disk space (`jlumbroso/free-disk-space@v1.3.1`). +2. Checkout the calling repo. +3. Install Go (`actions/setup-go@v5`, `go-version-file: go.mod`, cache on). +4. Install `kubectl` (`azure/setup-kubectl@v4`). +5. Install `helm` (`azure/setup-helm@v4`). +6. AWS Login via OIDC (`aws-actions/configure-aws-credentials@v5.1.1`, + `role-to-assume: arn:aws:iam::084374023943:role/e2e-test-executor`, + `aws-region: us-west-2`, `role-duration-seconds: 6300`, + `output-credentials: true`). +7. Resolve and validate the four version inputs (see below). +8. Download the `vcluster` CLI binary that matches the resolved base + standalone vCluster version. +9. Verify `kubectl`, `helm`, and `vcluster` are on `$PATH`. + +The action is intended for the two pre-release jobs only. AI Cloud's EC2 +provisioning (`aws-test-infra`) and the Ginkgo test execution +(`run-ginkgo`) remain in the calling workflow. + +## Inputs + +| Name | Required | Default | Description | +|------|----------|---------|-------------| +| `role-session-name` | yes | — | AWS STS role-session-name. Pass a job-distinct value such as `prerelease-vcluster-${{ github.run_id }}`. | +| `standalone-vcluster-version` | no | `''` | Standalone vCluster install version (e.g. `0.34.0`). Empty resolves to the latest GitHub release of `loft-sh/vcluster`. | +| `standalone-vcluster-upgrade-version` | yes | — | Standalone vCluster upgrade target (e.g. `0.35.0-alpha.7`). Must differ from the resolved base. | +| `platform-base-version` | no | `''` | Platform install version (e.g. `4.9.0`). Empty leaves `PLATFORM_BASE_VERSION` unset; the consumer test step uses its own default. | +| `platform-rc-version` | no | `''` | Platform RC upgrade version (e.g. `4.10.0-alpha.6`). Empty resolves to the latest pre-release of `loft-sh/loft-enterprise`. | +| `vci-k8s-version` | no | `''` | Pass-through for the private-nodes VCI Kubernetes version (v-prefixed, e.g. `v1.34.5`). Forwarded to `$GITHUB_ENV` as `VCI_K8S_VERSION`. | +| `vci-k8s-upgrade-version` | no | `''` | Pass-through for the VCI K8s upgrade target (v-prefixed). Forwarded to `$GITHUB_ENV` as `VCI_K8S_UPGRADE_VERSION`. | + +Inputs accept versions with or without a leading `v`; the action strips +the `v` before validating against the semver regex +`^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$`. + +## Outputs + +| Name | Description | +|------|-------------| +| `standalone-vcluster-version` | Resolved standalone vCluster version (no leading `v`). | +| `standalone-vcluster-upgrade-version` | Validated upgrade version (no leading `v`). | +| `platform-base-version` | Validated platform base version (no leading `v`). Empty when the input was empty. | +| `platform-rc-version` | Resolved platform RC version (no leading `v`). | + +## Environment variables exported to subsequent steps + +The action also writes the resolved values to `$GITHUB_ENV` so the +calling job's downstream steps (in particular `run-ginkgo`) read them +as plain env vars, matching the behaviour of the inlined version: + +- `STANDALONE_VCLUSTER_VERSION` +- `DEFAULT_VCLUSTER_CHART_VERSION` (same value as + `STANDALONE_VCLUSTER_VERSION`; consumed by some test paths) +- `STANDALONE_VCLUSTER_UPGRADE_VERSION` +- `PLATFORM_RC_VERSION` +- `PLATFORM_BASE_VERSION` (only when `platform-base-version` was provided) +- `VCI_K8S_VERSION` (only when `vci-k8s-version` was provided) +- `VCI_K8S_UPGRADE_VERSION` (only when `vci-k8s-upgrade-version` was provided) + +The OIDC step exports `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and +`AWS_SESSION_TOKEN` to the environment via +`aws-actions/configure-aws-credentials` with `output-credentials: true`. + +## Permissions + +The calling job must declare: + +```yaml +permissions: + contents: read + id-token: write +``` + +`id-token: write` is required for the OIDC `assume-role` exchange. + +## Usage + +```yaml +jobs: + prerelease-vcluster: + runs-on: ubuntu-latest + timeout-minutes: 90 + permissions: + contents: read + id-token: write + env: + STANDALONE_VCLUSTER_UPGRADE_VERSION: ${{ inputs.standalone_vcluster_upgrade_version || github.event.client_payload.standalone_vcluster_upgrade_version }} + PLATFORM_BASE_VERSION: ${{ inputs.platform_base_version || github.event.client_payload.platform_base_version }} + PLATFORM_RC_VERSION: ${{ inputs.platform_rc_version || github.event.client_payload.platform_rc_version }} + steps: + - name: Pre-release setup + uses: loft-sh/github-actions/.github/actions/prerelease-setup@prerelease-setup/v1 + with: + role-session-name: prerelease-vcluster-${{ github.run_id }} + standalone-vcluster-version: ${{ inputs.standalone_vcluster_version || github.event.client_payload.standalone_vcluster_version }} + standalone-vcluster-upgrade-version: ${{ env.STANDALONE_VCLUSTER_UPGRADE_VERSION }} + platform-base-version: ${{ env.PLATFORM_BASE_VERSION }} + platform-rc-version: ${{ env.PLATFORM_RC_VERSION }} + + - name: Run pre-release vCluster checks + uses: loft-sh/github-actions/.github/actions/run-ginkgo@run-ginkgo/v1 + with: + test-dir: e2e/prerelease/vcluster + ginkgo-label: prerelease-upgrade + timeout: 80m + procs: "1" + additional-ginkgo-flags: "-v" +``` + +The AI Cloud job is identical apart from a different `role-session-name`, +the addition of `vci-k8s-version` / `vci-k8s-upgrade-version` inputs, and +the surrounding `aws-test-infra` provision/cleanup steps that are +specific to that job. diff --git a/.github/actions/prerelease-setup/action.yml b/.github/actions/prerelease-setup/action.yml new file mode 100644 index 0000000..8cefb5c --- /dev/null +++ b/.github/actions/prerelease-setup/action.yml @@ -0,0 +1,182 @@ +name: 'Pre-Release Setup' +description: 'Shared setup for the loft-enterprise pre-release vCluster + AI Cloud workflow jobs: free disk, checkout, install Go/kubectl/helm/vCluster CLI, AWS Login, and resolve+validate the four version inputs (standalone vCluster base+upgrade, platform base+RC) including "latest" fallback resolvers.' + +inputs: + role-session-name: + description: 'AWS STS role-session-name. Each consumer job passes a distinct value (e.g. prerelease-vcluster-, prerelease-aicloud-).' + required: true + standalone-vcluster-version: + description: 'vCluster version to install for standalone (e.g. 0.34.0). Empty resolves to the latest GitHub release of loft-sh/vcluster.' + required: false + default: '' + standalone-vcluster-upgrade-version: + description: 'vCluster version to upgrade standalone to (e.g. 0.35.0-alpha.7). Must differ from the resolved base version.' + required: true + platform-base-version: + description: 'Platform version for the initial install (e.g. 4.9.0). Empty leaves PLATFORM_BASE_VERSION unset; the consumer test step uses its own default.' + required: false + default: '' + platform-rc-version: + description: 'Platform RC version for upgrade (e.g. 4.10.0-alpha.6). Empty resolves to the latest pre-release of loft-sh/loft-enterprise.' + required: false + default: '' + vci-k8s-version: + description: 'Kubernetes version for the private-nodes VCI (v-prefixed, e.g. v1.34.5). Pass-through — the consumer workflow is expected to pin a default and forward it here so it appears as an env var to subsequent steps.' + required: false + default: '' + vci-k8s-upgrade-version: + description: 'Kubernetes version to upgrade the private-nodes VCI to (v-prefixed, e.g. v1.35.0). Pass-through — see vci-k8s-version.' + required: false + default: '' + +outputs: + standalone-vcluster-version: + description: 'Resolved standalone vCluster version (no leading v).' + value: ${{ steps.resolve.outputs.standalone-vcluster-version }} + standalone-vcluster-upgrade-version: + description: 'Validated standalone vCluster upgrade version (no leading v).' + value: ${{ steps.resolve.outputs.standalone-vcluster-upgrade-version }} + platform-base-version: + description: 'Validated platform base version (no leading v). Empty when the input was empty.' + value: ${{ steps.resolve.outputs.platform-base-version }} + platform-rc-version: + description: 'Resolved platform RC version (no leading v).' + value: ${{ steps.resolve.outputs.platform-rc-version }} + +runs: + using: 'composite' + steps: + - name: Free disk space + uses: jlumbroso/free-disk-space@v1.3.1 + with: + tool-cache: false + + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Setup kubectl + uses: azure/setup-kubectl@v4 + + - name: Setup helm + uses: azure/setup-helm@v4 + + - name: AWS Login + uses: aws-actions/configure-aws-credentials@v5.1.1 + with: + role-to-assume: arn:aws:iam::084374023943:role/e2e-test-executor + role-session-name: ${{ inputs.role-session-name }} + aws-region: us-west-2 + output-credentials: true + role-duration-seconds: 6300 + + - name: Resolve and validate versions + id: resolve + shell: bash + env: + STANDALONE_VCLUSTER_VERSION_INPUT: ${{ inputs.standalone-vcluster-version }} + STANDALONE_VCLUSTER_UPGRADE_VERSION_INPUT: ${{ inputs.standalone-vcluster-upgrade-version }} + PLATFORM_BASE_VERSION_INPUT: ${{ inputs.platform-base-version }} + PLATFORM_RC_VERSION_INPUT: ${{ inputs.platform-rc-version }} + VCI_K8S_VERSION_INPUT: ${{ inputs.vci-k8s-version }} + VCI_K8S_UPGRADE_VERSION_INPUT: ${{ inputs.vci-k8s-upgrade-version }} + run: | + set -euo pipefail + # Accept inputs with or without a leading `v`; normalize to no-v before exporting. + VERSION_RE='^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$' + + VERSION="$STANDALONE_VCLUSTER_VERSION_INPUT" + if [ -z "$VERSION" ]; then + VERSION=$(curl -fsSL https://api.github.com/repos/loft-sh/vcluster/releases/latest | jq -r .tag_name) + fi + VERSION="${VERSION#v}" + [[ "$VERSION" =~ $VERSION_RE ]] || { echo "invalid standalone-vcluster-version: $VERSION"; exit 1; } + echo "Resolved vCluster version: $VERSION" + + UP="${STANDALONE_VCLUSTER_UPGRADE_VERSION_INPUT#v}" + [[ "$UP" =~ $VERSION_RE ]] || { echo "invalid standalone-vcluster-upgrade-version: $UP"; exit 1; } + [ "$UP" != "$VERSION" ] || { echo "standalone-vcluster-upgrade-version must differ from base ($VERSION)"; exit 1; } + + RC="${PLATFORM_RC_VERSION_INPUT#v}" + if [ -z "$RC" ]; then + # GitHub's /releases/latest excludes pre-releases, so list /releases and filter on `prerelease == true`. + # The list is newest-first by created_at, so `[0]` after filter = latest pre-release. + RC=$(curl -fsSL https://api.github.com/repos/loft-sh/loft-enterprise/releases \ + | jq -r '[.[] | select(.prerelease == true and .draft == false)][0].tag_name') + [ -n "$RC" ] && [ "$RC" != "null" ] || { echo "no platform pre-release found in loft-sh/loft-enterprise"; exit 1; } + RC="${RC#v}" + echo "Resolved latest platform pre-release: $RC" + fi + [[ "$RC" =~ $VERSION_RE ]] || { echo "invalid platform-rc-version: $RC"; exit 1; } + + BASE="${PLATFORM_BASE_VERSION_INPUT#v}" + if [ -n "$BASE" ]; then + [[ "$BASE" =~ $VERSION_RE ]] || { echo "invalid platform-base-version: $BASE"; exit 1; } + [ "$BASE" != "$RC" ] || { echo "platform-base-version must differ from platform-rc-version"; exit 1; } + fi + + { + echo "standalone-vcluster-version=$VERSION" + echo "standalone-vcluster-upgrade-version=$UP" + echo "platform-rc-version=$RC" + echo "platform-base-version=$BASE" + } >> "$GITHUB_OUTPUT" + + # Also mirror to $GITHUB_ENV so the next composite step (Setup vCluster + # CLI) and the consumer's downstream test step read the same env vars + # the inlined version of this setup exported. This preserves behavioural + # parity with the pre-extraction workflow. + { + echo "STANDALONE_VCLUSTER_VERSION=$VERSION" + echo "DEFAULT_VCLUSTER_CHART_VERSION=$VERSION" + echo "STANDALONE_VCLUSTER_UPGRADE_VERSION=$UP" + echo "PLATFORM_RC_VERSION=$RC" + } >> "$GITHUB_ENV" + if [ -n "$BASE" ]; then + echo "PLATFORM_BASE_VERSION=$BASE" >> "$GITHUB_ENV" + fi + + # VCI K8s versions are pass-through: the consumer pins a default and + # forwards it here; we re-export to env so the test step sees it + # without the consumer wiring per-step env: blocks. + if [ -n "$VCI_K8S_VERSION_INPUT" ]; then + echo "VCI_K8S_VERSION=$VCI_K8S_VERSION_INPUT" >> "$GITHUB_ENV" + fi + if [ -n "$VCI_K8S_UPGRADE_VERSION_INPUT" ]; then + echo "VCI_K8S_UPGRADE_VERSION=$VCI_K8S_UPGRADE_VERSION_INPUT" >> "$GITHUB_ENV" + fi + + - name: Setup vCluster CLI (matches base standalone version) + shell: bash + env: + STANDALONE_VCLUSTER_VERSION: ${{ steps.resolve.outputs.standalone-vcluster-version }} + run: | + set -euo pipefail + # Initial CLI matches the base standalone vCluster — used by the + # initial `vcluster platform start` in BeforeSuite. The test swaps + # this binary to the upgrade-target CLI before the platform upgrade + # spec runs. + URL="https://github.com/loft-sh/vcluster/releases/download/v${STANDALONE_VCLUSTER_VERSION}/vcluster-linux-amd64" + sudo curl -fL "${URL}" -o /usr/local/bin/vcluster + sudo chmod +x /usr/local/bin/vcluster + vcluster --version + + - name: Verify required CLIs + shell: bash + run: | + set -e + echo "kubectl: $(command -v kubectl)" + kubectl version --client + echo "helm: $(command -v helm)" + helm version + echo "vcluster: $(command -v vcluster)" + vcluster --version + +branding: + icon: 'package' + color: 'orange' From 42ca79b78181933372af24ee3c928df5d75641bb Mon Sep 17 00:00:00 2001 From: Sowmya Viswanathan Date: Sun, 24 May 2026 15:15:19 -0700 Subject: [PATCH 2/5] fix(prerelease-setup): write only to GITHUB_OUTPUT; add auto-doc markers; drop vci-k8s inputs Two CI fixes plus a scope tightening: - zizmor (github-env): the previous revision mirrored resolved versions to GITHUB_ENV so the consumer's downstream test step could read them as env vars. zizmor flagged this as a code-execution risk and the repo convention (aws-test-infra) writes only to GITHUB_OUTPUT. The consumer now wires outputs to env via the run-ginkgo step's env: block (shown in README Usage). - check-docs: README must use the AUTO-DOC-INPUT / AUTO-DOC-OUTPUT markers consumed by tj-actions/auto-doc; hand-written tables removed and the markers populated via `make generate-docs`. - vci-k8s-version / vci-k8s-upgrade-version inputs dropped. They are not produced or validated by any setup step and are already available to the consumer at the workflow env: block; carrying them as pass-through-only inputs would be dead code. README "Notes" calls this deviation from the original ticket scope out explicitly. --- .github/actions/prerelease-setup/README.md | 81 +++++++++++++-------- .github/actions/prerelease-setup/action.yml | 36 +-------- 2 files changed, 51 insertions(+), 66 deletions(-) diff --git a/.github/actions/prerelease-setup/README.md b/.github/actions/prerelease-setup/README.md index 5933e2a..903a0ce 100644 --- a/.github/actions/prerelease-setup/README.md +++ b/.github/actions/prerelease-setup/README.md @@ -8,7 +8,7 @@ and `prerelease-aicloud` jobs. The action performs, in order: 1. Free disk space (`jlumbroso/free-disk-space@v1.3.1`). -2. Checkout the calling repo. +2. Checkout the calling repo (`actions/checkout@v6`). 3. Install Go (`actions/setup-go@v5`, `go-version-file: go.mod`, cache on). 4. Install `kubectl` (`azure/setup-kubectl@v4`). 5. Install `helm` (`azure/setup-helm@v4`). @@ -27,15 +27,17 @@ provisioning (`aws-test-infra`) and the Ginkgo test execution ## Inputs -| Name | Required | Default | Description | -|------|----------|---------|-------------| -| `role-session-name` | yes | — | AWS STS role-session-name. Pass a job-distinct value such as `prerelease-vcluster-${{ github.run_id }}`. | -| `standalone-vcluster-version` | no | `''` | Standalone vCluster install version (e.g. `0.34.0`). Empty resolves to the latest GitHub release of `loft-sh/vcluster`. | -| `standalone-vcluster-upgrade-version` | yes | — | Standalone vCluster upgrade target (e.g. `0.35.0-alpha.7`). Must differ from the resolved base. | -| `platform-base-version` | no | `''` | Platform install version (e.g. `4.9.0`). Empty leaves `PLATFORM_BASE_VERSION` unset; the consumer test step uses its own default. | -| `platform-rc-version` | no | `''` | Platform RC upgrade version (e.g. `4.10.0-alpha.6`). Empty resolves to the latest pre-release of `loft-sh/loft-enterprise`. | -| `vci-k8s-version` | no | `''` | Pass-through for the private-nodes VCI Kubernetes version (v-prefixed, e.g. `v1.34.5`). Forwarded to `$GITHUB_ENV` as `VCI_K8S_VERSION`. | -| `vci-k8s-upgrade-version` | no | `''` | Pass-through for the VCI K8s upgrade target (v-prefixed). Forwarded to `$GITHUB_ENV` as `VCI_K8S_UPGRADE_VERSION`. | + + +| INPUT | TYPE | REQUIRED | DEFAULT | DESCRIPTION | +|-------------------------------------|--------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| platform-base-version | string | false | | Platform version for the initial install
(e.g. 4.9.0). Empty leaves the output empty;
the consumer wires its own default
into the test step. | +| platform-rc-version | string | false | | Platform RC version for upgrade (e.g. 4.10.0-alpha.6).
Empty resolves to the latest pre-release
of loft-sh/loft-enterprise. | +| role-session-name | string | true | | AWS STS role-session-name. Each consumer job
passes a distinct value (e.g. prerelease-vcluster-, prerelease-aicloud-). | +| standalone-vcluster-upgrade-version | string | true | | vCluster version to upgrade standalone to
(e.g. 0.35.0-alpha.7). Must differ from the resolved
base version. | +| standalone-vcluster-version | string | false | | vCluster version to install for standalone
(e.g. 0.34.0). Empty resolves to the latest
GitHub release of loft-sh/vcluster. | + + Inputs accept versions with or without a leading `v`; the action strips the `v` before validating against the semver regex @@ -43,31 +45,29 @@ the `v` before validating against the semver regex ## Outputs -| Name | Description | -|------|-------------| -| `standalone-vcluster-version` | Resolved standalone vCluster version (no leading `v`). | -| `standalone-vcluster-upgrade-version` | Validated upgrade version (no leading `v`). | -| `platform-base-version` | Validated platform base version (no leading `v`). Empty when the input was empty. | -| `platform-rc-version` | Resolved platform RC version (no leading `v`). | + -## Environment variables exported to subsequent steps +| OUTPUT | TYPE | DESCRIPTION | +|-------------------------------------|--------|--------------------------------------------------------------------------------------| +| platform-base-version | string | Validated platform base version (no leading v). Empty
when the input was empty. | +| platform-rc-version | string | Resolved platform RC version (no leading v). | +| standalone-vcluster-upgrade-version | string | Validated standalone vCluster upgrade version (no leading v). | +| standalone-vcluster-version | string | Resolved standalone vCluster version (no leading v). | -The action also writes the resolved values to `$GITHUB_ENV` so the -calling job's downstream steps (in particular `run-ginkgo`) read them -as plain env vars, matching the behaviour of the inlined version: + -- `STANDALONE_VCLUSTER_VERSION` -- `DEFAULT_VCLUSTER_CHART_VERSION` (same value as - `STANDALONE_VCLUSTER_VERSION`; consumed by some test paths) -- `STANDALONE_VCLUSTER_UPGRADE_VERSION` -- `PLATFORM_RC_VERSION` -- `PLATFORM_BASE_VERSION` (only when `platform-base-version` was provided) -- `VCI_K8S_VERSION` (only when `vci-k8s-version` was provided) -- `VCI_K8S_UPGRADE_VERSION` (only when `vci-k8s-upgrade-version` was provided) +Outputs are written to `$GITHUB_OUTPUT` only. The consumer wires them to +its downstream test step via an `env:` block (see Usage below). This +matches the convention of `aws-test-infra` and avoids the +`github-env` zizmor finding that comes with mirroring values into +`$GITHUB_ENV` from a composite step. The OIDC step exports `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` to the environment via `aws-actions/configure-aws-credentials` with `output-credentials: true`. +These propagate to subsequent steps in the calling job through the +standard action mechanism, so the consumer does not need to wire them +explicitly. ## Permissions @@ -97,6 +97,7 @@ jobs: PLATFORM_RC_VERSION: ${{ inputs.platform_rc_version || github.event.client_payload.platform_rc_version }} steps: - name: Pre-release setup + id: setup uses: loft-sh/github-actions/.github/actions/prerelease-setup@prerelease-setup/v1 with: role-session-name: prerelease-vcluster-${{ github.run_id }} @@ -113,9 +114,27 @@ jobs: timeout: 80m procs: "1" additional-ginkgo-flags: "-v" + env: + STANDALONE_VCLUSTER_VERSION: ${{ steps.setup.outputs.standalone-vcluster-version }} + DEFAULT_VCLUSTER_CHART_VERSION: ${{ steps.setup.outputs.standalone-vcluster-version }} + STANDALONE_VCLUSTER_UPGRADE_VERSION: ${{ steps.setup.outputs.standalone-vcluster-upgrade-version }} + PLATFORM_BASE_VERSION: ${{ steps.setup.outputs.platform-base-version }} + PLATFORM_RC_VERSION: ${{ steps.setup.outputs.platform-rc-version }} + AWS_ACCESS_KEY_ID: ${{ env.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ env.AWS_SECRET_ACCESS_KEY }} + AWS_SESSION_TOKEN: ${{ env.AWS_SESSION_TOKEN }} ``` The AI Cloud job is identical apart from a different `role-session-name`, -the addition of `vci-k8s-version` / `vci-k8s-upgrade-version` inputs, and -the surrounding `aws-test-infra` provision/cleanup steps that are -specific to that job. +the addition of `VCI_K8S_VERSION` / `VCI_K8S_UPGRADE_VERSION` (kept at +the workflow `env:` block in the consumer, not passed through this +action), and the surrounding `aws-test-infra` provision/cleanup steps +that are specific to that job. + +## Notes + +- The two `vci-k8s-*` inputs called out in the original ticket scope are + intentionally not part of this action. They are not produced or + validated by any of the setup steps, and they are already available to + the consumer at the workflow `env:` level. Adding them as + pass-through-only inputs would be dead code. diff --git a/.github/actions/prerelease-setup/action.yml b/.github/actions/prerelease-setup/action.yml index 8cefb5c..3e4d818 100644 --- a/.github/actions/prerelease-setup/action.yml +++ b/.github/actions/prerelease-setup/action.yml @@ -13,21 +13,13 @@ inputs: description: 'vCluster version to upgrade standalone to (e.g. 0.35.0-alpha.7). Must differ from the resolved base version.' required: true platform-base-version: - description: 'Platform version for the initial install (e.g. 4.9.0). Empty leaves PLATFORM_BASE_VERSION unset; the consumer test step uses its own default.' + description: 'Platform version for the initial install (e.g. 4.9.0). Empty leaves the output empty; the consumer wires its own default into the test step.' required: false default: '' platform-rc-version: description: 'Platform RC version for upgrade (e.g. 4.10.0-alpha.6). Empty resolves to the latest pre-release of loft-sh/loft-enterprise.' required: false default: '' - vci-k8s-version: - description: 'Kubernetes version for the private-nodes VCI (v-prefixed, e.g. v1.34.5). Pass-through — the consumer workflow is expected to pin a default and forward it here so it appears as an env var to subsequent steps.' - required: false - default: '' - vci-k8s-upgrade-version: - description: 'Kubernetes version to upgrade the private-nodes VCI to (v-prefixed, e.g. v1.35.0). Pass-through — see vci-k8s-version.' - required: false - default: '' outputs: standalone-vcluster-version: @@ -83,8 +75,6 @@ runs: STANDALONE_VCLUSTER_UPGRADE_VERSION_INPUT: ${{ inputs.standalone-vcluster-upgrade-version }} PLATFORM_BASE_VERSION_INPUT: ${{ inputs.platform-base-version }} PLATFORM_RC_VERSION_INPUT: ${{ inputs.platform-rc-version }} - VCI_K8S_VERSION_INPUT: ${{ inputs.vci-k8s-version }} - VCI_K8S_UPGRADE_VERSION_INPUT: ${{ inputs.vci-k8s-upgrade-version }} run: | set -euo pipefail # Accept inputs with or without a leading `v`; normalize to no-v before exporting. @@ -127,30 +117,6 @@ runs: echo "platform-base-version=$BASE" } >> "$GITHUB_OUTPUT" - # Also mirror to $GITHUB_ENV so the next composite step (Setup vCluster - # CLI) and the consumer's downstream test step read the same env vars - # the inlined version of this setup exported. This preserves behavioural - # parity with the pre-extraction workflow. - { - echo "STANDALONE_VCLUSTER_VERSION=$VERSION" - echo "DEFAULT_VCLUSTER_CHART_VERSION=$VERSION" - echo "STANDALONE_VCLUSTER_UPGRADE_VERSION=$UP" - echo "PLATFORM_RC_VERSION=$RC" - } >> "$GITHUB_ENV" - if [ -n "$BASE" ]; then - echo "PLATFORM_BASE_VERSION=$BASE" >> "$GITHUB_ENV" - fi - - # VCI K8s versions are pass-through: the consumer pins a default and - # forwards it here; we re-export to env so the test step sees it - # without the consumer wiring per-step env: blocks. - if [ -n "$VCI_K8S_VERSION_INPUT" ]; then - echo "VCI_K8S_VERSION=$VCI_K8S_VERSION_INPUT" >> "$GITHUB_ENV" - fi - if [ -n "$VCI_K8S_UPGRADE_VERSION_INPUT" ]; then - echo "VCI_K8S_UPGRADE_VERSION=$VCI_K8S_UPGRADE_VERSION_INPUT" >> "$GITHUB_ENV" - fi - - name: Setup vCluster CLI (matches base standalone version) shell: bash env: From 550fae155c13618aed7abe476049045dd2fd27e1 Mon Sep 17 00:00:00 2001 From: Sowmya Viswanathan Date: Sun, 24 May 2026 15:17:45 -0700 Subject: [PATCH 3/5] fix(prerelease-setup): pin third-party action refs to commit SHAs (zizmor unpinned-uses) --- .github/actions/prerelease-setup/action.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/prerelease-setup/action.yml b/.github/actions/prerelease-setup/action.yml index 3e4d818..32a15b4 100644 --- a/.github/actions/prerelease-setup/action.yml +++ b/.github/actions/prerelease-setup/action.yml @@ -39,27 +39,27 @@ runs: using: 'composite' steps: - name: Free disk space - uses: jlumbroso/free-disk-space@v1.3.1 + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 with: tool-cache: false - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 with: go-version-file: go.mod cache: true - name: Setup kubectl - uses: azure/setup-kubectl@v4 + uses: azure/setup-kubectl@776406bce94f63e41d621b960d78ee25c8b76ede # v4 - name: Setup helm - uses: azure/setup-helm@v4 + uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4 - name: AWS Login - uses: aws-actions/configure-aws-credentials@v5.1.1 + uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 with: role-to-assume: arn:aws:iam::084374023943:role/e2e-test-executor role-session-name: ${{ inputs.role-session-name }} From a2f9c1953c643f54abc9adbc7f4db7fcecf27db3 Mon Sep 17 00:00:00 2001 From: Sowmya Viswanathan Date: Sun, 24 May 2026 15:19:22 -0700 Subject: [PATCH 4/5] fix(prerelease-setup): set persist-credentials: false on checkout (zizmor artipacked) --- .github/actions/prerelease-setup/action.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/actions/prerelease-setup/action.yml b/.github/actions/prerelease-setup/action.yml index 32a15b4..34143fe 100644 --- a/.github/actions/prerelease-setup/action.yml +++ b/.github/actions/prerelease-setup/action.yml @@ -45,6 +45,11 @@ runs: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # The test code only reads from the working tree (go build, ginkgo + # discovery). No subsequent push/fetch needs the embedded token, so + # we drop it from the .git/config to satisfy zizmor[artipacked]. + persist-credentials: false - name: Setup Go uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 From 280bcb0af9bc9496d96cd205746295017cb2a81d Mon Sep 17 00:00:00 2001 From: Sowmya Viswanathan Date: Mon, 1 Jun 2026 13:23:31 -0700 Subject: [PATCH 5/5] =?UTF-8?q?fix(prerelease-setup):=20address=20PR=20rev?= =?UTF-8?q?iew=20=E2=80=94=20auth=20+=20version=20bumps=20+=20cleanup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add required github-token input. The platform release resolvers call the GitHub API for loft-sh/loft-enterprise, which is private — unauthenticated calls return 404. Pass ${{ github.token }} from a caller whose job permissions include contents:read. - Authenticate the vcluster (public) /releases/latest call too. The GH API rate-limits unauthenticated calls to 60/hr per runner IP; auth bumps it to 1000/hr per token and removes the intermittent 403 mode on shared runners. - Drop output-credentials: true. configure-aws-credentials exports AWS_* env vars by default; the flag only adds step outputs, which no downstream step reads. - Bump major versions on third-party actions: actions/setup-go v5 -> v6.4.0 azure/setup-kubectl v4 -> v5.1.0 azure/setup-helm v4 -> v5.0.0 aws-actions/configure-aws-credentials v5.1.1 -> v6.1.3 All four bumps share the same breaking change (node24 runtime, requires runner v2.327.1+) which GitHub-hosted ubuntu-latest already satisfies. - Regenerate README via make generate-docs. --- .github/actions/prerelease-setup/README.md | 34 ++++++++++----------- .github/actions/prerelease-setup/action.yml | 20 +++++++----- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/.github/actions/prerelease-setup/README.md b/.github/actions/prerelease-setup/README.md index 903a0ce..881a80a 100644 --- a/.github/actions/prerelease-setup/README.md +++ b/.github/actions/prerelease-setup/README.md @@ -9,13 +9,12 @@ The action performs, in order: 1. Free disk space (`jlumbroso/free-disk-space@v1.3.1`). 2. Checkout the calling repo (`actions/checkout@v6`). -3. Install Go (`actions/setup-go@v5`, `go-version-file: go.mod`, cache on). -4. Install `kubectl` (`azure/setup-kubectl@v4`). -5. Install `helm` (`azure/setup-helm@v4`). -6. AWS Login via OIDC (`aws-actions/configure-aws-credentials@v5.1.1`, +3. Install Go (`actions/setup-go@v6`, `go-version-file: go.mod`, cache on). +4. Install `kubectl` (`azure/setup-kubectl@v5`). +5. Install `helm` (`azure/setup-helm@v5`). +6. AWS Login via OIDC (`aws-actions/configure-aws-credentials@v6`, `role-to-assume: arn:aws:iam::084374023943:role/e2e-test-executor`, - `aws-region: us-west-2`, `role-duration-seconds: 6300`, - `output-credentials: true`). + `aws-region: us-west-2`, `role-duration-seconds: 6300`). 7. Resolve and validate the four version inputs (see below). 8. Download the `vcluster` CLI binary that matches the resolved base standalone vCluster version. @@ -29,13 +28,14 @@ provisioning (`aws-test-infra`) and the Ginkgo test execution -| INPUT | TYPE | REQUIRED | DEFAULT | DESCRIPTION | -|-------------------------------------|--------|----------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| -| platform-base-version | string | false | | Platform version for the initial install
(e.g. 4.9.0). Empty leaves the output empty;
the consumer wires its own default
into the test step. | -| platform-rc-version | string | false | | Platform RC version for upgrade (e.g. 4.10.0-alpha.6).
Empty resolves to the latest pre-release
of loft-sh/loft-enterprise. | -| role-session-name | string | true | | AWS STS role-session-name. Each consumer job
passes a distinct value (e.g. prerelease-vcluster-, prerelease-aicloud-). | -| standalone-vcluster-upgrade-version | string | true | | vCluster version to upgrade standalone to
(e.g. 0.35.0-alpha.7). Must differ from the resolved
base version. | -| standalone-vcluster-version | string | false | | vCluster version to install for standalone
(e.g. 0.34.0). Empty resolves to the latest
GitHub release of loft-sh/vcluster. | +| INPUT | TYPE | REQUIRED | DEFAULT | DESCRIPTION | +|-------------------------------------|--------|----------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| github-token | string | true | | GitHub token with contents:read on loft-sh/loft-enterprise.
Required because the platform release resolvers
call the GitHub API for a
private repo; unauthenticated calls return 404.
Pass ${{ github.token }} from a
job whose permissions grant contents:read. | +| platform-base-version | string | false | | Platform version for the initial install
(e.g. 4.9.0). Empty leaves the output empty;
the consumer wires its own default
into the test step. | +| platform-rc-version | string | false | | Platform RC version for upgrade (e.g. 4.10.0-alpha.6).
Empty resolves to the latest pre-release
of loft-sh/loft-enterprise. | +| role-session-name | string | true | | AWS STS role-session-name. Each consumer job
passes a distinct value (e.g. prerelease-vcluster-, prerelease-aicloud-). | +| standalone-vcluster-upgrade-version | string | true | | vCluster version to upgrade standalone to
(e.g. 0.35.0-alpha.7). Must differ from the resolved
base version. | +| standalone-vcluster-version | string | false | | vCluster version to install for standalone
(e.g. 0.34.0). Empty resolves to the latest
GitHub release of loft-sh/vcluster. | @@ -64,10 +64,9 @@ matches the convention of `aws-test-infra` and avoids the The OIDC step exports `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN` to the environment via -`aws-actions/configure-aws-credentials` with `output-credentials: true`. -These propagate to subsequent steps in the calling job through the -standard action mechanism, so the consumer does not need to wire them -explicitly. +`aws-actions/configure-aws-credentials`. These propagate to subsequent +steps in the calling job through the standard action mechanism, so the +consumer does not need to wire them explicitly. ## Permissions @@ -101,6 +100,7 @@ jobs: uses: loft-sh/github-actions/.github/actions/prerelease-setup@prerelease-setup/v1 with: role-session-name: prerelease-vcluster-${{ github.run_id }} + github-token: ${{ github.token }} standalone-vcluster-version: ${{ inputs.standalone_vcluster_version || github.event.client_payload.standalone_vcluster_version }} standalone-vcluster-upgrade-version: ${{ env.STANDALONE_VCLUSTER_UPGRADE_VERSION }} platform-base-version: ${{ env.PLATFORM_BASE_VERSION }} diff --git a/.github/actions/prerelease-setup/action.yml b/.github/actions/prerelease-setup/action.yml index 34143fe..e5a5fcc 100644 --- a/.github/actions/prerelease-setup/action.yml +++ b/.github/actions/prerelease-setup/action.yml @@ -5,6 +5,9 @@ inputs: role-session-name: description: 'AWS STS role-session-name. Each consumer job passes a distinct value (e.g. prerelease-vcluster-, prerelease-aicloud-).' required: true + github-token: + description: 'GitHub token with contents:read on loft-sh/loft-enterprise. Required because the platform release resolvers call the GitHub API for a private repo; unauthenticated calls return 404. Pass ${{ github.token }} from a job whose permissions grant contents:read.' + required: true standalone-vcluster-version: description: 'vCluster version to install for standalone (e.g. 0.34.0). Empty resolves to the latest GitHub release of loft-sh/vcluster.' required: false @@ -52,24 +55,23 @@ runs: persist-credentials: false - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod cache: true - name: Setup kubectl - uses: azure/setup-kubectl@776406bce94f63e41d621b960d78ee25c8b76ede # v4 + uses: azure/setup-kubectl@829323503d1be3d00ca8346e5391ca0b07a9ab0d # v5.1.0 - name: Setup helm - uses: azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4 + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 - name: AWS Login - uses: aws-actions/configure-aws-credentials@61815dcd50bd041e203e49132bacad1fd04d2708 # v5.1.1 + uses: aws-actions/configure-aws-credentials@99214aa6889fcddfa57764031d71add364327e59 # v6.1.3 with: role-to-assume: arn:aws:iam::084374023943:role/e2e-test-executor role-session-name: ${{ inputs.role-session-name }} aws-region: us-west-2 - output-credentials: true role-duration-seconds: 6300 - name: Resolve and validate versions @@ -80,6 +82,7 @@ runs: STANDALONE_VCLUSTER_UPGRADE_VERSION_INPUT: ${{ inputs.standalone-vcluster-upgrade-version }} PLATFORM_BASE_VERSION_INPUT: ${{ inputs.platform-base-version }} PLATFORM_RC_VERSION_INPUT: ${{ inputs.platform-rc-version }} + GH_TOKEN: ${{ inputs.github-token }} run: | set -euo pipefail # Accept inputs with or without a leading `v`; normalize to no-v before exporting. @@ -87,7 +90,9 @@ runs: VERSION="$STANDALONE_VCLUSTER_VERSION_INPUT" if [ -z "$VERSION" ]; then - VERSION=$(curl -fsSL https://api.github.com/repos/loft-sh/vcluster/releases/latest | jq -r .tag_name) + # vcluster is public but unauthenticated calls share the runner IP's 60/hr rate limit. + # Pass the token to bump the budget to 1000/hr per token and avoid intermittent 403s. + VERSION=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/repos/loft-sh/vcluster/releases/latest | jq -r .tag_name) fi VERSION="${VERSION#v}" [[ "$VERSION" =~ $VERSION_RE ]] || { echo "invalid standalone-vcluster-version: $VERSION"; exit 1; } @@ -101,7 +106,8 @@ runs: if [ -z "$RC" ]; then # GitHub's /releases/latest excludes pre-releases, so list /releases and filter on `prerelease == true`. # The list is newest-first by created_at, so `[0]` after filter = latest pre-release. - RC=$(curl -fsSL https://api.github.com/repos/loft-sh/loft-enterprise/releases \ + # loft-enterprise is private — unauthenticated requests return 404, so the token is required. + RC=$(curl -fsSL -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/repos/loft-sh/loft-enterprise/releases \ | jq -r '[.[] | select(.prerelease == true and .draft == false)][0].tag_name') [ -n "$RC" ] && [ "$RC" != "null" ] || { echo "no platform pre-release found in loft-sh/loft-enterprise"; exit 1; } RC="${RC#v}"