Skip to content

ci(playwright): unify postgres/mysql e2e into a reusable workflow - #31628

Merged
chirag-madlani merged 4 commits into
mainfrom
unify-nightly-e2e-workflows
Aug 18, 2026
Merged

ci(playwright): unify postgres/mysql e2e into a reusable workflow#31628
chirag-madlani merged 4 commits into
mainfrom
unify-nightly-e2e-workflows

Conversation

@chirag-madlani

@chirag-madlani chirag-madlani commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Summary

The nightly postgres and mysql workflows had drifted from the maintained playwright-postgresql-e2e.yml pipeline — same intent, but hand-copied shard logic, no smart selection, no distribution/fixture cache, and their own env blocks. Changing the pipeline meant editing three files.

Extract every job into playwright-e2e-reusable.yml (on: workflow_call) parameterised by database, profile, workflow_filename, and ref. Three thin callers now share it:

File Triggers Purpose
playwright-postgresql-e2e.yml (94 lines) PR + PR-target + merge_group + on-demand PR gate
playwright-postgresql-nightly.yml (81 lines, new) on-demand only, branch: input Release-branch runner
playwright-mysql-nightly.yml (80 lines) on-demand only, branch: input Release-branch runner

The old on-demand runners (postgresql-nightly-e2e.yml, mysql-nightly-e2e.yml) and the required-check placeholder playwright-mysql-e2e-skip.yml are deleted; their behaviour is folded into the callers above.

Notable design points

  • ref: input on the reusable — branch-runners are always dispatched from main; the input drives every test-path checkout (7 places: cache-keys, build, detect-changes, plan-playwright, restore-playwright-fixture, prepare-playwright-fixture, playwright-ci, plus slack-notify).
  • Security-sensitive checkouts stay pinned — the playwright-summary render script uses github.sha and the timing-baseline refresh uses main, so a compromised release branch can't rewrite the trusted-code paths.
  • playwright-ci-postgresqlplaywright-ci — the sharded job is DB-neutral now. This shows up in 4 tests / 2 comments that parsed the workflow by string; all 103 workflow-parsing tests still pass.
  • Slack notification preserved — both nightly callers expose send_slack_notification (was already there on the old nightly files); the PR flow never had it and still doesn't.

Test plan

  • python3 -m pytest .github/scripts/tests/test_playwright_ci_planning.py .github/scripts/tests/test_playwright_cache_assets.py .github/scripts/tests/test_playwright_performance_gate.py103 passed
  • python3 -c "import yaml; ..." on all 4 workflow YAMLs — parses clean
  • make harness-check — no new warnings introduced (2 pre-existing dead-reference warnings, 2 doc-size, 1 generated-fresh, all unrelated)
  • Dispatch Postgresql PR Playwright E2E Tests (workflow_dispatch) on this branch — full suite, http, coarse=true — and confirm green
  • Dispatch PostgreSQL Playwright E2E (release branch) from main with branch: unify-nightly-e2e-workflows — confirm every checkout picks up this branch, summary/baseline-refresh stay on main
  • Dispatch MySQL Playwright E2E (release branch) from main with branch: unify-nightly-e2e-workflows — confirm mysql compose file (docker-compose.yml) is selected, -d mysql reaches the setup action, --profile mysql-nightly reaches the shadow-gate

🤖 Generated with Claude Code

Greptile Summary

The PR consolidates PostgreSQL and MySQL Playwright pipelines into a reusable workflow while retaining thin trigger-specific callers.

  • Adds database, profile, workflow filename, source-ref, and notification inputs to the shared pipeline.
  • Replaces the old nightly workflows with PostgreSQL and MySQL release-branch callers.
  • Keeps the PostgreSQL PR summary and timing-baseline refresh in the caller to preserve trusted checkout and required-check behavior.
  • Updates workflow-parsing tests and documentation for the renamed shared jobs.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
