diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 55bec68b..a83f7d65 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -166,6 +166,23 @@ on: required: false default: "" + # Dev Catalog publishing + Argo deployment (for Cloud E2E) + run-publish-to-dev-catalog: + description: | + Whether to publish the plugin to the internal dev Plugin Catalog after a successful build. + Only runs on pushes to the main branch. Intended for use with Cloud E2E tests. + type: boolean + required: false + default: false + run-trigger-argo-workflow: + description: | + Whether to trigger an Argo workflow after publishing to the dev catalog. + Only runs on pushes to the main branch. Requires run-publish-to-dev-catalog to also be true + for the published version to be available when the Argo workflow runs. + type: boolean + required: false + default: false + # Trufflehog run-trufflehog: description: Whether to run Trufflehog secrets scanning. @@ -708,6 +725,9 @@ jobs: playwright-grafana-url: ${{ inputs.playwright-grafana-url }} playwright-secrets: ${{ inputs.playwright-secrets }} + run-publish-to-dev-catalog: ${{ inputs.run-publish-to-dev-catalog }} + run-trigger-argo-workflow: ${{ inputs.run-trigger-argo-workflow }} + run-trufflehog: ${{ inputs.run-trufflehog }} trufflehog-version: ${{ inputs.trufflehog-version }} trufflehog-include-detectors: ${{ inputs.trufflehog-include-detectors }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc94b67d..068ac72f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,6 +142,22 @@ on: required: false default: "" + # Dev Catalog publishing + Argo deployment (for Cloud E2E) + run-publish-to-dev-catalog: + description: | + Whether to publish the plugin to the internal dev Plugin Catalog after a successful build. + Only runs on pushes to the main branch. Intended for use with Cloud E2E tests. + type: boolean + required: false + default: false + run-trigger-argo-workflow: + description: | + Whether to trigger an Argo workflow after publishing to the dev catalog. + Only runs on pushes to the main branch. Requires run-publish-to-dev-catalog to also be true + for the published version to be available when the Argo workflow runs. + type: boolean + required: false + default: false # Trufflehog run-trufflehog: description: Whether to run Trufflehog secrets scanning. @@ -845,3 +861,73 @@ jobs: GCS_UPLOADED_FILES_COMMIT: ${{ steps.gcs-upload-commit.outputs.uploaded }} UNIVERSAL_ZIP: ${{ needs.test-and-build.outputs.universal-zip }} shell: bash + + publish-to-dev-catalog: + name: Publish to Dev Catalog + # Main-only: publish the latest build to the internal Plugin Catalog (dev), for Cloud E2E + if: ${{ inputs.run-publish-to-dev-catalog == true && github.ref == 'refs/heads/main' }} + needs: + - test-and-build + uses: grafana/plugin-ci-workflows/.github/workflows/cd.yml@main + permissions: + attestations: write + contents: write # Required to publish artifacts + id-token: write # OIDC for Vault / signing + with: + # This is a "publish latest" flow, not a full release + disable-docs-publishing: true + disable-github-release: true + + # Target the dev catalog environment + environment: "dev" + + # Keep toolchain consistent with the CI job above + go-version: ${{ inputs.go-version }} + golangci-lint-version: ${{ inputs.golangci-lint-version }} + plugin-directory: ${{ inputs.plugin-directory }} + + # Suffix artifact with commit SHA for traceability and uniqueness + plugin-version-suffix: ${{ github.sha }} + + # Avoid re-running Playwright here (the CI job above already covers E2E) + run-playwright: false + + # Restrict publishing scope to the Cloud E2E instance + scopes: grafana_cloud_instance_datasourcese2e + + trigger-argo-workflow: + name: Trigger Argo Workflow + runs-on: ubuntu-24.04 + timeout-minutes: 10 + + # Main-only: trigger downstream deployment after the dev catalog publish succeeds + if: ${{ inputs.run-trigger-argo-workflow == true && github.ref == 'refs/heads/main' }} + + needs: + - test-and-build + - publish-to-dev-catalog + + permissions: + contents: read + id-token: write # Required by trigger-argo-workflow action for OIDC + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Trigger workflow + uses: grafana/shared-workflows/actions/trigger-argo-workflow@b513eb1dfd9becfa671a41e55063cdd5c0a08031 # trigger-argo-workflow/v1.2.2 + with: + instance: ops + namespace: grafana-datasources-cd + + # Use plugin ID as the WorkflowTemplate name in Argo + workflow_template: ${{ fromJSON(needs.test-and-build.outputs.plugin).id }} + + # dockertag matches the version published by publish-to-dev-catalog above + parameters: | + dockertag=${{ fromJSON(needs.test-and-build.outputs.plugin).version }}+${{ github.sha }} + prCommentContext=triggered-by-push-to-main datasource=${{ fromJSON(needs.test-and-build.outputs.plugin).id }} + commit_author=grafana-delivery-bot diff --git a/.github/workflows/playwright-cloud.yml b/.github/workflows/playwright-cloud.yml new file mode 100644 index 00000000..e9a95b20 --- /dev/null +++ b/.github/workflows/playwright-cloud.yml @@ -0,0 +1,233 @@ +# Description: +# Run Playwright E2E tests against a shared Grafana Cloud instance using Grafana Bench. +# Unlike the standard playwright workflow, no local Grafana container is started — tests run +# against the live Cloud instance at the provided grafana-url. +# Datasource-specific secrets are fetched from Vault and forwarded into the Bench container. + +name: Plugins - Playwright E2E tests against Grafana Cloud + +on: + workflow_call: + inputs: + pdc-network-name: + description: | + Private Data Source Connect network name. + Find this on https://datasources.grafana.net/ for the provisioned datasource. + If set, tests should enable/select PDC; if unset, tests should skip PDC config. + type: string + required: false + grafana-url: + description: | + Grafana Cloud instance URL to run tests against. + type: string + required: false + default: "https://datasourcese2e.grafana-dev.net" + repo-secrets: + description: | + Datasource-specific Vault secrets, in get-vault-secrets repo_secrets format. + Each line: ENV_VAR_NAME=vault-path:field + All listed env vars are injected into the Bench container and forwarded to Playwright. + type: string + required: false + bench-prepare-cmd: + description: Shell command Bench runs to prepare the test environment (install deps and browsers). + type: string + required: false + default: "npm ci --no-audit --fund=false; npx playwright install" + bench-execute-cmd: + description: Shell command Bench runs to execute the Playwright tests. + type: string + required: false + default: "npm run e2e" + bench-version: + description: | + Grafana Bench image version to use (e.g. 1.0.0). + Override to pin a specific version or test against a newer release. + type: string + required: false + default: "1.0.0" + grafana-readiness-timeout: + description: | + How long (in seconds) to wait for the Grafana Cloud instance to become reachable before failing. + Increase if your instance is known to be slow after maintenance windows. + type: number + required: false + default: 600 + service-version: + description: Service version label reported to Grafana Bench (e.g. rrc-fast, nightly). + type: string + required: false + default: "rrc-fast" + +defaults: + run: + shell: bash + +# Prevent duplicate runs — if a new run starts for the same ref, cancel the in-progress one. +concurrency: + group: playwright-cloud-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + # Private Data Source Connect network name + DS_PDC_NETWORK_NAME: ${{ inputs.pdc-network-name }} + + # Grafana URL for the Cloud instance under test + GRAFANA_URL: ${{ inputs.grafana-url }} + +jobs: + bench-tests: + name: Run E2E Tests with Grafana Bench + runs-on: ubuntu-24.04 + timeout-minutes: 60 + + # Never run on forks — they lack the required Vault secrets regardless of trigger + if: ${{ github.repository_owner == 'grafana' }} + + # This job needs OIDC to fetch Vault secrets via the shared action + permissions: + contents: read + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + # Avoid leaving a token in the repo checkout; prefer explicit auth for publishing steps + persist-credentials: false + + - name: Get secrets from Vault + id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@f1614b210386ac420af6807a997ac7f6d96e477a # get-vault-secrets/v1.3.1 + with: + # Grafana auth (used by @grafana/plugin-e2e) + # + Prometheus creds for Bench metrics reporting + common_secrets: | + PLAYWRIGHT_GRAFANA_PASSWORD=data-sources/e2e:grafana-pw + PLAYWRIGHT_GRAFANA_USERNAME=data-sources/e2e:grafana-username + PROMETHEUS_PASSWORD=grafana-bench:prometheus_token + PROMETHEUS_URL=grafana-bench:prometheus_url + PROMETHEUS_USER=grafana-bench:prometheus_user + + # Repo-specific backend secrets (naming and paths vary by datasource) + repo_secrets: ${{ inputs.repo-secrets }} + + # Keep secrets in step output; we'll pass them into the container explicitly + export_env: false + + - name: Export repo secrets to runner environment + # Parse the repo-secrets keys and write each secret value to GITHUB_ENV + # so the docker run step can forward them into the container via -e flags. + if: inputs.repo-secrets != '' + env: + SECRETS_JSON: ${{ steps.get-secrets.outputs.secrets }} + REPO_SECRETS_INPUT: ${{ inputs.repo-secrets }} + run: | + set -euo pipefail + while IFS= read -r line; do + key="${line%%=*}" + key="${key// /}" # trim any whitespace + [[ -z "$key" ]] && continue + value=$(printf '%s' "$SECRETS_JSON" | jq -r --arg k "$key" '.[$k]') + # Use the delimiter syntax so values containing newlines don't corrupt GITHUB_ENV + delimiter=$(openssl rand -hex 16) + printf '%s<<%s\n%s\n%s\n' "$key" "$delimiter" "$value" "$delimiter" >> "$GITHUB_ENV" + done <<< "$REPO_SECRETS_INPUT" + + - name: Wait for Grafana to be reachable + # Wait until the Cloud instance is responding before starting tests + uses: grafana/plugin-actions/wait-for-grafana@c8ad89b7d81f8cb9967bb65e444d85f5b3d7c674 # wait-for-grafana/v1.0.2 + with: + # Use /login so "reachable" also implies the app is up + url: "${{ env.GRAFANA_URL }}/login" + timeout: ${{ inputs.grafana-readiness-timeout }} + interval: 10 # 10 seconds + + - name: Run Grafana Bench tests + env: + # Grafana admin credentials used by @grafana/plugin-e2e auth/storage state + GRAFANA_ADMIN_PASSWORD: ${{ fromJSON(steps.get-secrets.outputs.secrets).PLAYWRIGHT_GRAFANA_PASSWORD }} + GRAFANA_ADMIN_USER: ${{ fromJSON(steps.get-secrets.outputs.secrets).PLAYWRIGHT_GRAFANA_USERNAME }} + + # Bench Prometheus reporting credentials (for metrics + strict lint) + PROMETHEUS_PASSWORD: ${{ fromJSON(steps.get-secrets.outputs.secrets).PROMETHEUS_PASSWORD }} + PROMETHEUS_URL: ${{ fromJSON(steps.get-secrets.outputs.secrets).PROMETHEUS_URL }} + PROMETHEUS_USER: ${{ fromJSON(steps.get-secrets.outputs.secrets).PROMETHEUS_USER }} + + # PDC network name comes from the workflow-level env above + DS_PDC_NETWORK_NAME: ${{ env.DS_PDC_NETWORK_NAME }} + + # Bench command inputs passed via env to avoid template-injection in the run script + BENCH_IMAGE: us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench-playwright:v${{ inputs.bench-version }} + BENCH_PREPARE_CMD: ${{ inputs.bench-prepare-cmd }} + BENCH_EXECUTE_CMD: ${{ inputs.bench-execute-cmd }} + BENCH_SERVICE_VERSION: ${{ inputs.service-version }} + BENCH_SUITE_NAME: ${{ github.repository }}/e2e + + # repo-secrets keys for dynamic flag generation; values are already in GITHUB_ENV + REPO_SECRETS_INPUT: ${{ inputs.repo-secrets }} + run: | + set -euo pipefail + + # Build dynamic -e and --test-env flags from the repo-secrets input keys. + # Values were exported to GITHUB_ENV in the previous step, so docker inherits them. + REPO_SECRET_DOCKER_FLAGS="" + REPO_SECRET_TEST_ENV_FLAGS="" + if [[ -n "${REPO_SECRETS_INPUT:-}" ]]; then + while IFS= read -r line; do + key="${line%%=*}" + key="${key// /}" + [[ -z "$key" ]] && continue + REPO_SECRET_DOCKER_FLAGS+=" -e $key" + REPO_SECRET_TEST_ENV_FLAGS+=" --test-env $key" + done <<< "$REPO_SECRETS_INPUT" + fi + + # Run Bench in a container. Bench bootstraps Playwright deps (prepare) + # and executes your repo's e2e script (execute) with standardized reporting. + # + # Flags grouped by purpose: + # Container setup : --network, --rm, --volume + # Backend secrets : dynamic $REPO_SECRET_DOCKER_FLAGS (from repo-secrets input) + # Grafana auth : -e GRAFANA_ADMIN_*, -e GRAFANA_URL + # Bench Prometheus : -e PROMETHEUS_* + # PDC : -e DS_PDC_NETWORK_NAME + # Bench reporting : --report-output, --run-stage, --service-*, --suite-* + # Test env : --test-env (forwarded into the Playwright process) + # Runner : --test-runner, --test-verbose + # shellcheck disable=SC2086 + docker run \ + --network=host \ + --rm \ + --volume "$PWD:/tests" \ + $REPO_SECRET_DOCKER_FLAGS \ + -e DS_PDC_NETWORK_NAME \ + -e GRAFANA_ADMIN_PASSWORD \ + -e GRAFANA_ADMIN_USER \ + -e GRAFANA_URL \ + -e PROMETHEUS_PASSWORD \ + -e PROMETHEUS_URL \ + -e PROMETHEUS_USER \ + us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench-playwright:v1.0.0 test \ + --prometheus-metrics \ + --prometheus-strict-lint \ + --pw-prepare "$BENCH_PREPARE_CMD" \ + --pw-execute "$BENCH_EXECUTE_CMD" \ + --report-output log \ + --run-stage ci \ + --service ${GITHUB_REPOSITORY#grafana/} \ + --service-url ${GRAFANA_URL} \ + --service-version "$BENCH_SERVICE_VERSION" \ + --suite-name "$BENCH_SUITE_NAME" \ + --suite-path /tests \ + --test-env CI=true \ + $REPO_SECRET_TEST_ENV_FLAGS \ + --test-env DS_PDC_NETWORK_NAME \ + --test-env GRAFANA_ADMIN_PASSWORD \ + --test-env GRAFANA_ADMIN_USER \ + --test-env GRAFANA_URL \ + --test-runner playwright \ + --test-verbose diff --git a/.github/workflows/pr-checks-workflow-references.yml b/.github/workflows/pr-checks-workflow-references.yml index 9a6fff8f..d96dbea3 100644 --- a/.github/workflows/pr-checks-workflow-references.yml +++ b/.github/workflows/pr-checks-workflow-references.yml @@ -34,6 +34,7 @@ jobs: .github/workflows/ci.yml .github/workflows/cd.yml .github/workflows/playwright.yml + .github/workflows/playwright-cloud.yml .github/workflows/playwright-docker.yml .github/workflows/check-release-channel.yml actions/plugins/** diff --git a/.github/workflows/release-please-pr-update-tagged-references.yml b/.github/workflows/release-please-pr-update-tagged-references.yml index 38a46098..f5e8118b 100644 --- a/.github/workflows/release-please-pr-update-tagged-references.yml +++ b/.github/workflows/release-please-pr-update-tagged-references.yml @@ -132,6 +132,7 @@ jobs: .github/workflows/ci.yml .github/workflows/cd.yml .github/workflows/playwright.yml + .github/workflows/playwright-cloud.yml .github/workflows/playwright-docker.yml .github/workflows/check-release-channel.yml actions/plugins/** diff --git a/.github/workflows/release-please-restore-rolling-release.yml b/.github/workflows/release-please-restore-rolling-release.yml index 31e91880..ba710aaf 100644 --- a/.github/workflows/release-please-restore-rolling-release.yml +++ b/.github/workflows/release-please-restore-rolling-release.yml @@ -75,6 +75,7 @@ jobs: .github/workflows/ci.yml .github/workflows/cd.yml .github/workflows/playwright.yml + .github/workflows/playwright-cloud.yml .github/workflows/playwright-docker.yml .github/workflows/check-release-channel.yml actions/plugins/** diff --git a/tests/act/main_workflow_consistency_test.go b/tests/act/main_workflow_consistency_test.go index e804aafc..25c43f82 100644 --- a/tests/act/main_workflow_consistency_test.go +++ b/tests/act/main_workflow_consistency_test.go @@ -24,6 +24,7 @@ var knownWorkflows = []workflowEntry{ {path: "ci.yml", internal: false}, {path: "cd.yml", internal: false}, {path: "playwright.yml", internal: false}, + {path: "playwright-cloud.yml", internal: false}, {path: "playwright-docker.yml", internal: false}, {path: "check-release-channel.yml", internal: false},