-
Notifications
You must be signed in to change notification settings - Fork 15
migrate(leftNavigation): PMM-T1830 server diagnostics logs to Playwright #1094
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kiranvuyurru
wants to merge
3
commits into
main
Choose a base branch
from
cursor/migrate-leftnavigation-helpcenter-2547
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
2448229
migrate(leftNavigation): PMM-T1830 server diagnostics logs to Playwright
cursoragent f687d92
Preserve @menu nightly coverage for migrated PMM-T1830 via Playwright
kiranvuyurru 5f4757a
Merge branch 'main' into cursor/migrate-leftnavigation-helpcenter-2547
kiranvuyurru File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
314 changes: 314 additions & 0 deletions
314
.github/workflows/runner-e2e-tests-playwright-remote-nightly-tests.yml
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,314 @@ | ||
| name: _runner-e2e-tests-playwright-remote-nightly-tests | ||
|
|
||
| on: | ||
| workflow_call: | ||
| inputs: | ||
| pmm_server_address: | ||
| required: true | ||
| type: string | ||
| admin_password: | ||
| required: false | ||
| default: 'admin' | ||
| type: string | ||
| pmm_qa_branch: | ||
| required: false | ||
| default: 'main' | ||
| type: string | ||
| pmm_client_version: | ||
| required: false | ||
| default: 'latest-tarball' | ||
| type: string | ||
| pmm_server_image: | ||
| required: false | ||
| default: 'perconalab/pmm-server:3-dev-latest' | ||
| type: string | ||
| tags_for_tests: | ||
| required: true | ||
| type: string | ||
| expected_setup_jobs: | ||
| required: false | ||
| default: 14 | ||
| type: number | ||
| workers: | ||
| required: false | ||
| default: 1 | ||
| type: number | ||
| launchable_confidence: | ||
| required: false | ||
| default: '100%' | ||
| type: string | ||
| installation_type: | ||
| required: false | ||
| default: 'docker' | ||
| type: string | ||
| secrets: | ||
| LAUNCHABLE_TOKEN: | ||
| required: false | ||
| GH_TOKEN: | ||
| required: false | ||
|
|
||
| permissions: | ||
| actions: read | ||
| contents: read | ||
|
|
||
| jobs: | ||
| tests: | ||
| name: "test execution / ${{ inputs.tags_for_tests }}" | ||
| runs-on: ubuntu-22.04 | ||
| timeout-minutes: 120 | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} | ||
| ADMIN_PASSWORD: ${{ inputs.admin_password || 'admin' }} | ||
| PMM_SERVER_ADDRESS: ${{ inputs.pmm_server_address }} | ||
| PMM_QA_BRANCH: ${{ inputs.pmm_qa_branch || 'main' }} | ||
| PMM_QA_GIT_BRANCH: ${{ inputs.pmm_qa_branch || 'main' }} | ||
| PMM_CLIENT_VERSION: ${{ inputs.pmm_client_version || 'latest-tarball' }} | ||
| CLIENT_VERSION: ${{ inputs.pmm_client_version || 'latest-tarball' }} | ||
| DOCKER_VERSION: ${{ inputs.pmm_server_image || 'perconalab/pmm-server:3-dev-latest' }} | ||
| PMM_TEST_FLAG: ${{ inputs.tags_for_tests }} | ||
| TAGS_FOR_TESTS: ${{ inputs.tags_for_tests }} | ||
| WORKERS: ${{ inputs.workers || 1 }} | ||
| LAUNCHABLE_CONFIDENCE: ${{ inputs.launchable_confidence || '100%' }} | ||
| INSTALLATION_TYPE: ${{ inputs.installation_type || 'docker' }} | ||
| LAUNCHABLE_TOKEN: ${{ secrets.LAUNCHABLE_TOKEN }} | ||
| SUBSET_FILE_NAME: 'launchable-subset.txt' | ||
| SETUP_WAIT_STEP: 'Waiting for tests execution' | ||
| SETUP_JOB_PREFIX: 'setup / ' | ||
| EXPECTED_SETUP_JOBS: ${{ inputs.expected_setup_jobs || 14 }} | ||
| WAIT_POLL_INTERVAL_SECONDS: 600 | ||
| WAIT_TIMEOUT_SECONDS: 10800 | ||
|
|
||
| steps: | ||
| - name: Checkout pmm-qa repo | ||
| uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 | ||
| with: | ||
| repository: percona/pmm-qa | ||
| ref: ${{ env.PMM_QA_BRANCH }} | ||
| path: ./pmm-qa | ||
|
|
||
| - name: Cleanup disk space on a worker according to https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 | ||
| run: | | ||
| sudo rm -rf /usr/share/dotnet | ||
| sudo rm -rf /opt/ghc | ||
| sudo rm -rf "/usr/local/share/boost" | ||
| sudo rm -rf "$AGENT_TOOLSDIRECTORY" | ||
|
|
||
| - name: Resolve PMM Server URLs | ||
| id: pmm_urls | ||
| run: | | ||
| ADDR="${{ env.PMM_SERVER_ADDRESS }}" | ||
| ADDR_NO_SCHEME=${ADDR#http://} | ||
| ADDR_NO_SCHEME=${ADDR_NO_SCHEME#https://} | ||
| ADDR_NO_SCHEME=${ADDR_NO_SCHEME%/} | ||
| echo "PMM_UI_URL=https://${ADDR_NO_SCHEME}/" >> "$GITHUB_ENV" | ||
| echo "PMM_URL=https://admin:${{ env.ADMIN_PASSWORD }}@${ADDR_NO_SCHEME}" >> "$GITHUB_ENV" | ||
| echo "SERVER_IP=${ADDR_NO_SCHEME}" >> "$GITHUB_ENV" | ||
|
|
||
| - name: Sanity check remote PMM Server reachability | ||
| run: | | ||
| set -eux | ||
| for i in $(seq 1 30); do | ||
| code=$(curl -ksS -o /dev/null -w "%{http_code}" "https://${SERVER_IP}/v1/server/readyz" || echo "000") | ||
| if [ "$code" = "200" ]; then | ||
| echo "Remote PMM Server is ready" | ||
| exit 0 | ||
| fi | ||
| echo "Attempt $i: PMM Server not ready (http=$code), sleeping 5s" | ||
| sleep 5 | ||
| done | ||
| echo "Remote PMM Server did not become ready in time" >&2 | ||
| exit 1 | ||
|
|
||
| - name: Setup npm modules for e2e tests | ||
| working-directory: pmm-qa/e2e_tests | ||
| run: | | ||
| npm ci | ||
| npx playwright install-deps | ||
| npx playwright install chromium | ||
|
|
||
| - uses: actions/github-script@v7 | ||
| id: tags_processed | ||
| with: | ||
| script: | | ||
| return `${process.env.TAGS_FOR_TESTS.replaceAll('|', '-')}` | ||
| result-encoding: string | ||
|
|
||
| - name: Prepare launchable | ||
| working-directory: pmm-qa/e2e_tests | ||
| run: | | ||
| pip3 install --user --upgrade launchable~=1.0 | ||
| launchable verify || true | ||
|
|
||
| docker pull ${{ env.DOCKER_VERSION }} || true | ||
| export DOCKER_IMAGE_ID=$(docker inspect -f '{{index .RepoDigests 0}}' ${{ env.DOCKER_VERSION }} | cut -d@ -f2) || true | ||
| echo "Launchable build: ${DOCKER_IMAGE_ID}" | ||
| echo "Launchable confidence: ${{ env.LAUNCHABLE_CONFIDENCE }}" | ||
|
|
||
| launchable record session --build ${DOCKER_IMAGE_ID} --test-suite "${{ env.INSTALLATION_TYPE }}-nightly-ui-tests-${{ steps.tags_processed.outputs.result }}" > launchable-session.txt || true | ||
| node launchable-prepare.js "${{ env.PMM_TEST_FLAG }}" | ||
| cat test_list.txt | launchable subset --session $(cat launchable-session.txt) --confidence ${{ env.LAUNCHABLE_CONFIDENCE }} --use-case feature-branch playwright > ${{ env.SUBSET_FILE_NAME }} || true | ||
| echo "$(cat ${{ env.SUBSET_FILE_NAME }})" || true | ||
|
|
||
| - name: Check if launchable subset is empty | ||
| id: check_launchable_subset | ||
| working-directory: pmm-qa/e2e_tests | ||
| run: | | ||
| if [ -s "${{ env.SUBSET_FILE_NAME }}" ]; then | ||
| echo "has_subset=true" >> "$GITHUB_OUTPUT" | ||
| echo "Launchable subset is not empty. Continue with tests." | ||
| else | ||
| echo "has_subset=false" >> "$GITHUB_OUTPUT" | ||
| echo "Launchable subset is empty. Test execution will be skipped." | ||
| fi | ||
|
|
||
| - name: Skip notice | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset != 'true' }} | ||
| run: echo "No tests selected by Launchable subset. Skipping setup wait and test execution." | ||
|
|
||
| - name: Wait for all setup jobs to be ready | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset == 'true' }} | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const owner = context.repo.owner; | ||
| const repo = context.repo.repo; | ||
| const run_id = context.runId; | ||
| const expectedSetupJobs = Number(process.env.EXPECTED_SETUP_JOBS); | ||
| const pollIntervalMs = Number(process.env.WAIT_POLL_INTERVAL_SECONDS) * 1000; | ||
| const timeoutMs = Number(process.env.WAIT_TIMEOUT_SECONDS) * 1000; | ||
| const setupPrefix = process.env.SETUP_JOB_PREFIX; | ||
| const setupWaitStep = process.env.SETUP_WAIT_STEP; | ||
| const startedAt = Date.now(); | ||
|
|
||
| async function sleep(ms) { | ||
| return new Promise((resolve) => setTimeout(resolve, ms)); | ||
| } | ||
|
|
||
| async function listJobs() { | ||
| return github.paginate(github.rest.actions.listJobsForWorkflowRun, { | ||
| owner, | ||
| repo, | ||
| run_id, | ||
| filter: 'latest', | ||
| per_page: 100, | ||
| headers: { | ||
| 'X-GitHub-Api-Version': '2026-03-10', | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| async function getJob(job) { | ||
| const response = await github.rest.actions.getJobForWorkflowRun({ | ||
| owner, | ||
| repo, | ||
| job_id: job.id, | ||
| headers: { | ||
| 'X-GitHub-Api-Version': '2026-03-10', | ||
| }, | ||
| }); | ||
| return response.data; | ||
| } | ||
|
|
||
| function findSetupWaitStep(job) { | ||
| return (job.steps || []).find((step) => step.name === setupWaitStep); | ||
| } | ||
|
|
||
| function isWaitingForTests(job) { | ||
| const step = findSetupWaitStep(job); | ||
| return step?.status === 'in_progress'; | ||
| } | ||
|
|
||
| function finishedWaitingSuccessfully(job) { | ||
| const step = findSetupWaitStep(job); | ||
| return step?.status === 'completed' && step?.conclusion === 'success'; | ||
| } | ||
|
|
||
| function setupJobFailed(job) { | ||
| return ['failure', 'cancelled'].includes(job.conclusion); | ||
| } | ||
|
|
||
| while (true) { | ||
| const jobs = await listJobs(); | ||
| const setupJobs = jobs.filter((job) => job.name.startsWith(setupPrefix) || job.name.includes(setupPrefix)); | ||
| const detailedSetupJobs = await Promise.all(setupJobs.map(getJob)); | ||
| const setupJobsWithWaitStep = detailedSetupJobs.filter(findSetupWaitStep); | ||
| const readySetupJobs = setupJobsWithWaitStep.filter(isWaitingForTests); | ||
| const failedSetupJobs = detailedSetupJobs.filter(setupJobFailed); | ||
| const finishedEarlyJobs = setupJobsWithWaitStep.filter(finishedWaitingSuccessfully); | ||
|
|
||
| core.info(`Setup jobs: discovered=${setupJobsWithWaitStep.length}/${expectedSetupJobs}, ready=${readySetupJobs.length}/${expectedSetupJobs}`); | ||
| for (const job of setupJobsWithWaitStep) { | ||
| const step = findSetupWaitStep(job); | ||
| core.info(`- ${job.name}: job_status=${job.status}, step_status=${step.status}, step_conclusion=${step.conclusion || 'null'}, job_conclusion=${job.conclusion || 'null'}`); | ||
| } | ||
|
|
||
| if (failedSetupJobs.length > 0) { | ||
| throw new Error(`Setup job(s) failed or were cancelled: ${failedSetupJobs.map((job) => `${job.name} (${job.conclusion})`).join(', ')}`); | ||
| } | ||
|
|
||
| if (finishedEarlyJobs.length > 0) { | ||
| throw new Error(`Setup wait step completed before this test job was ready: ${finishedEarlyJobs.map((job) => job.name).join(', ')}`); | ||
| } | ||
|
|
||
| if (setupJobsWithWaitStep.length >= expectedSetupJobs && readySetupJobs.length >= expectedSetupJobs) { | ||
| core.info('All setup jobs are waiting for test execution.'); | ||
| return; | ||
| } | ||
|
|
||
| if (Date.now() - startedAt > timeoutMs) { | ||
| throw new Error(`Timed out waiting for ${expectedSetupJobs} setup jobs to reach '${setupWaitStep}' with status=in_progress.`); | ||
| } | ||
|
|
||
| await sleep(pollIntervalMs); | ||
| } | ||
|
|
||
| - name: Wait for metrics to be collected | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset == 'true' }} | ||
| working-directory: pmm-qa/e2e_tests | ||
| run: | | ||
| sleep 60 | ||
|
|
||
| - name: 'Run UI tests: ${{ env.PMM_TEST_FLAG }} with launchable' | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset == 'true' }} | ||
| working-directory: pmm-qa/e2e_tests | ||
| run: | | ||
| npx playwright test --grep "${{ env.PMM_TEST_FLAG }}" $(cat "${{ env.SUBSET_FILE_NAME }}") || true | ||
|
|
||
| - name: Download PMM Server logs | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset == 'true' && always() }} | ||
| working-directory: pmm-qa/e2e_tests | ||
| continue-on-error: true | ||
| run: | | ||
| curl --insecure "https://admin:${{ env.ADMIN_PASSWORD }}@${SERVER_IP}/logs.zip" --output logs.zip | ||
| unzip logs.zip -d logs || true | ||
|
|
||
| - name: Record launchable test results | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset == 'true' && always() }} | ||
| working-directory: pmm-qa/e2e_tests | ||
| run: | | ||
| launchable record tests --session $(cat launchable-session.txt) playwright --json output/results.json || true | ||
| launchable gate --session "$(cat launchable-session.txt)" | ||
|
|
||
| - name: Record launchable logs on failure | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset == 'true' && failure() }} | ||
| working-directory: pmm-qa/e2e_tests | ||
| continue-on-error: true | ||
| run: | | ||
| launchable record attachment --session $(cat launchable-session.txt) --include "*.log" logs.zip || true | ||
|
|
||
| - uses: actions/upload-artifact@v4 | ||
| if: ${{ failure() }} | ||
| continue-on-error: true | ||
| with: | ||
| name: "artifacts_${{ steps.tags_processed.outputs.result }}" | ||
| path: | | ||
| pmm-qa/e2e_tests/playwright-report | ||
| pmm-qa/e2e_tests/screenshots | ||
| pmm-qa/e2e_tests/logs | ||
|
|
||
| - uses: actions/upload-artifact@v4 | ||
| if: ${{ steps.check_launchable_subset.outputs.has_subset == 'true' && always() }} | ||
| continue-on-error: true | ||
| with: | ||
| name: "junit_${{ steps.tags_processed.outputs.result }}" | ||
| path: pmm-qa/e2e_tests/output/results.json | ||
File renamed without changes.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Make required failures fail the workflow.
Make it so
|| truedoes not convert required failures into success. A Launchable error can leavelaunchable-subset.txtempty, and Lines 152-166 then skip required@menucoverage. A Playwright failure also leaves the step successful, so failure-only diagnostics and artifacts do not run..github/workflows/runner-e2e-tests-playwright-remote-nightly-tests.yml#L271-L275: remove|| trueand preserve the Playwright exit status..github/workflows/runner-e2e-tests-playwright-remote-nightly-tests.yml#L147-L150: fail Launchable preparation, or implement an explicit full-test fallback when Launchable is unavailable.🧰 Tools
🪛 zizmor (1.29.0)
[warning] 275-275: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
📍 Affects 1 file
.github/workflows/runner-e2e-tests-playwright-remote-nightly-tests.yml#L271-L275(this comment).github/workflows/runner-e2e-tests-playwright-remote-nightly-tests.yml#L147-L150🤖 Prompt for AI Agents