.github/workflows/playwright-e2e-reusable.yml Introduces the parameterized workflow containing the shared gating, caching, fixture preparation, shard execution, reporting, and notification jobs.
.github/workflows/playwright-postgresql-e2e.yml Converts the PostgreSQL PR workflow into a reusable-workflow caller while retaining its trusted summary and baseline-refresh jobs.
.github/workflows/playwright-postgresql-nightly.yml Adds a manual PostgreSQL release-branch caller that forwards database-specific inputs to the reusable pipeline.
.github/workflows/playwright-mysql-nightly.yml Adds a manual MySQL release-branch caller that selects the MySQL compose and shadow-gate configuration.
.github/scripts/tests/test_playwright_ci_planning.py Updates workflow-structure assertions for the reusable job names and caller-owned summary job.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  PR[PostgreSQL PR and merge triggers] --> PGC[playwright-postgresql-e2e.yml]
  PGD[PostgreSQL release-branch dispatch] --> PGN[playwright-postgresql-nightly.yml]
  MYD[MySQL release-branch dispatch] --> MYN[playwright-mysql-nightly.yml]
  PGC --> RW[playwright-e2e-reusable.yml]
  PGN -->|database: postgresql| RW
  MYN -->|database: mysql| RW
  RW --> PLAN[Change detection and shard planning]
  RW --> FIXTURE[Build or restore fixture]
  RW --> TESTS[Playwright shard jobs]
  RW --> REPORTS[Reports and optional Slack notification]
  PGC --> SUMMARY[Trusted PR summary and baseline refresh]
Loading

Reviews (3): Last reviewed commit: "ci(playwright): move summary+baseline-re..." | Re-trigger Greptile

The nightly postgres and mysql files had drifted from the maintained
`playwright-postgresql-e2e.yml` pipeline — same intent, but hand-copied
shard logic, no smart selection, no distribution/fixture cache, and their
own env blocks. A change to the pipeline meant editing three files.

Extract every pipeline job into `playwright-e2e-reusable.yml`
(`on: workflow_call`) parameterised by `database`, `profile`,
`workflow_filename`, and `ref`. Three thin callers:

  - `playwright-postgresql-e2e.yml` (94 lines) — PR + merge_group +
    pull_request_target + workflow_dispatch on the current branch.
  - `playwright-postgresql-nightly.yml` (81 lines, new) — on-demand,
    takes a `branch:` input for release-branch verification.
  - `playwright-mysql-nightly.yml` (80 lines) — on-demand, same shape;
    MySQL is not a PR gate.

The old on-demand branch-runners (`postgresql-nightly-e2e.yml`,
`mysql-nightly-e2e.yml`) and the required-check placeholder
`playwright-mysql-e2e-skip.yml` are deleted; their behaviour is folded
into the callers above.

Security-sensitive checkouts (`playwright-summary` render script,
`refresh-timing-baseline`) deliberately ignore the branch input and
stay on `github.sha`/`main` so a compromised release branch cannot
tamper with the trusted-code paths.

The 20-line rename `playwright-ci-postgresql` → `playwright-ci` (the
sharded job is DB-neutral now) shows up in the four tests / two comments
that parsed the workflow file by string; all 103 workflow-parsing tests
still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 17, 2026 10:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Aug 17, 2026
Comment on lines +283 to +306
- name: Calculate cache fingerprints
id: fingerprints
env:
# inputs.coarse_bundle is always defined (workflow_call), defaults to true.
COARSE_BUNDLE: ${{ inputs.coarse_bundle }}
run: |
bundle_mode=regular
if [[ "$COARSE_BUNDLE" == "true" ]]; then
bundle_mode=coarse
fi
toolchain=$(mvn --version | sed -n '1p' | tr -d '\r')
distribution=$(python3 .github/scripts/playwright_cache_fingerprint.py \
--kind distribution \
--bundle-mode "$bundle_mode" \
--toolchain "$toolchain")
{
echo "bundle_mode=$bundle_mode"
echo "distribution=$distribution"
echo "fixture=$(python3 .github/scripts/playwright_cache_fingerprint.py --kind fixture)"
echo "ingestion=$(python3 .github/scripts/playwright_cache_fingerprint.py --kind ingestion)"
echo "toolchain=$toolchain"
} >> "$GITHUB_OUTPUT"

build:
Comment on lines +393 to +397
- name: Install antlr cli
if: ${{ steps.distribution-cache.outputs.usable != 'true' }}
run: sudo make install_antlr_cli

- name: Restore openmetadata-ui dist cache
Comment on lines +397 to +417
- name: Restore openmetadata-ui dist cache
# Two guards ANDed together:
# - distribution-cache miss (otherwise we already have the tarball)
# - not a fork PR under pull_request_target — that combination runs
# base-branch privileged with untrusted checked-out code, so a
# cache save would poison the default-branch cache scope (CodeQL
# `actions/actions/cache-poisoning`). Same guard lives inside the
# composite action for defence in depth, but CodeQL is a static
# analyser and needs to see it here at the workflow level.
if: >-
steps.distribution-cache.outputs.usable != 'true' &&
!(github.event_name == 'pull_request_target' &&
github.event.pull_request.head.repo.full_name != github.repository)
uses: ./.github/actions/cache-ui-dist
with:
# PW_E2E_BUILD alters vite chunking; PW_E2E_BUNDLE alters it further.
# Keep those variants in separate cache buckets so a non-e2e build
# never returns an e2e-shaped dist (or vice versa).
variant: pw-e2e-${{ needs.cache-keys.outputs.bundle_mode == 'coarse' && 'bundle' || 'build' }}

