Skip to content

GitLab E2E

GitLab E2E #119

Workflow file for this run

name: GitLab E2E
on:
workflow_run:
workflows: [GitLab E2E request]
types: [completed]
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
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_run' &&
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.path == '.github/workflows/e2e-request.yml')
runs-on: ubuntu-latest
timeout-minutes: 35
outputs:
pr_number: ${{ steps.parameters.outputs.pr_number }}
setup_vp_ref: ${{ steps.parameters.outputs.setup_vp_ref }}
suite: ${{ steps.parameters.outputs.suite }}
vite_plus_version: ${{ steps.parameters.outputs.vite_plus_version }}
pipeline_url: ${{ steps.trigger.outputs.pipeline_url }}
# Keep this privileged job API-only: no PR checkout, artifacts, or caches.
steps:
- name: Resolve test parameters
id: parameters
env:
EVENT_NAME: ${{ github.event_name }}
REQUEST_EVENT: ${{ github.event.workflow_run.event }}
REQUEST_CONCLUSION: ${{ github.event.workflow_run.conclusion }}
REQUEST_PATH: ${{ github.event.workflow_run.path }}
REQUEST_TITLE: ${{ github.event.workflow_run.display_title }}
REQUEST_ACTOR: ${{ github.event.workflow_run.actor.login }}
REQUEST_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
REQUEST_HEAD_REPOSITORY: ${{ github.event.workflow_run.head_repository.full_name }}
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=false
setup_vp_ref=""
suite=full
vite_plus_version=latest
report_pr_number=""
skip_reason='GitLab E2E requires a successful run-e2e label request for a PR or a manual workflow_dispatch run.'
resolve_pr_request() {
local request_pattern='^PR #([1-9][0-9]*): labeled run-e2e at ([0-9a-f]{40})$'
if [ "$EVENT_NAME" != "workflow_run" ] || [ "$REQUEST_EVENT" != "pull_request" ] ||
[ "$REQUEST_CONCLUSION" != "success" ] ||
[ "$REQUEST_PATH" != ".github/workflows/e2e-request.yml" ] ||
! [[ "$REQUEST_TITLE" =~ $request_pattern ]]; then
return
fi
local pr_number="${BASH_REMATCH[1]}"
if [ "${BASH_REMATCH[2]}" != "$REQUEST_HEAD_SHA" ]; then
echo "::error::The request title does not match the workflow run's head SHA."
exit 1
fi
# Check the original labeler's access, even on reruns.
# GitHub maps the maintain role to write.
local permission
permission="$(gh api "repos/${GITHUB_REPOSITORY}/collaborators/${REQUEST_ACTOR}/permission" --jq '.permission')"
case "$permission" in
admin | write) ;;
*)
echo "::error::Adding run-e2e requires repository write access to approve a test run."
exit 1
;;
esac
# Compare Git blobs to reject altered request triggers or run names.
local workflow_url="repos/${GITHUB_REPOSITORY}/contents/${REQUEST_PATH}"
local trusted_blob request_blob
trusted_blob="$(gh api "${workflow_url}?ref=${GITHUB_SHA}" --jq '.sha')"
if ! request_blob="$(gh api "${workflow_url}?ref=${REQUEST_HEAD_SHA}" --jq '.sha')" ||
[ "$request_blob" != "$trusted_blob" ]; then
echo "::error::The PR must include e2e-request.yml unchanged from main. Update the branch, then remove and re-add run-e2e."
exit 1
fi
# Fork runs can have an empty workflow_run.pull_requests array.
# Resolve the PR from the verified title and cross-check its head.
local pull_request
pull_request="$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${pr_number}")"
if ! jq -e --arg sha "$REQUEST_HEAD_SHA" --arg head_repo "$REQUEST_HEAD_REPOSITORY" --arg repo "$GITHUB_REPOSITORY" \
'.state == "open" and .head.sha == $sha and .head.repo.full_name == $head_repo and
.base.repo.full_name == $repo and .base.ref == "main" and any(.labels[]; .name == "run-e2e")' \
<<< "$pull_request" > /dev/null; then
skip_reason='This approval is stale or does not match the PR: check its head, base, and run-e2e label. Review the current commit, then remove and re-add run-e2e to test it.'
return
fi
should_run=true
setup_vp_ref="$REQUEST_HEAD_SHA"
report_pr_number="$pr_number"
}
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
should_run=true
setup_vp_ref="${MANUAL_SETUP_REF:-$GITHUB_SHA}"
suite="${MANUAL_SUITE:-full}"
vite_plus_version="${MANUAL_VITE_PLUS_VERSION:-latest}"
else
resolve_pr_request
fi
{
echo "should_run=$should_run"
echo "setup_vp_ref=$setup_vp_ref"
echo "suite=$suite"
echo "vite_plus_version=$vite_plus_version"
echo "pr_number=$report_pr_number"
} >> "$GITHUB_OUTPUT"
if [ "$should_run" = "false" ]; then
echo "$skip_reason"
{
echo "### GitLab E2E"
echo
echo "$skip_reason"
} >> "$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
report-result:
name: Report GitLab E2E result
needs: gitlab-e2e
if: always() && needs.gitlab-e2e.outputs.pr_number != ''
runs-on: ubuntu-latest
permissions:
pull-requests: write
concurrency:
group: gitlab-e2e-comment-${{ needs.gitlab-e2e.outputs.pr_number }}
cancel-in-progress: false
steps:
- name: Update PR comment
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
PR_NUMBER: ${{ needs.gitlab-e2e.outputs.pr_number }}
SETUP_VP_REF: ${{ needs.gitlab-e2e.outputs.setup_vp_ref }}
TEST_SUITE: ${{ needs.gitlab-e2e.outputs.suite }}
VITE_PLUS_VERSION: ${{ needs.gitlab-e2e.outputs.vite_plus_version }}
PIPELINE_URL: ${{ needs.gitlab-e2e.outputs.pipeline_url }}
RESULT: ${{ needs.gitlab-e2e.result }}
with:
script: |
const { PR_NUMBER, SETUP_VP_REF, TEST_SUITE, VITE_PLUS_VERSION, PIPELINE_URL, RESULT } = process.env;
const issue = { ...context.repo, issue_number: Number(PR_NUMBER) };
const { data: pr } = await github.rest.pulls.get({ ...context.repo, pull_number: issue.issue_number });
if (pr.head.sha !== SETUP_VP_REF) {
core.info('The PR head changed; skip this result.');
return;
}
const marker = '<!-- setup-vp-gitlab-e2e -->';
const comments = await github.paginate(github.rest.issues.listComments, { ...issue, per_page: 100 });
let previous;
for (const comment of comments) {
if (!comment.body?.startsWith(marker)) continue;
if (comment.user?.login === 'github-actions[bot]') {
previous = comment;
break;
}
// Reuse a result posted by a maintainer, including before automation was added.
if (!comment.user) continue;
let permission;
try {
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
...context.repo, username: comment.user.login,
});
permission = data.permission;
} catch (error) {
if (error.status === 404) continue;
throw error;
}
if (permission === 'admin' || permission === 'write') {
previous = comment;
break;
}
}
const attempt = Number(process.env.GITHUB_RUN_ATTEMPT);
const previousRun = previous?.body.match(/<!-- setup-vp-gitlab-e2e-run:(\d+):(\d+) -->/);
if (previousRun && (Number(previousRun[1]) > context.runId ||
(Number(previousRun[1]) === context.runId && Number(previousRun[2]) > attempt))) {
core.info('A newer run already reported its result.');
return;
}
let title;
if (RESULT === 'success') title = '✅ GitLab E2E passed';
else if (RESULT === 'cancelled') title = '⚠️ GitLab E2E run cancelled';
else if (!PIPELINE_URL) title = '❌ GitLab E2E could not start';
else title = '❌ GitLab E2E did not complete successfully';
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}/attempts/${attempt}`;
const links = [PIPELINE_URL && `[GitLab pipeline](${PIPELINE_URL})`, `[GitHub Actions run](${runUrl})`].filter(Boolean);
const body = [
marker,
`<!-- setup-vp-gitlab-e2e-run:${context.runId}:${attempt} -->`,
'', `### ${title}`, '',
`Commit: \`${SETUP_VP_REF}\` `,
`Suite: \`${TEST_SUITE}\` · Vite+ version: \`${VITE_PLUS_VERSION}\``,
'', links.join(' · '),
].join('\n');
if (previous) {
await github.rest.issues.updateComment({ ...context.repo, comment_id: previous.id, body });
} else {
await github.rest.issues.createComment({ ...issue, body });
}