diff --git a/.github/actions/prerelease-setup/README.md b/.github/actions/prerelease-setup/README.md new file mode 100644 index 0000000..881a80a --- /dev/null +++ b/.github/actions/prerelease-setup/README.md @@ -0,0 +1,140 @@ +# 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 (`actions/checkout@v6`). +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`). +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 + + + +| 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. | + + + +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 + + + +| 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). | + + + +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`. 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 + +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 + id: setup + 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 }} + 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" + 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` (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 new file mode 100644 index 0000000..e5a5fcc --- /dev/null +++ b/.github/actions/prerelease-setup/action.yml @@ -0,0 +1,159 @@ +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 + 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 + 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 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: '' + +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@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 + with: + tool-cache: false + + - 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@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: go.mod + cache: true + + - name: Setup kubectl + uses: azure/setup-kubectl@829323503d1be3d00ca8346e5391ca0b07a9ab0d # v5.1.0 + + - name: Setup helm + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 + + - name: AWS Login + 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 + 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 }} + GH_TOKEN: ${{ inputs.github-token }} + 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 + # 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; } + 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. + # 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}" + 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" + + - 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'