d5c7b735d2
# Introduction Getting rid of the fine grained PAT used to dispatch to internal repositories. Repo dispatch requires the contents write permissions which is too wide for such use Refactored all senders and target to pass through a workflow dispatch instead Creating a centralize app that forges a token with actions: write only provided permissions to mitigate any token exfiltrations
276 lines
11 KiB
YAML
276 lines
11 KiB
YAML
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 New 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 }}
|
|
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 New UI': 'twenty-new-ui',
|
|
'CI Front': 'twenty-front',
|
|
};
|
|
const project = projectByWorkflow[name] ?? 'twenty-ui';
|
|
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}`,
|
|
});
|
|
|
|
if (comparison.merge_base_commit?.sha) {
|
|
core.setOutput('sha', comparison.merge_base_commit.sha);
|
|
core.info(`Merge base: ${comparison.merge_base_commit.sha}`);
|
|
} else {
|
|
core.info('Could not determine merge base — will skip reference_commit');
|
|
core.setOutput('sha', '');
|
|
}
|
|
} catch (error) {
|
|
core.warning(`Failed to compute merge base: ${error instanceof Error ? error.message : String(error)}`);
|
|
core.setOutput('sha', '');
|
|
}
|
|
|
|
# ── 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 }}
|
|
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
|
|
|
|
gh workflow run post-visual-regression-comment.yaml --repo twentyhq/ci-privileged --ref main "${ARGS[@]}"
|
|
|
|
# ── Dispatch: cross-comparison baseline (CI UI on main → twenty-ui-vs-new-ui) ──
|
|
dispatch-comparison-baseline:
|
|
needs: resolve-context
|
|
if: >-
|
|
needs.resolve-context.outputs.workflow_name == 'CI UI' &&
|
|
needs.resolve-context.outputs.has_artifact == 'true' &&
|
|
github.event.workflow_run.event == 'push' &&
|
|
github.event.workflow_run.head_branch == 'main'
|
|
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 (comparison baseline)
|
|
env:
|
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
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 }}
|
|
ARTIFACT_NAME: ${{ needs.resolve-context.outputs.artifact_name }}
|
|
run: |
|
|
gh workflow run post-visual-regression-comment.yaml --repo twentyhq/ci-privileged --ref main \
|
|
-f project="twenty-ui-vs-new-ui" \
|
|
-f artifact_name="$ARTIFACT_NAME" \
|
|
-f run_id="$WORKFLOW_RUN_ID" \
|
|
-f repo="$REPOSITORY" \
|
|
-f branch="$BRANCH" \
|
|
-f commit="$COMMIT"
|
|
|
|
# ── Dispatch: cross-comparison PR (CI New UI on PRs → twenty-ui-vs-new-ui) ──
|
|
dispatch-comparison-pr:
|
|
needs: resolve-context
|
|
if: >-
|
|
needs.resolve-context.outputs.workflow_name == 'CI New UI' &&
|
|
needs.resolve-context.outputs.has_artifact == 'true' &&
|
|
needs.resolve-context.outputs.is_pr == 'true' &&
|
|
github.event.workflow_run.event == 'pull_request'
|
|
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 (comparison PR)
|
|
env:
|
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
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 }}
|
|
PR_NUMBER: ${{ needs.resolve-context.outputs.pr_number }}
|
|
REFERENCE_COMMIT: ${{ needs.resolve-context.outputs.merge_base_sha }}
|
|
ARTIFACT_NAME: ${{ needs.resolve-context.outputs.artifact_name }}
|
|
run: |
|
|
ARGS=(
|
|
-f project="twenty-ui-vs-new-ui"
|
|
-f artifact_name="$ARTIFACT_NAME"
|
|
-f run_id="$WORKFLOW_RUN_ID"
|
|
-f repo="$REPOSITORY"
|
|
-f branch="$BRANCH"
|
|
-f commit="$COMMIT"
|
|
-f pr_number="$PR_NUMBER"
|
|
)
|
|
|
|
if [ -n "$REFERENCE_COMMIT" ]; then
|
|
ARGS+=(-f reference_commit="$REFERENCE_COMMIT")
|
|
fi
|
|
|
|
gh workflow run post-visual-regression-comment.yaml --repo twentyhq/ci-privileged --ref main "${ARGS[@]}"
|