diff --git a/.github/workflows/gitlab-e2e.yml b/.github/workflows/gitlab-e2e.yml new file mode 100644 index 0000000..57b4ad7 --- /dev/null +++ b/.github/workflows/gitlab-e2e.yml @@ -0,0 +1,197 @@ +name: GitLab E2E + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + branches: [main] + merge_group: + workflow_dispatch: + inputs: + setup_ref: + description: "Enter an exact setup-vp commit SHA or release tag. If empty, the workflow uses this commit." + required: false + default: "" + suite: + description: "Select the GitLab test suite." + type: choice + options: [full, required] + default: full + vite_plus_version: + description: "Vite+ version or dist-tag to install." + required: false + default: latest + +permissions: + contents: read + pull-requests: read + +jobs: + gitlab-e2e: + name: GitLab E2E + runs-on: ubuntu-latest + timeout-minutes: 35 + steps: + - name: Resolve test parameters + id: parameters + env: + EVENT_NAME: ${{ github.event_name }} + EVENT_REF: ${{ github.ref }} + EVENT_REF_NAME: ${{ github.ref_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + MANUAL_SETUP_REF: ${{ inputs.setup_ref }} + MANUAL_SUITE: ${{ inputs.suite }} + MANUAL_VITE_PLUS_VERSION: ${{ inputs.vite_plus_version }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + should_run=true + setup_vp_ref="$GITHUB_SHA" + suite=required + vite_plus_version=latest + + if [ "$EVENT_NAME" = "pull_request" ]; then + if [ "$PR_HEAD_REPOSITORY" != "$GITHUB_REPOSITORY" ]; then + should_run=false + echo "The workflow skips fork pull requests. The merge queue or a maintainer can test these changes." + else + setup_vp_ref="$PR_HEAD_SHA" + while IFS= read -r changed_path; do + case "$changed_path" in + gitlab/* | src/gitlab/* | src/ci/* | dist/gitlab/* | .github/workflows/gitlab-e2e.yml | vite.config.ts | package.json | pnpm-lock.yaml | pnpm-workspace.yaml) + suite=full + break + ;; + esac + done < <(gh api --paginate "repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}/files" --jq '.[].filename') + fi + elif [ "$EVENT_NAME" = "merge_group" ]; then + suite=full + elif [ "$EVENT_NAME" = "workflow_dispatch" ]; then + setup_vp_ref="${MANUAL_SETUP_REF:-$GITHUB_SHA}" + suite="${MANUAL_SUITE:-full}" + vite_plus_version="${MANUAL_VITE_PLUS_VERSION:-latest}" + elif [ "$EVENT_NAME" = "push" ] && [[ "$EVENT_REF" == refs/tags/* ]]; then + setup_vp_ref="$EVENT_REF_NAME" + suite=full + elif [ "$EVENT_NAME" = "push" ]; then + suite=full + fi + + { + echo "should_run=$should_run" + echo "setup_vp_ref=$setup_vp_ref" + echo "suite=$suite" + echo "vite_plus_version=$vite_plus_version" + } >> "$GITHUB_OUTPUT" + + if [ "$should_run" = "false" ]; then + { + echo "### GitLab E2E" + echo + echo "The workflow skipped this fork pull request. The merge queue tests the reviewed merge commit." + } >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Trigger GitLab pipeline + if: steps.parameters.outputs.should_run == 'true' + id: trigger + env: + GITLAB_PROJECT_ID: "85578524" + GITLAB_TRIGGER_TOKEN: ${{ secrets.GITLAB_TRIGGER_TOKEN }} + SETUP_VP_REF: ${{ steps.parameters.outputs.setup_vp_ref }} + TEST_SUITE: ${{ steps.parameters.outputs.suite }} + VITE_PLUS_VERSION: ${{ steps.parameters.outputs.vite_plus_version }} + run: | + set -euo pipefail + + if [ -z "$GITLAB_TRIGGER_TOKEN" ]; then + echo "::error::This repository does not have the GITLAB_TRIGGER_TOKEN secret." + exit 1 + fi + + response="$(curl --silent --show-error --fail-with-body \ + --request POST \ + --form-string "token=$GITLAB_TRIGGER_TOKEN" \ + --form-string "ref=main" \ + --form-string "inputs[setup_vp_ref]=$SETUP_VP_REF" \ + --form-string "inputs[suite]=$TEST_SUITE" \ + --form-string "inputs[vite_plus_version]=$VITE_PLUS_VERSION" \ + "https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/trigger/pipeline")" + + pipeline_id="$(jq -er '.id' <<< "$response")" + pipeline_url="$(jq -er '.web_url' <<< "$response")" + echo "Triggered GitLab pipeline: $pipeline_url" + + { + echo "pipeline_id=$pipeline_id" + echo "pipeline_url=$pipeline_url" + } >> "$GITHUB_OUTPUT" + + { + echo "### GitLab E2E" + echo + echo "- Pipeline: [$pipeline_id]($pipeline_url)" + echo "- setup-vp ref: \`$SETUP_VP_REF\`" + echo "- Suite: \`$TEST_SUITE\`" + echo "- Vite+ version: \`$VITE_PLUS_VERSION\`" + } >> "$GITHUB_STEP_SUMMARY" + + - name: Wait for GitLab pipeline + if: steps.parameters.outputs.should_run == 'true' + env: + GITLAB_PROJECT_ID: "85578524" + GITLAB_PIPELINE_ID: ${{ steps.trigger.outputs.pipeline_id }} + GITLAB_PIPELINE_URL: ${{ steps.trigger.outputs.pipeline_url }} + run: | + set -euo pipefail + + api_url="https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/pipelines/${GITLAB_PIPELINE_ID}" + deadline=$((SECONDS + 1800)) + previous_status="" + final_status="timed_out" + + while [ "$SECONDS" -lt "$deadline" ]; do + if payload="$(curl --silent --show-error --fail --retry 3 --retry-delay 2 "$api_url")"; then + status="$(jq -er '.status' <<< "$payload")" + if [ "$status" != "$previous_status" ]; then + echo "GitLab pipeline status: $status" + previous_status="$status" + fi + + case "$status" in + success) + final_status=success + break + ;; + failed | canceled | skipped | manual) + final_status="$status" + break + ;; + esac + else + echo "The workflow could not read the GitLab pipeline status. It will try again." + fi + sleep 10 + done + + { + echo "- Result: \`$final_status\`" + } >> "$GITHUB_STEP_SUMMARY" + + if [ "$final_status" = "success" ]; then + echo "GitLab pipeline passed: $GITLAB_PIPELINE_URL" + exit 0 + fi + + echo "GitLab pipeline did not pass: $GITLAB_PIPELINE_URL" + jobs_url="https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/pipelines/${GITLAB_PIPELINE_ID}/jobs?per_page=100" + if jobs="$(curl --silent --show-error --fail --retry 3 --retry-delay 2 "$jobs_url")"; then + jq -r 'sort_by(.name)[] | "\(.status)\t\(.name)\t\(.web_url)"' <<< "$jobs" + fi + echo "::error::GitLab pipeline finished with status $final_status." + exit 1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39d30bc..aa76e48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -49,7 +49,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} # The version assertion below must see the bridge-resolved build. Pin # VP_PR_VERSION to "" so a manual pr_version dispatch can't override it. @@ -71,8 +71,10 @@ jobs: echo "Resolved preview build: ${version}" echo "version=${version}" >> "$GITHUB_OUTPUT" + # The self-repository reference gives the action its repository identity. + # The preview installer uses that identity for legacy path compatibility. - name: Setup Vite+ - uses: ./ + uses: $/. with: version: ${{ steps.preview.outputs.version }} run-install: false diff --git a/README.md b/README.md index c2e1c21..7f5ecc7 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,8 @@ setup-vp also provides a GitLab CI/CD remote template hosted from this GitHub re See [GitLab integration notes](rfcs/gitlab-integration.md) for the design background, constraints, and follow-up work. +The dedicated [GitLab end-to-end test project](https://gitlab.com/fengmk2/setup-vp-gitlab-test) tests each setup-vp pull request, merge, and release. The pipeline loads the template, bootstrap script, and compiled runtime from the exact setup-vp commit or release tag that it tests. + ### Basic GitLab Usage Use an exact release tag in the `include:remote` URL, and pin `setup-ref` to the same tag so the bootstrap and compiled runtime are downloaded from the same version as the included template: @@ -668,7 +670,9 @@ To cut a release: git status --short # must be empty ``` -3. Create the new annotated version tag and push it. For example: +3. Confirm that the release commit on `main` passes the full GitLab E2E workflow. + +4. Create the new annotated version tag and push it. For example: ```bash git tag -a v1.17.0 -m "v1.17.0" diff --git a/gitlab/bootstrap.sh b/gitlab/bootstrap.sh index 82fd2c5..f574d4f 100644 --- a/gitlab/bootstrap.sh +++ b/gitlab/bootstrap.sh @@ -133,8 +133,9 @@ elif [[ "$SETUP_VP_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; th setup_vp_pinned_ref="v${SETUP_VP_VERSION}" fi setup_vp_install_tmp="$(mktemp "${TMPDIR:-/tmp}/setup-vp-install.XXXXXX")" -setup_vp_runtime_tmp="$(mktemp "${TMPDIR:-/tmp}/setup-vp-gitlab-runtime.XXXXXX.mjs")" -trap 'rm -f "$setup_vp_install_tmp" "$setup_vp_runtime_tmp"' EXIT +setup_vp_runtime_dir="$(mktemp -d "${TMPDIR:-/tmp}/setup-vp-gitlab-runtime.XXXXXX")" +setup_vp_runtime_tmp="${setup_vp_runtime_dir}/index.mjs" +trap 'rm -f "$setup_vp_install_tmp" "$setup_vp_runtime_tmp"; rmdir "$setup_vp_runtime_dir" 2>/dev/null || true' EXIT setup_vp_install_viteplus export PATH="$HOME/.vite-plus/bin:$PATH"