Visual Regression Dispatch #2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Visual Regression Dispatch | |
| # Dispatches visual regression processing to ci-privileged after CI completes. | |
| # Runs in the context of the base repo (not the fork) so it has access to secrets, | |
| # making it work for external contributor PRs. | |
| # | |
| # All paths trigger the same ci-privileged workflow (post-visual-regression-comment.yaml) | |
| # via workflow_dispatch, passing the project as an input. ci-privileged routes to the | |
| # correct Argos project based on it. | |
| on: | |
| workflow_run: | |
| workflows: ['CI UI', 'CI Front'] | |
| types: [completed] | |
| permissions: | |
| actions: read | |
| contents: read | |
| pull-requests: read | |
| jobs: | |
| resolve-context: | |
| if: github.event.workflow_run.conclusion == 'success' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| workflow_name: ${{ steps.context.outputs.workflow_name }} | |
| project: ${{ steps.context.outputs.project }} | |
| artifact_name: ${{ steps.context.outputs.artifact_name }} | |
| has_artifact: ${{ steps.check-artifact.outputs.exists }} | |
| is_pr: ${{ steps.pr-info.outputs.has_pr }} | |
| pr_number: ${{ steps.pr-info.outputs.pr_number }} | |
| merge_base_sha: ${{ steps.merge-base.outputs.sha }} | |
| parent_commits: ${{ steps.merge-base.outputs.parent_commits }} | |
| steps: | |
| - name: Resolve workflow context | |
| id: context | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 | |
| with: | |
| script: | | |
| const name = context.payload.workflow_run.name; | |
| const projectByWorkflow = { | |
| 'CI UI': 'twenty-ui', | |
| 'CI Front': 'twenty-front', | |
| }; | |
| const project = projectByWorkflow[name] ?? 'twenty-front'; | |
| const artifactName = `argos-screenshots-${project}`; | |
| core.setOutput('workflow_name', name); | |
| core.setOutput('project', project); | |
| core.setOutput('artifact_name', artifactName); | |
| core.info(`Workflow: ${name}, project: ${project}, artifact: ${artifactName}`); | |
| - name: Check if screenshots artifact exists | |
| id: check-artifact | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 | |
| with: | |
| script: | | |
| const artifactName = '${{ steps.context.outputs.artifact_name }}'; | |
| const runId = context.payload.workflow_run.id; | |
| const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: runId, | |
| }); | |
| const found = artifacts.artifacts.some(a => a.name === artifactName); | |
| core.setOutput('exists', found ? 'true' : 'false'); | |
| if (!found) { | |
| core.info(`Artifact "${artifactName}" not found in run ${runId} — skipping`); | |
| } | |
| - name: Get PR number | |
| if: >- | |
| steps.check-artifact.outputs.exists == 'true' && | |
| github.event.workflow_run.event == 'pull_request' | |
| id: pr-info | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 | |
| with: | |
| script: | | |
| const headBranch = context.payload.workflow_run.head_branch; | |
| const headRepo = context.payload.workflow_run.head_repository; | |
| let pullRequests = context.payload.workflow_run.pull_requests; | |
| let prNumber; | |
| if (pullRequests && pullRequests.length > 0) { | |
| prNumber = pullRequests[0].number; | |
| } else { | |
| const headLabel = `${headRepo.owner.login}:${headBranch}`; | |
| core.info(`Searching for PR by head label: ${headLabel}`); | |
| const { data: prs } = await github.rest.pulls.list({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| head: headLabel, | |
| per_page: 1, | |
| }); | |
| if (prs.length > 0) { | |
| prNumber = prs[0].number; | |
| } | |
| } | |
| if (!prNumber) { | |
| core.info('No pull request found — skipping'); | |
| core.setOutput('has_pr', 'false'); | |
| return; | |
| } | |
| core.setOutput('pr_number', prNumber); | |
| core.setOutput('has_pr', 'true'); | |
| core.info(`PR #${prNumber}`); | |
| - name: Compute merge-base for Argos reference | |
| if: steps.check-artifact.outputs.exists == 'true' && steps.pr-info.outputs.has_pr == 'true' | |
| id: merge-base | |
| uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 | |
| with: | |
| script: | | |
| const headSha = context.payload.workflow_run.head_sha; | |
| try { | |
| const { data: comparison } = await github.rest.repos.compareCommitsWithBasehead({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| basehead: `main...${headSha}`, | |
| }); | |
| const mergeBaseSha = comparison.merge_base_commit?.sha; | |
| if (mergeBaseSha) { | |
| core.setOutput('sha', mergeBaseSha); | |
| core.info(`Merge base: ${mergeBaseSha}`); | |
| const { data: commits } = await github.rest.repos.listCommits({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| sha: mergeBaseSha, | |
| per_page: 100, | |
| }); | |
| const parentCommits = commits.map((commit) => commit.sha); | |
| core.setOutput('parent_commits', parentCommits.join(' ')); | |
| core.info(`Parent commits: ${parentCommits.length}`); | |
| } else { | |
| core.info('Could not determine merge base — will skip reference_commit'); | |
| core.setOutput('sha', ''); | |
| core.setOutput('parent_commits', ''); | |
| } | |
| } catch (error) { | |
| core.warning(`Failed to compute merge base: ${error instanceof Error ? error.message : String(error)}`); | |
| core.setOutput('sha', ''); | |
| core.setOutput('parent_commits', ''); | |
| } | |
| # ── Dispatch: pixel diff for the triggering workflow's project (PRs + main) ── | |
| dispatch-pixel-diff: | |
| needs: resolve-context | |
| if: needs.resolve-context.outputs.has_artifact == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Mint ci-privileged dispatch token | |
| id: app-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 | |
| with: | |
| client-id: ${{ vars.TWENTY_WORKFLOW_DISPATCHER_CLIENT_ID }} | |
| private-key: ${{ secrets.TWENTY_WORKFLOW_DISPATCHER_PRIVATE_KEY }} | |
| owner: twentyhq | |
| repositories: ci-privileged | |
| permission-actions: write | |
| - name: Dispatch to ci-privileged | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| PROJECT: ${{ needs.resolve-context.outputs.project }} | |
| PR_NUMBER: ${{ needs.resolve-context.outputs.pr_number }} | |
| WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} | |
| REPOSITORY: ${{ github.repository }} | |
| BRANCH: ${{ github.event.workflow_run.head_branch }} | |
| COMMIT: ${{ github.event.workflow_run.head_sha }} | |
| REFERENCE_COMMIT: ${{ needs.resolve-context.outputs.merge_base_sha }} | |
| PARENT_COMMITS: ${{ needs.resolve-context.outputs.parent_commits }} | |
| ARTIFACT_NAME: ${{ needs.resolve-context.outputs.artifact_name }} | |
| run: | | |
| ARGS=( | |
| -f project="$PROJECT" | |
| -f artifact_name="$ARTIFACT_NAME" | |
| -f run_id="$WORKFLOW_RUN_ID" | |
| -f repo="$REPOSITORY" | |
| -f branch="$BRANCH" | |
| -f commit="$COMMIT" | |
| ) | |
| if [ -n "$PR_NUMBER" ]; then | |
| ARGS+=(-f pr_number="$PR_NUMBER") | |
| fi | |
| if [ -n "$REFERENCE_COMMIT" ]; then | |
| ARGS+=(-f reference_commit="$REFERENCE_COMMIT") | |
| fi | |
| if [ -n "$PARENT_COMMITS" ]; then | |
| ARGS+=(-f parent_commits="$PARENT_COMMITS") | |
| fi | |
| gh workflow run post-visual-regression-comment.yaml --repo twentyhq/ci-privileged --ref main "${ARGS[@]}" |