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[@]}"