diff --git a/.cursor/AUTOMATIONS.md b/.cursor/AUTOMATIONS.md new file mode 100644 index 000000000..9603bffee --- /dev/null +++ b/.cursor/AUTOMATIONS.md @@ -0,0 +1,5 @@ +# Cursor Cloud Automations + +Dashboard spec (pointer prompts + canvas sync): **[docs/agents/AUTOMATIONS.md](../docs/agents/AUTOMATIONS.md)** + +Agent roles: `.cursor/agents/`. Skills: `.cursor/skills/`. diff --git a/.cursor/agents/test-healer.md b/.cursor/agents/test-healer.md new file mode 100644 index 000000000..797beaa84 --- /dev/null +++ b/.cursor/agents/test-healer.md @@ -0,0 +1,58 @@ +--- +name: test-healer +description: Use proactively when Percona-Lab/pmm-submodules FB Tests or CI checks fail — triage product vs test bug, reproduce with the same setup as the FB GitHub workflow, fix percona/pmm-qa, open PR. Trigger on failed gh pr checks, FB test failures, or when asked to heal/fix flaky or broken PMM QA tests. +--- + +# Test Healer + +You are **Test Healer** — PMM FB Tests triage and repair cloud agent. + +**Input:** pmm-submodules PR number, Actions run URL, or triggering GitHub workflow event. + +## Knowledge (read by path) + +| Skill | Path | +|-------|------| +| FB checks, workflow mapping | `.cursor/skills/pmm-fb-tests/SKILL.md` | +| FB workflow provisioning | `.cursor/skills/pmm-provisioning/SKILL.md` | +| Repo map | `.cursor/skills/pmm-repos/SKILL.md` | +| Jira (optional context) | `.cursor/skills/pmm-jira/SKILL.md` | + +## Workflow + +1. **Evidence** — `gh pr checks -R Percona-Lab/pmm-submodules`. If all green → exit immediately. Latest FB build only. Map failures to `pmm-qa/.github/workflows/` runners (see `pmm-fb-tests`). Build a **failure list**: every failed check plus each failing test name, spec path, and `@tag` from the Actions log. +2. **Classify** — **Product bug** → stop (no pmm-qa PR). **Test bug** → continue (wrong selector, flaky timing, setup failure, out-of-scope FB red). +3. **Dedup (mandatory — stop if work already in flight)** — Before reproduce, fix, or any new PR: + - List **every** failing test identifier from step 1 (not just one tag — a run can fail 5+ tests). + - Fetch open pmm-qa PRs and read bodies (title alone is not enough): + ```bash + gh pr list -R percona/pmm-qa --state open --limit 50 --json number,title,body + ``` + - For each open PR, look for section `## FB failures fixed (healer)` (or same tests listed in body). If **any** identifier from your failure list already appears in an open PR → **stop immediately**. Do not reproduce, commit, or open a new PR. Reply with that PR URL. + - Also match on `pmm-submodules PR: #` in that section when the trigger is the same submodules PR and the test list overlaps. + - If no open PR covers these failures → continue. +4. **Reproduce** — Same steps as failed FB job. UI: `runner-e2e-tests-codeceptjs.yml` (legacy CodeceptJS) **or** `runner-e2e-tests-playwright.yml` (`e2e_tests/`). CLI: `runner-integration-cli-tests.yml`. **Not** Jenkins staging. +5. **Fix** — Minimal change in `percona/pmm-qa` only. Re-run failed suite until green. +6. **PR** — Open **one** PR on `percona/pmm-qa`. Body **must** include: + +```markdown +## FB failures fixed (healer) + +- pmm-submodules PR: # +- tests: + - / @tag + - ... +``` + +List **all** tests fixed in this PR so future runs can dedup via step 3. + +## Cleanup + +Remove all Docker resources when finished (see `pmm-provisioning` cleanup section or full docker teardown). + +## Never + +- Fix `percona/pmm` or `percona/grafana` for FB failures +- Clone `pmm-submodules` +- Act on green FB runs +- Start work when an open pmm-qa PR already lists any of the same failing tests diff --git a/.cursor/agents/test-reporter.md b/.cursor/agents/test-reporter.md new file mode 100644 index 000000000..a5d64505e --- /dev/null +++ b/.cursor/agents/test-reporter.md @@ -0,0 +1,31 @@ +--- +name: test-reporter +description: Use when pmm-submodules FB Tests finish all green — capture FB test evidence and update Jira customfield_10492 (FB test screenshots). Trigger on successful FB checks, green CI on pmm-submodules, or when asked to attach FB screenshot to a PMM Jira ticket. +--- + +# Test Reporter + +You are **Test Reporter** — attach FB test evidence to Jira when all checks pass. + +**Input:** pmm-submodules PR number or linked Jira ticket from trigger context. + +## Knowledge (read by path) + +| Skill | Path | +|-------|------| +| FB checks (green gate) | `.cursor/skills/pmm-fb-tests/SKILL.md` | +| Jira field update, attachments | `.cursor/skills/pmm-jira/SKILL.md` | +| UI screenshot of Actions run | `.cursor/skills/pmm-ui-evidence/SKILL.md` | +| Repo map | `.cursor/skills/pmm-repos/SKILL.md` | + +## Workflow + +1. **Verify green** — `gh pr checks -R Percona-Lab/pmm-submodules` — any `fail` → **stop** (no screenshot, no Jira update). +2. **Screenshot** — FB Tests Actions run (not PR checks page). Use **computer use** per `pmm-ui-evidence`. +3. **Update Jira** — `customfield_10492` with run URL and image attachment via Atlassian MCP. + +## Never + +- Attach green screenshot when checks failed +- Clone `pmm-submodules` +- Post Jira **comments** — this role only updates `customfield_10492` (and attachments). Comments are Test Runner / human QA. diff --git a/.cursor/agents/test-runner.md b/.cursor/agents/test-runner.md new file mode 100644 index 000000000..767901a43 --- /dev/null +++ b/.cursor/agents/test-runner.md @@ -0,0 +1,40 @@ +--- +name: test-runner +description: Use proactively for PMM manual QA on a Jira ticket — read requirements, verify against code, provision PMM on the cloud VM, execute tests (terminal + UI), post Developers-only Jira results, optionally open a pmm-qa PR. Trigger when the user asks to test a ticket, run QA, verify a PMM fix, or debug manual test steps for PMM-XXXX. +--- + +# Test Runner + +You are **Test Runner** — PMM manual QA cloud agent. + +**Input:** Jira key (e.g. `PMM-15196`) from the user, webhook `issueKey`, or Slack/Jira message. Natural language is fine ("please test PMM-15196") — you do not need a rigid slash prefix when the role file is loaded. + +## Knowledge (read by path before acting) + +| Skill | Path | +|-------|------| +| Jira read/write, visibility | `.cursor/skills/pmm-jira/SKILL.md` | +| FB checks, JNKPercona | `.cursor/skills/pmm-fb-tests/SKILL.md` | +| Docker, MicroVM, pmm-framework | `.cursor/skills/pmm-provisioning/SKILL.md` | +| PR diffs, JSON dashboards | `.cursor/skills/pmm-git-diff/SKILL.md` | +| UI screenshots / recordings | `.cursor/skills/pmm-ui-evidence/SKILL.md` | +| Repo map, gh rules | `.cursor/skills/pmm-repos/SKILL.md` | + +Read each file when its step needs it. Do not guess field IDs or setup commands. + +## Workflow + +1. **Read ticket** — Atlassian MCP: summary, AC, `customfield_10083`, `customfield_10492`, dev links, comments. Cross-check with `pmm-git-diff` on linked `percona/pmm` / `percona/grafana` PRs. +2. **Plan** — Short test plan: criteria, `DOCKER_ENV_VARIABLE`, `CLIENTS` / DB needs, post-provision steps, FB images from latest JNKPercona comment on linked pmm-submodules PR (`gh` only). +3. **Provision** — Follow `pmm-provisioning` skill (`.cursor/environment.json` + `qa-integration/pmm_qa/pmm-framework/pmm-framework`). +4. **Execute** — Terminal for API/CLI; **computer use** for UI per `pmm-ui-evidence`. +5. **Report** — One Jira comment, **Developers visibility only** (see `pmm-jira`). Include pass/fail per criterion, artifact paths, blockers. Do not mark pass if criteria failed. +6. **Automation decision** — After manual QA: if a minimal `pmm-qa` test adds clear value, implement and open PR to `percona/pmm-qa` only. Otherwise stop after Jira comment. + +## Never + +- Open PRs to `percona/pmm` or `percona/grafana` +- `git clone` `Percona-Lab/pmm-submodules` +- Modify `qa-integration/` in this agent PR (provisioning fixes are separate upstream PRs) +- Trust "How to test" without reading PR diff +- Post public Jira comments on QA results diff --git a/.cursor/environment.json b/.cursor/environment.json new file mode 100644 index 000000000..9e794cb44 --- /dev/null +++ b/.cursor/environment.json @@ -0,0 +1,4 @@ +{ + "install": "sudo service docker start 2>/dev/null || true; docker info >/dev/null 2>&1 || sudo dockerd >/tmp/dockerd.log 2>&1 & sleep 5; if ! command -v gh >/dev/null 2>&1; then curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg 2>/dev/null; sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg; echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\" | sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null; sudo apt-get update -qq && sudo apt-get install -y gh; fi; npm install -g json-diff; QA_ROOT=\"${PWD}\"; [ -d pmm-qa ] && QA_ROOT=\"${PWD}/pmm-qa\"; cd \"${QA_ROOT}/codeceptjs-e2e\" && npm ci && npx playwright install --with-deps; cd \"${QA_ROOT}/cli\" && npm ci; cd \"${QA_ROOT}/qa-integration/pmm_qa\" && python3 -m venv virtenv && . virtenv/bin/activate && pip install --upgrade pip setuptools && pip install -r requirements.txt 'docker>=7.0.0'; ansible-galaxy collection install -p \"${HOME}/.ansible/collections\" community.docker >/dev/null 2>&1 || true; chmod +x \"${QA_ROOT}/qa-integration/pmm_qa/pmm-framework/pmm-framework\" \"${QA_ROOT}/.cursor/scripts/\"*.sh 2>/dev/null || true", + "start": "TMUX_CONF=/exec-daemon/tmux.portal.conf; if ! docker info >/dev/null 2>&1; then if ! tmux -f \"$TMUX_CONF\" has-session -t =dockerd 2>/dev/null; then tmux -f \"$TMUX_CONF\" new-session -d -s dockerd -c /tmp -- sudo dockerd; fi; for _ in $(seq 1 30); do [ -S /var/run/docker.sock ] && break; sleep 1; done; docker info >/dev/null 2>&1 || sudo chmod 666 /var/run/docker.sock; fi; export ANSIBLE_COLLECTIONS_PATH=\"${HOME}/.ansible/collections:${ANSIBLE_COLLECTIONS_PATH:-/usr/lib/python3/dist-packages/ansible_collections}\"" +} diff --git a/.cursor/hooks.json b/.cursor/hooks.json new file mode 100644 index 000000000..bcba84b17 --- /dev/null +++ b/.cursor/hooks.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "hooks": { + "beforeShellExecution": [ + { + "command": ".cursor/hooks/block-pmm-submodules-clone.sh", + "matcher": "git clone", + "failClosed": false + } + ], + "stop": [ + { + "type": "prompt", + "prompt": "If this run performed PMM QA (test-runner or test-reporter role) and reported results to Jira, confirm a comment was added with Developers-only visibility (not public). If results were produced but no such Jira comment exists, return a followup_message instructing the agent to post using .cursor/skills/pmm-jira/SKILL.md visibility rules. Otherwise allow completion. Input: $ARGUMENTS", + "loop_limit": 2 + } + ] + } +} diff --git a/.cursor/hooks/block-pmm-submodules-clone.sh b/.cursor/hooks/block-pmm-submodules-clone.sh new file mode 100644 index 000000000..16ef4a778 --- /dev/null +++ b/.cursor/hooks/block-pmm-submodules-clone.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Block cloning Percona-Lab/pmm-submodules — use gh only. +input=$(cat) +command=$(echo "$input" | jq -r '.command // empty') + +if echo "$command" | grep -qiE 'git\s+clone.*pmm-submodules'; then + echo '{ + "permission": "deny", + "user_message": "Cloning pmm-submodules is not allowed. Use gh pr checks and gh api on Percona-Lab/pmm-submodules instead.", + "agent_message": "Never clone pmm-submodules. Use gh only per pmm-repos skill." + }' + exit 0 +fi + +echo '{ "permission": "allow" }' +exit 0 diff --git a/.cursor/scripts/pmm-ui-login.sh b/.cursor/scripts/pmm-ui-login.sh new file mode 100755 index 000000000..2dc670702 --- /dev/null +++ b/.cursor/scripts/pmm-ui-login.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Log into PMM UI via playwright-cli (Cloud Agent / MicroVM). +# Avoids the Grafana login form and self-signed TLS issues. +# +# Usage: +# export PMM_URL='https://127.0.0.1' # optional, default below +# export ADMIN_PASSWORD='pmm3admin!' # optional +# .cursor/scripts/pmm-ui-login.sh PMM-14576 +# +# Session name: pmm- (reuse the same id for follow-up UI commands). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" +PW_CONFIG="${REPO_ROOT}/.playwright/cli.config.json" + +SESSION_ID="${1:?usage: $0 e.g. PMM-14576}" +PMM_URL="${PMM_URL:-https://127.0.0.1}" +ADMIN_PASSWORD="${ADMIN_PASSWORD:-pmm3admin!}" +PLAYWRIGHT_SESSION="pmm-${SESSION_ID}" + +export PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS="${PLAYWRIGHT_MCP_IGNORE_HTTPS_ERRORS:-1}" +export DISPLAY="${DISPLAY:-:1}" + +if ! command -v playwright-cli >/dev/null 2>&1; then + echo "ERROR: playwright-cli not found on PATH" >&2 + exit 1 +fi + +AUTH_TOKEN="$(printf 'admin:%s' "$ADMIN_PASSWORD" | base64 -w0 2>/dev/null || printf 'admin:%s' "$ADMIN_PASSWORD" | base64)" + +PW_CLI=(playwright-cli --config="$PW_CONFIG") +OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open --headed "$PMM_URL") +if [ "${PMM_UI_HEADED:-1}" = "0" ]; then + OPEN_ARGS=(-s="$PLAYWRIGHT_SESSION" open "$PMM_URL") +fi + +"${PW_CLI[@]}" "${OPEN_ARGS[@]}" + +PMM_UI_WIDTH="${PMM_UI_WIDTH:-1920}" +PMM_UI_HEIGHT="${PMM_UI_HEIGHT:-1200}" +"${PW_CLI[@]}" -s="$PLAYWRIGHT_SESSION" resize "$PMM_UI_WIDTH" "$PMM_UI_HEIGHT" >/dev/null 2>&1 || true + +# shellcheck disable=SC2016 +"${PW_CLI[@]}" -s="$PLAYWRIGHT_SESSION" run-code "async page => { + const base = page.url().match(/^https?:\\/\\/[^/]+/)[0]; + await page.setExtraHTTPHeaders({ Authorization: 'Basic ${AUTH_TOKEN}' }); + await page.route('**/api/user/auth-tokens/rotate', r => r.fulfill({ status: 200, contentType: 'application/json', body: '{}' })); + await page.route('**/v1/users/me', r => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ alerting_tour_completed: true, product_tour_completed: true, snoozed_pmm_version: '', user_id: 1 }) })); + await page.route('**/v1/server/updates?force=**', r => r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ installed: {}, last_check: new Date().toISOString(), latest: {}, update_available: false }) })); + const res = await page.request.post(base + '/graph/login', { data: { user: 'admin', password: '${ADMIN_PASSWORD}' } }); + if (res.status() >= 400) throw new Error('Login failed: HTTP ' + res.status() + ' ' + await res.text()); + await page.goto(base + '/pmm-ui/help'); +}" + +echo "PMM UI login OK (session=${PLAYWRIGHT_SESSION}, url=${PMM_URL})" diff --git a/.cursor/settings.json b/.cursor/settings.json new file mode 100644 index 000000000..32c28fc75 --- /dev/null +++ b/.cursor/settings.json @@ -0,0 +1,7 @@ +{ + "plugins": { + "atlassian": { + "enabled": true + } + } +} diff --git a/.cursor/skills/pmm-fb-tests/SKILL.md b/.cursor/skills/pmm-fb-tests/SKILL.md new file mode 100644 index 000000000..8e128faa8 --- /dev/null +++ b/.cursor/skills/pmm-fb-tests/SKILL.md @@ -0,0 +1,56 @@ +--- +name: pmm-fb-tests +description: Analyze Percona-Lab/pmm-submodules FB Tests via gh pr checks, JNKPercona build comments, flaky triage, and map failures to pmm-qa GitHub workflow runners. Use when reading FB test status, finding server/client docker versions, or deciding what failed in FB CI. +--- + +# PMM FB Tests + +**pmm-submodules** — use `gh` only. **Never** `git clone` this repo. + +## Collect checks + +```bash +gh pr checks -R Percona-Lab/pmm-submodules +``` + +- **Latest FB build only** — older comments/checks are invalid +- Ignore JNKPercona "API tests have succeded/failed" comments + +## JNKPercona build comment (latest only) + +```bash +gh api repos/Percona-Lab/pmm-submodules/issues//comments \ + --jq '[.[] | select(.user.login == "JNKPercona" and (.body | contains("Staging instance:"))) | {created_at, body}] | sort_by(.created_at) | .[-1]' +``` + +| Field in comment | Use as | +|------------------|--------| +| Server docker | `DOCKER_VERSION` / `PMM_SERVER_IMAGE` | +| Watchtower docker | `WATCHTOWER_VERSION` | +| Client tarball | `CLIENT_VERSION` | +| Client docker | **ignore** for `CLIENT_VERSION` | + +## Map failures to workflows + +| Failed check pattern | pmm-qa workflow | Runner | +|---------------------|-----------------|--------| +| `@* UI tests` | `fb-e2e-suite.yml` | `runner-e2e-tests-codeceptjs.yml` (legacy) or `runner-e2e-tests-playwright.yml` (`e2e_tests/`) | +| `CLI tests *` | `fb-integration-suite.yml` | `runner-integration-cli-tests.yml` | + +Extract `setup_services` / `tags_for_tests` or `services_list` / `cli_tag` from the failed job inputs. + +## Flaky triage + +Mark each failure: **relevant** (overlaps ticket) / **flaky** / **out of scope**. Only expand manual scope for **relevant** failures. + +## Green gate (Test Reporter) + +```bash +gh pr checks -R Percona-Lab/pmm-submodules 2>&1 | grep -E '\tfail\t' +``` + +Any output → do **not** attach green screenshot to Jira. + +## Detail + +See [references/fb-tests.md](references/fb-tests.md) (full templates and screenshot workflow). diff --git a/.cursor/skills/pmm-manual-test/fb-tests.md b/.cursor/skills/pmm-fb-tests/references/fb-tests.md similarity index 85% rename from .cursor/skills/pmm-manual-test/fb-tests.md rename to .cursor/skills/pmm-fb-tests/references/fb-tests.md index 8554f8be7..f6a543096 100644 --- a/.cursor/skills/pmm-manual-test/fb-tests.md +++ b/.cursor/skills/pmm-fb-tests/references/fb-tests.md @@ -110,6 +110,31 @@ Save screenshots to a temp path the user can find (e.g. workspace root or `%TEMP ## Update Jira +### Comment visibility (mandatory) + +**Every Jira comment** on `perconadev.atlassian.net` PMM tickets MUST be restricted to the **Developers** role. Omitting visibility posts a **public** comment (visible to reporters/customers) — never do that for QA notes, triage, or test results. + +**Always** pass visibility when calling a comment tool: + +| MCP server | Tool | Required parameter | +|------------|------|--------------------| +| `user-mcp-atlassian` | `jira_add_comment` | `visibility: "{\"type\":\"role\",\"value\":\"Developers\"}"` | +| `plugin-atlassian-atlassian` | `addCommentToJiraIssue` | `commentVisibility: {"type": "role", "value": "Developers"}` | + +Example (`user-mcp-atlassian`): + +```json +jira_add_comment( + issue_key: "PMM-14915", + body: "*QA note (dev)* — ...", + visibility: "{\"type\":\"role\",\"value\":\"Developers\"}" +) +``` + +If the MCP tool does not accept a visibility parameter, **do not post** — show the draft to the user and ask them to paste it with **Restrict to → Developers**. + +`jira_update_issue` on custom fields below does **not** use comment visibility (different mechanism). + ### Custom fields | Field | ID | Use | diff --git a/.cursor/skills/pmm-git-diff/SKILL.md b/.cursor/skills/pmm-git-diff/SKILL.md new file mode 100644 index 000000000..b36b520a3 --- /dev/null +++ b/.cursor/skills/pmm-git-diff/SKILL.md @@ -0,0 +1,41 @@ +--- +name: pmm-git-diff +description: Read and summarize git diffs for percona/pmm and percona/grafana PRs linked to a PMM Jira ticket. Use before planning manual QA or when Test Runner needs PR scope. Includes JSON dashboard diff guidance. +--- + +# PMM git diff + +Read `.cursor/skills/pmm-repos/SKILL.md` for repo rules. Never clone `pmm-submodules`. + +## Find and diff PRs + +```bash +gh search prs "" --repo percona/pmm --json number,title,url +gh search prs "" --repo percona/grafana --json number,title,url +gh pr diff --repo percona/pmm +``` + +Return: files changed, behavioral summary, gaps in "How to test", suggested manual checks. + +## Large Grafana dashboard JSON diffs + +Grafana PRs often change dashboard JSON under `grafana/public/` or packaged dashboards. A raw `gh pr diff` can be **thousands of lines** of minified JSON — do not read the full diff inline. + +1. List changed JSON files: `gh pr diff --repo percona/grafana --name-only | grep -E '\.json$'` +2. For each file, use a structural JSON diff tool (installed in the PMM cloud environment): + +```bash +gh pr diff --repo percona/grafana -- path/to/dashboard.json | json-diff /dev/stdin +# or save base/head and compare: +json-diff base.json head.json +``` + +3. Summarize **what panels/queries/alerts changed**, not every byte. + +`json-diff` is installed globally via `.cursor/environment.json` (`npm install -g json-diff`). + +## When diff is too large even for json-diff + +- Read the PR description and linked Jira AC first +- Use `gh api` to fetch only the files list and line change counts +- Ask the dev for a testing focus if dashboard churn is unrelated to the ticket diff --git a/.cursor/skills/pmm-manual-test/SKILL.md b/.cursor/skills/pmm-jenkins-staging/SKILL.md similarity index 89% rename from .cursor/skills/pmm-manual-test/SKILL.md rename to .cursor/skills/pmm-jenkins-staging/SKILL.md index 9b47e8f0b..6ea4c6821 100644 --- a/.cursor/skills/pmm-manual-test/SKILL.md +++ b/.cursor/skills/pmm-jenkins-staging/SKILL.md @@ -1,6 +1,6 @@ --- -name: pmm-manual-test -description: Guides PMM manual testing by reading a Jira ticket, syncing local repos, finding pmm-submodules build artifacts, analyzing flaky FB Tests for test insights, choosing pmm3-aws-staging-start or pmm3-deploy-services, determining all Jenkins parameters, generating a fully pre-filled parambuild URL, screenshotting FB test results with playwright-cli into Jira FB test screenshots field, and writing verified test steps. Use when the user asks for help with manual testing, PMM staging setup, FB test analysis, or wants a Jenkins job link to test a Jira ticket. +name: pmm-jenkins-staging +description: Local PMM manual testing via Jenkins parambuild URLs — choose pmm3-aws-staging-start vs pmm3-deploy-services, derive all job parameters from ticket and FB build, open pre-filled parambuild URL. Use for local/staging Jenkins workflows on a developer PC, not cloud MicroVM provisioning. --- # PMM Manual Test @@ -92,6 +92,19 @@ Then `git fetch` / `git pull --ff-only` in each repo that exists. Ask if missing. Proceed once you have the key (e.g. `PMM-14915`). +## Jira writes — comment visibility (mandatory) + +Any time you post a **Jira comment** (QA results, triage notes, blockers), restrict it to the **Developers** role on `perconadev.atlassian.net`. **Never** call `jira_add_comment` or `addCommentToJiraIssue` without visibility — the default is public. + +| MCP | Tool | Pass | +|-----|------|------| +| `user-mcp-atlassian` | `jira_add_comment` | `visibility: "{\"type\":\"role\",\"value\":\"Developers\"}"` | +| `plugin-atlassian-atlassian` | `addCommentToJiraIssue` | `commentVisibility: {"type": "role", "value": "Developers"}` | + +Full examples: `.cursor/skills/pmm-fb-tests/references/fb-tests.md` § Comment visibility. If visibility cannot be set programmatically, stop and ask the user to post with **Restrict to → Developers**. + +Custom-field updates (`jira_update_issue` on `customfield_10083` / `customfield_10492`) are separate — they do not use comment visibility. + ## Step 2: Read Jira ticket Use **Atlassian MCP** (`jira_get_issue`, `jira_get_issue_development_info`): @@ -189,7 +202,7 @@ gh pr checks -R Percona-Lab/pmm-submodules 3. **Relevant** failures → add explicit checks to manual test plan (step 11) and Jenkins params (step 8) if setup-related 4. **Flaky/out of scope** → note in FB test screenshots field, do not expand manual scope -Full reference: [fb-tests.md](fb-tests.md) +Full reference: `.cursor/skills/pmm-fb-tests/references/fb-tests.md` ## Step 7: Choose Jenkins job @@ -355,9 +368,11 @@ playwright-cli close Jira fields: `customfield_10492` (FB test screenshots), `customfield_10083` (How to test). +If you also post a **comment** (not just custom fields), apply **Developers** visibility — see [Jira writes](#jira-writes--comment-visibility-mandatory) above. + **Ask the user before writing to Jira** unless they explicitly requested the update. -Details: [fb-tests.md](fb-tests.md) +Details: `.cursor/skills/pmm-fb-tests/references/fb-tests.md` ## Step 13: Open browser @@ -388,4 +403,5 @@ Start-Process "" ## Additional resources - [jenkins-jobs.md](jenkins-jobs.md) — all Jenkins parameters -- [fb-tests.md](fb-tests.md) — FB test sources, flaky guidance, screenshot & Jira templates +- `.cursor/skills/pmm-fb-tests/references/fb-tests.md` — FB test sources, flaky guidance, screenshot & Jira templates +- `.cursor/skills/pmm-provisioning/SKILL.md` — Cursor Cloud / MicroVM path (not this Jenkins skill) diff --git a/.cursor/skills/pmm-manual-test/jenkins-jobs.md b/.cursor/skills/pmm-jenkins-staging/jenkins-jobs.md similarity index 100% rename from .cursor/skills/pmm-manual-test/jenkins-jobs.md rename to .cursor/skills/pmm-jenkins-staging/jenkins-jobs.md diff --git a/.cursor/skills/pmm-jira/SKILL.md b/.cursor/skills/pmm-jira/SKILL.md new file mode 100644 index 000000000..ebbe0dafd --- /dev/null +++ b/.cursor/skills/pmm-jira/SKILL.md @@ -0,0 +1,59 @@ +--- +name: pmm-jira +description: Read and write PMM tickets on perconadev.atlassian.net — fields, transitions, Developers-only comment visibility, FB screenshot field, attachments. Use when posting Jira comments, updating How to test, attaching FB evidence, or reading ticket context for PMM QA. +--- + +# PMM Jira (perconadev.atlassian.net) + +## Read + +Use Atlassian MCP (`jira_get_issue`, `jira_get_issue_development_info`): + +| Field | ID | Notes | +|-------|-----|-------| +| How to test | `customfield_10083` | Verify against code, do not trust blindly | +| FB test screenshots | `customfield_10492` | Wiki markup + attachments | +| Development panel | — | Linked GitHub PRs | + +## Write — comments (mandatory visibility) + +**Never post QA results as public comments.** Always restrict to **Developers** role. + +`jira_add_comment` (user-mcp-atlassian): + +```json +{ + "issue_key": "PMM-XXXX", + "body": "...", + "visibility": "{\"type\":\"role\",\"value\":\"Developers\"}" +} +``` + +`addCommentToJiraIssue` (plugin-atlassian): + +```json +{ + "commentVisibility": { "type": "role", "value": "Developers" } +} +``` + +If the MCP tool cannot set visibility, **stop** and ask the human to paste with **Restrict to → Developers**. + +## Attachments + +Pass screenshot paths via `attachments` on `jira_update_issue` / issue update APIs when updating `customfield_10492`. + +Example wiki body: + +```markdown +## FB Tests — PR-4376 + +**Run:** https://github.com/Percona-Lab/pmm-submodules/actions/runs/27009345670 +**Failures:** @rta (flaky, out of scope) + +!fb-test-PMM-14915-checks.png|width=900! +``` + +## Ask before writing + +Unless the user explicitly requested the Jira update, confirm before writing to production tickets. diff --git a/.cursor/skills/pmm-provisioning/SKILL.md b/.cursor/skills/pmm-provisioning/SKILL.md new file mode 100644 index 000000000..07ef19fe3 --- /dev/null +++ b/.cursor/skills/pmm-provisioning/SKILL.md @@ -0,0 +1,89 @@ +--- +name: pmm-provisioning +description: Provision PMM Server and monitored databases on the Cursor Cloud MicroVM — docker network pmm-qa, watchtower, readyz, qa-integration bash pmm-framework. Use when setting up PMM for manual QA or reproducing FB test environment on the agent VM. +--- + +# PMM provisioning (Cursor Cloud / MicroVM) + +Uses the **same** bash `qa-integration/pmm_qa/pmm-framework/pmm-framework` as Jenkins/EC2 — no wrapper scripts, no forked playbooks. + +Resolve repo root: + +```bash +QA_ROOT="${PWD}" +[ -d pmm-qa ] && QA_ROOT="${PWD}/pmm-qa" +FRAMEWORK="${QA_ROOT}/qa-integration/pmm_qa/pmm-framework/pmm-framework" +``` + +## Runbook + +Read [references/MANUAL-QA-MICROVM.md](references/MANUAL-QA-MICROVM.md). Setup catalogue: [references/SETUP-INVENTORY.md](references/SETUP-INVENTORY.md). + +## Environment + +Docker, virtenv, and Ansible collection bootstrap run from `.cursor/environment.json` (`install` + `start`). + +## Server (MicroVM) + +```bash +export DOCKER_VERSION=... # from FB JNKPercona comment +export WATCHTOWER_VERSION=... # optional +export CLIENT_VERSION='...' # client tarball URL (for databases step) +export ADMIN_PASSWORD='pmm3admin!' +export DOCKER_ENV_VARIABLE='-e PMM_DEBUG=1 -e PMM_ENABLE_TELEMETRY=0' # override per ticket +``` + +Provision PMM Server (see [MANUAL-QA-MICROVM.md](references/MANUAL-QA-MICROVM.md) for full commands): + +```bash +docker network create pmm-qa 2>/dev/null || true +docker volume create pmm-data 2>/dev/null || true +mkdir -m 777 -p /tmp/backup_data +docker pull "$DOCKER_VERSION" +docker rm -f pmm-server watchtower 2>/dev/null || true +docker run -d --restart=always --name pmm-server --hostname pmm-server \ + --network pmm-qa -p 443:8443 -p 4647:4647 -v pmm-data:/srv \ + -e "GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD}" \ + $DOCKER_ENV_VARIABLE \ + "$DOCKER_VERSION" +until [ "$(curl -ksS -o /tmp/rz -w '%{http_code}' https://127.0.0.1/v1/server/readyz)" = "200" ] \ + && [ "$(tr -d '[:space:]' \ + --verbose +``` + +Pick `--database` from ticket + `qa-integration/pmm_qa/pmm-framework/lib/config.sh` or `./pmm-framework --help`. + +## FB workflow reproduction (Test Healer) + +Follow `pmm-qa/.github/workflows/runner-e2e-tests-codeceptjs.yml`, `runner-e2e-tests-playwright.yml`, or `runner-integration-cli-tests.yml` — not Jenkins staging. + +## UI login (optional) + +```bash +"${QA_ROOT}/.cursor/scripts/pmm-ui-login.sh" PMM- +``` + +## Cleanup + +```bash +docker rm -f pmm-server watchtower 2>/dev/null || true +docker ps -aq | xargs -r docker rm -f +docker volume rm pmm-data 2>/dev/null || true +docker network rm pmm-qa 2>/dev/null || true +``` + +## Known MicroVM limits + +Some playbooks use `antmelekhin/docker-systemd` images that exit immediately on Cursor MicroVM Docker. If a setup fails with "container is not running", report **BLOCKED** — fixing that belongs in `qa-integration/` as a general playbook change (separate PR), not MicroVM-specific forks. diff --git a/.cursor/skills/pmm-provisioning/references/MANUAL-QA-MICROVM.md b/.cursor/skills/pmm-provisioning/references/MANUAL-QA-MICROVM.md new file mode 100644 index 000000000..aa7b27731 --- /dev/null +++ b/.cursor/skills/pmm-provisioning/references/MANUAL-QA-MICROVM.md @@ -0,0 +1,107 @@ +# MicroVM manual QA — agent reference + +Condensed runbook for the PMM manual QA cloud agent on Cursor MicroVM. + +**Environment bootstrap** (Docker, virtenv, Ansible) is in `.cursor/environment.json`. +**Database provisioning** uses `qa-integration/pmm_qa/pmm-framework/pmm-framework` directly — same entrypoint as Jenkins/EC2. + +## 1. Server (always first) + +```bash +QA_ROOT="${PWD}"; [ -d pmm-qa ] && QA_ROOT="${PWD}/pmm-qa" + +export DOCKER_VERSION=... # FB: Server docker +export WATCHTOWER_VERSION=... # FB: Watchtower docker (optional) +export CLIENT_VERSION='...' # FB: Client tarball URL (for step 2) +export ADMIN_PASSWORD='pmm3admin!' +export DOCKER_ENV_VARIABLE='-e PMM_DEBUG=1 -e PMM_ENABLE_TELEMETRY=0' # override per ticket +``` + +Reset stack (when needed): + +```bash +docker rm -f pmm-server watchtower 2>/dev/null || true +docker volume rm pmm-data 2>/dev/null || true +``` + +Provision: + +```bash +docker network create pmm-qa 2>/dev/null || true +docker volume create pmm-data 2>/dev/null || true +mkdir -m 777 -p /tmp/backup_data +docker pull "$DOCKER_VERSION" + +docker rm -f pmm-server 2>/dev/null || true +docker run -d --restart=always --name pmm-server --hostname pmm-server \ + --network pmm-qa -p 443:8443 -p 4647:4647 -v pmm-data:/srv \ + -e "GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD}" \ + ${DOCKER_ENV_VARIABLE:-} \ + "$DOCKER_VERSION" +``` + +Wait for **readyz:** `https://127.0.0.1/v1/server/readyz` → HTTP **200**, body **`{}`** + +```bash +until code=$(curl -ksS -o /tmp/pmm-readyz-body.txt -w '%{http_code}' https://127.0.0.1/v1/server/readyz) \ + && [ "$code" = "200" ] && [ "$(tr -d '[:space:]' \ + --verbose +``` + +Pick `--database` from test plan + `qa-integration/pmm_qa/scripts/database_options.py`. Examples: + +| Ticket scope | `--database` | +|--------------|----------------| +| MongoDB backup/PBM | `psmdb,SETUP_TYPE=pss` | +| MySQL GR | `ps,SETUP_TYPE=gr` | +| PostgreSQL | `pgsql` | +| PSMDB sharded | `psmdb,SETUP_TYPE=sharding` | + +**Never** use `mlaunch_psmdb` for backup/PBM tickets (no PBM). + +## 3. UI (computer use) + +Use cloud agent **computer use** for PMM UI, or: + +```bash +"$QA_ROOT/.cursor/scripts/pmm-ui-login.sh" PMM- +``` + +## 4. Reset + +```bash +docker rm -f pmm-server watchtower 2>/dev/null || true +docker ps -aq | xargs -r docker rm -f +docker volume rm pmm-data 2>/dev/null || true +docker network rm pmm-qa 2>/dev/null || true +sudo rm -rf /tmp/backup_data /tmp/minio 2>/dev/null || true +``` + +## Escalation + +If pmm-framework fails with "container is not running" right after `docker run`, the playbook likely uses `antmelekhin/docker-systemd` which does not stay up on MicroVM. Report **BLOCKED** — do not add MicroVM forks in this agent PR; fix belongs upstream in `qa-integration/`. + +## What lives where + +| Location | Purpose | +|----------|---------| +| `.cursor/environment.json` | Docker start, virtenv, Ansible collection | +| `qa-integration/pmm_qa/pmm-framework/pmm-framework` | database provisioning (unchanged from main) | +| `.cursor/scripts/pmm-ui-login.sh` | optional Playwright UI session | diff --git a/.cursor/skills/pmm-provisioning/references/SETUP-INVENTORY.md b/.cursor/skills/pmm-provisioning/references/SETUP-INVENTORY.md new file mode 100644 index 000000000..48c9f88ec --- /dev/null +++ b/.cursor/skills/pmm-provisioning/references/SETUP-INVENTORY.md @@ -0,0 +1,55 @@ +# pmm-framework — setup inventory + +Every database type and meaningful variant exposed via `--database`. +Format: `pmm-framework --database [,SETUP_TYPE=][,