- name: Build with Maven
Comment on lines +417 to +424
- name: Build with Maven
if: ${{ steps.distribution-cache.outputs.usable != 'true' }}
env:
PW_E2E_BUILD: "true"
PW_E2E_BUNDLE: ${{ needs.cache-keys.outputs.bundle_mode == 'coarse' }}
run: mvn -DskipTests clean package -pl openmetadata-dist -am

- name: Package OpenMetadata distribution cache entry
Comment on lines +490 to +508
- name: Select PR coverage
id: select
env:
ALL_FILES: ${{ steps.all-changes.outputs.all_changed_files }}
run: |
mkdir -p "$RUNNER_TEMP/playwright-selection"
# shellcheck disable=SC2086
printf '%s\n' $ALL_FILES > "$RUNNER_TEMP/playwright-selection/changed-files.txt"
python3 .github/scripts/select_playwright_tests.py \
--event-name "${{ github.event_name }}" \
--changed-files "$RUNNER_TEMP/playwright-selection/changed-files.txt" \
--impact-map .github/playwright/impact-map.json \
--full-suite "${{ inputs.full_suite }}" \
--output "$RUNNER_TEMP/playwright-selection/selection.json" \
--github-output "$GITHUB_OUTPUT"
jq '{mode, reason, selected: (.selectors | length)}' \
"$RUNNER_TEMP/playwright-selection/selection.json"

- name: Upload Playwright selection
Comment on lines +823 to +830
- name: Install fixture-state dependencies
if: ${{ needs.restore-playwright-fixture.outputs.fixture_cache_hit != 'true' || (needs.plan-playwright.outputs.requires_airflow == 'true' && needs.restore-playwright-fixture.outputs.ingestion_cache_hit != 'true') }}
working-directory: openmetadata-ui/src/main/resources/ui
run: |
corepack enable
yarn --ignore-scripts --frozen-lockfile

- name: Restore Playwright browser cache
Comment on lines +896 to +912
- name: Snapshot database and OpenSearch
if: ${{ needs.restore-playwright-fixture.outputs.fixture_cache_hit != 'true' || (needs.plan-playwright.outputs.requires_airflow == 'true' && needs.restore-playwright-fixture.outputs.ingestion_cache_hit != 'true') }}
env:
CREATE_INGESTION_IMAGE: ${{ needs.plan-playwright.outputs.requires_airflow == 'true' && needs.restore-playwright-fixture.outputs.ingestion_cache_hit != 'true' }}
run: |
if ! command -v zstd >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y zstd
fi
mkdir -p "$FIXTURE_CACHE_DIR" "$INGESTION_CACHE_DIR"
fixture_args=("$FIXTURE_CACHE_DIR/playwright-fixture.tar.zst")
if [[ "$CREATE_INGESTION_IMAGE" == "true" ]]; then
fixture_args+=("$INGESTION_CACHE_DIR/playwright-ingestion-image.tar.zst")
fi
./.github/scripts/create_playwright_fixture.sh "${fixture_args[@]}"

- name: Validate generated golden fixture
Comment thread .github/workflows/playwright-e2e-reusable.yml Fixed
Comment thread .github/workflows/playwright-e2e-reusable.yml Fixed
Comment thread .github/workflows/playwright-e2e-reusable.yml Fixed
…thing

Declaring a `secrets:` block on `on: workflow_call` turns it into the
closed set of secrets the reusable can reference. The prior version
listed only `E2E_SLACK_BOT_OAUTH_TOKEN`, so every `secrets.TEST_*`
reference elsewhere in the file became an undefined property — GitHub
Actions caught this at parse time and reported `startup_failure` on the
`pull_request` run of the caller.

Remove the block entirely so `secrets: inherit` on both callers passes
the full secret set through unchanged. Actionlint drops from 44 errors
to zero (only a pre-existing SC2016 warning remains).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 17, 2026 12:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Startup_failure on the pull_request run:
  "The nested job 'refresh-timing-baseline' is requesting
  'contents: write, pull-requests: write', but is only allowed
  'contents: read, pull-requests: read'."

In reusable workflows the caller's job permissions are the *upper bound*
on what any reusable job can request. The `refresh-timing-baseline` job
inside the reusable declares `contents: write, pull-requests: write` so
it can open the auto-refresh PR — but the callers only granted read.

Elevate the caller-side `jobs.playwright.permissions` on all three
callers so the reusable's baseline-refresh job is inside the grant.
Every other reusable job declares its own read-only permissions block,
and baseline-refresh itself is gated on `github.event_name ==
'merge_group'` — so PRs, PR-targets, and dispatches never invoke a
writer, they just declare a header they don't use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 17, 2026 12:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

✅ Playwright Results — workflow succeeded

Validated commit 9f8f6257821a14182c6eb47d57a53da70ea58aa8 in Playwright run 32058451904, attempt 1.

✅ 551 passed · ❌ 0 failed · 🟡 0 flaky · ⏭️ 0 skipped · 🧰 0 lifecycle flaky

Performance

Blocking targets: ✅ met · Optimization targets: 🟡 in progress

Shard-job maxima below are not the full workflow wall time; the linked run includes build, fixture, planning, and reporting.

🕒 Full workflow signal wall (to summary) 57m 39s

⏱️ Max setup 2m 58s · max shard execution 18m 36s · max shard-job elapsed before upload 22m 24s · reporting 4s

🌐 213.70 requests/attempt · 2.83 app boots/UI scenario · 13.17% common-shard skew

Optimization targets still in progress:

  • Browser traffic was 213.7 requests per attempt (convergence target: fewer than 200).
  • Application boot ratio was 2.83 per UI scenario (1618 boots / 572 scenarios; convergence target: at most 1).
Shard Passed Failed Flaky Skipped Lifecycle failed Lifecycle flaky
✅ Shard chromium-01 142 0 0 0 0 0
✅ Shard chromium-02 133 0 0 0 0 0
✅ Shard chromium-03 124 0 0 0 0 0
✅ Shard data-asset-rules-01 61 0 0 0 0 0
✅ Shard domain-isolation-01 14 0 0 0 0 0
✅ Shard global-state-01 34 0 0 0 0 0
✅ Shard ingestion-01 1 0 0 0 0 0
✅ Shard reindex-01 2 0 0 0 0 0
✅ Shard search-01 11 0 0 0 0 0
✅ Shard search-rbac-01 29 0 0 0 0 0

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Reusable workflow jobs are always reported by GitHub as
`<caller-job> / <sub-job>`, so a `playwright-summary` job inside the
reusable ended up as `playwright / playwright-summary` — invisible to a
branch-protection rule requiring the unprefixed name. Same story for
`refresh-timing-baseline`.

Move both jobs out of the reusable and into `playwright-postgresql-e2e.yml`
so they run at the caller top level and produce the exact check names
branch protection expects:

  * `playwright-summary`         — required PR check + PR-comment payload
  * `Refresh timing baseline`    — merge_group-only auto-PR

To surface the reusable's per-job results/outputs into the caller, add a
`workflow_call.outputs:` block that publishes:
  gate_result, gate_should_run, check_changes_{result,e2e,docker_compose},
  cache_keys_result, build_result,
  detect_changes_{result,mode}, plan_playwright_{result,matrix},
  restore_playwright_fixture_result, prepare_playwright_fixture_result,
  playwright_ci_result

The caller's summary/refresh jobs consume these via
`needs.playwright.outputs.<key>`. Every other reusable job (gate through
playwright-ci) still runs inside the reusable so the mysql/postgres
branch-runners inherit the pipeline unchanged. `slack-notify` inside the
reusable drops its dep on `playwright-summary` and now waits only on
`playwright-ci`.

Reverts the caller-side `contents/pull-requests: write` grant added in
9ccd91f — nothing inside the reusable needs write now that the refresh
job has moved out. Only the caller-level `refresh-timing-baseline`
declares its own write permissions block.

Actionlint warns 9x about `jobs.<id>.result` not being a valid property
in `workflow_call.outputs.value`; GitHub Actions accepts it at runtime
per docs on the `jobs` context. If runtime disagrees we'll add an
aggregator job.

Tests: point workflow-parsing tests at the caller / reusable as
appropriate; all 103 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 17, 2026 19:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@chirag-madlani
chirag-madlani added this pull request to the merge queue Aug 18, 2026
Merged via the queue into main with commit 865f87e Aug 18, 2026
86 of 89 checks passed
@chirag-madlani
chirag-madlani deleted the unify-nightly-e2e-workflows branch August 18, 2026 14:02
@gitar-bot

gitar-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Consolidates PostgreSQL and MySQL Playwright end-to-end pipelines into a single parameterized reusable workflow with thin callers, reducing duplication across configuration files. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants