diff --git a/.github/actions/setup-enterprise/action.yml b/.github/actions/setup-enterprise/action.yml new file mode 100644 index 00000000000..37c09911fc8 --- /dev/null +++ b/.github/actions/setup-enterprise/action.yml @@ -0,0 +1,48 @@ +name: 'Setup Grafana Enterprise' +description: 'Clones and sets up Grafana Enterprise repository for testing' + +inputs: + github-app-name: + description: 'Name of the GitHub App in Vault' + required: false + default: 'grafana-ci-bot' + +runs: + using: "composite" + steps: + - name: Retrieve GitHub App secrets + id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 # zizmor: ignore[unpinned-uses] + with: + repo_secrets: | + APP_ID=${{ inputs.github-app-name }}:app-id + APP_INSTALLATION_ID=${{ inputs.github-app-name }}:app-installation-id + PRIVATE_KEY=${{ inputs.github-app-name }}:private-key + + - name: Generate GitHub App token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ env.APP_ID }} + private-key: ${{ env.PRIVATE_KEY }} + repositories: "grafana-enterprise" + owner: "grafana" + + - name: Setup Enterprise + shell: bash + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + run: | + git clone https://x-access-token:${GH_TOKEN}@github.com/grafana/grafana-enterprise.git ../grafana-enterprise; + + cd ../grafana-enterprise + + if git checkout ${GITHUB_HEAD_REF}; then + echo "checked out ${GITHUB_HEAD_REF}" + elif git checkout ${GITHUB_BASE_REF}; then + echo "checked out ${GITHUB_BASE_REF}" + else + git checkout main + fi + + ./build.sh diff --git a/.github/actions/setup-grafana-bench/action.yml b/.github/actions/setup-grafana-bench/action.yml new file mode 100644 index 00000000000..f30cc37221d --- /dev/null +++ b/.github/actions/setup-grafana-bench/action.yml @@ -0,0 +1,45 @@ +name: 'Setup Grafana Bench' +description: 'Sets up and installs Grafana Bench' + +inputs: + github-app-name: + description: 'Name of the GitHub App in Vault' + required: false + default: 'grafana-ci-bot' + branch: + description: 'The branch to install from' + required: false + default: 'main' + +runs: + using: "composite" + steps: + - name: Retrieve GitHub App secrets + id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 # zizmor: ignore[unpinned-uses] + with: + repo_secrets: | + APP_ID=${{ inputs.github-app-name }}:app-id + APP_INSTALLATION_ID=${{ inputs.github-app-name }}:app-installation-id + PRIVATE_KEY=${{ inputs.github-app-name }}:private-key + + - name: Generate GitHub App token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ env.APP_ID }} + private-key: ${{ env.PRIVATE_KEY }} + repositories: "grafana-bench" + owner: "grafana" + + - name: Setup Bench + shell: bash + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + BRANCH: ${{ inputs.branch }} + run: | + git clone https://x-access-token:${GH_TOKEN}@github.com/grafana/grafana-bench.git ../grafana-bench + + cd ../grafana-bench + git switch "$BRANCH" + go install . diff --git a/.github/actions/test-coverage-processor/action.yml b/.github/actions/test-coverage-processor/action.yml new file mode 100644 index 00000000000..bd2458020c3 --- /dev/null +++ b/.github/actions/test-coverage-processor/action.yml @@ -0,0 +1,50 @@ +name: 'Go Coverage Processor' +description: 'Process Go test coverage files and generate reports' + +inputs: + test-type: + description: 'Type of test (e.g., be-unit, be-integration)' + required: true + type: string + coverage-file: + description: 'Path to the Go coverage file (.cov)' + required: true + type: string + codecov-token: + description: 'Token for CodeCov (required for CodeCov reporting)' + required: false + default: '' + codecov-flag: + description: 'Flag to categorize the upload to CodeCov' + required: false + default: '' + codecov-name: + description: 'Custom name for the upload to CodeCov' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Process Go coverage output + shell: bash + env: + COVERAGE_FILE: ${{ inputs.coverage-file }} + run: | + # Ensure valid coverage file even if empty + if [ ! -s "$COVERAGE_FILE" ]; then + echo "Coverage file is empty, creating a minimal valid file" + echo "mode: set" > "$COVERAGE_FILE" + fi + + - name: Report coverage to CodeCov + uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5 + if: inputs.codecov-token != '' + with: + files: ${{ inputs.coverage-file }} + flags: ${{ inputs.codecov-flag || inputs.test-type }} + name: ${{ inputs.codecov-name || inputs.test-type }} + slug: grafana/grafana + # This URL doesn't use the Google auth, but is much more locked down. As such, it requires OIDC or a CodeCov-provided token to do anything. + url: https://codecov-webhook.grafana-dev.net + token: ${{ inputs.codecov-token }} diff --git a/.github/workflows/actions/changelog/index.js b/.github/workflows/actions/changelog/index.js index 48da9dcf33a..b658caf349c 100644 --- a/.github/workflows/actions/changelog/index.js +++ b/.github/workflows/actions/changelog/index.js @@ -69,8 +69,17 @@ const graphql = async (ghtoken, query, variables) => { }, body: JSON.stringify({ query, variables }), }); - const { data } = await results.json(); - return data; + + const res = await results.json(); + + LOG( + JSON.stringify({ + status: results.status, + text: results.statusText, + }) + ); + + return res.data; }; // Using Github GraphQL API find the timestamp for the given tag/commit hash. @@ -99,20 +108,20 @@ const getCommitishDate = async (name, owner, target) => { // Using Github GraphQL API get a list of PRs between the two "commitish" items. // This resoves the "since" item's timestamp first and iterates over all PRs // till "target" using naïve pagination. -const getHistory = async (name, owner, target, sinceDate) => { - LOG(`Fetching ${owner}/${name} PRs since ${sinceDate} till ${target}`); +const getHistory = async (name, owner, from, to) => { + LOG(`Fetching ${owner}/${name} PRs between ${from} and ${to}`); const query = ` query findCommitsWithAssociatedPullRequests( $name: String! $owner: String! - $target: String! - $sinceDate: GitTimestamp + $from: String! + $to: String! $cursor: String ) { repository(name: $name, owner: $owner) { - object(expression: $target) { - ... on Commit { - history(first: 50, since: $sinceDate, after: $cursor) { + ref(qualifiedName: $from) { + compare(headRef: $to) { + commits(first: 25, after: $cursor) { totalCount pageInfo { hasNextPage @@ -155,13 +164,13 @@ const getHistory = async (name, owner, target, sinceDate) => { const result = await graphql(ghtoken, query, { name, owner, - target, - sinceDate, + from, + to, cursor, }); LOG(`GraphQL: ${JSON.stringify(result)}`); - nodes = [...nodes, ...result.repository.object.history.nodes]; - const { hasNextPage, endCursor } = result.repository.object.history.pageInfo; + nodes = [...nodes, ...result.repository.ref.compare.commits.nodes]; + const { hasNextPage, endCursor } = result.repository.ref.compare.commits.pageInfo; if (!hasNextPage) { break; } @@ -175,11 +184,11 @@ const getHistory = async (name, owner, target, sinceDate) => { // feature, deprecation, breaking change and plugin fixes/enhancements). // // PR grouping relies on Github labels only, not on the PR contents. -const getChangeLogItems = async (name, owner, sinceDate, to) => { +const getChangeLogItems = async (name, owner, from, to) => { // check if a node contains a certain label const hasLabel = ({ labels }, label) => labels.nodes.some(({ name }) => name === label); // get all the PRs between the two "commitish" items - const history = await getHistory(name, owner, to, sinceDate); + const history = await getHistory(name, owner, from, to); const items = history.flatMap((node) => { // discard PRs without a "changelog" label @@ -231,13 +240,10 @@ const previous = process.argv[3] || process.env.INPUT_PREVIOUS || (await getPrev LOG(`Previous tag/commit: ${previous}`); -const sinceDate = await getCommitishDate('grafana', 'grafana', previous); -LOG(`Previous tag/commit timestamp: ${sinceDate}`); - // Get all changelog items from Grafana OSS -const oss = await getChangeLogItems('grafana', 'grafana', sinceDate, target); +const oss = await getChangeLogItems('grafana', 'grafana', previous, target); // Get all changelog items from Grafana Enterprise -const entr = await getChangeLogItems('grafana-enterprise', 'grafana', sinceDate, target); +const entr = await getChangeLogItems('grafana-enterprise', 'grafana', previous, target); LOG(`Found OSS PRs: ${oss.length}`); LOG(`Found Enterprise PRs: ${entr.length}`); diff --git a/.github/workflows/add-to-whats-new.yml b/.github/workflows/add-to-whats-new.yml new file mode 100644 index 00000000000..900ab9615bd --- /dev/null +++ b/.github/workflows/add-to-whats-new.yml @@ -0,0 +1,16 @@ +name: Add comment about adding a What's new note +on: + pull_request: + types: [labeled] + +jobs: + add-comment: + if: ${{ ! github.event.pull_request.head.repo.fork && contains(github.event.pull_request.labels.*.name, 'add to what''s new') }} + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1 + with: + message: | + Since you've added the `Add to what's new` label, consider drafting a [What's new note](https://admin.grafana.com/content-admin/#/collections/whats-new/new) for this feature. diff --git a/.github/workflows/alerting-swagger-gen.yml b/.github/workflows/alerting-swagger-gen.yml index 7c3af87837f..2526d924d5e 100644 --- a/.github/workflows/alerting-swagger-gen.yml +++ b/.github/workflows/alerting-swagger-gen.yml @@ -10,18 +10,19 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 2 + persist-credentials: false - name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: go-version-file: go.mod - name: Build swagger run: | make -C pkg/services/ngalert/api/tooling post.json api.json - name: Open Pull Request - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@4e1beaa7521e8b457b572c090b25bd3db56bf1c5 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "chore: update alerting swagger spec" @@ -34,4 +35,3 @@ jobs: labels: 'area/alerting,type/docs,no-changelog' team-reviewers: 'grafana/alerting-backend' draft: false - diff --git a/.github/workflows/alerting-update-module.yml b/.github/workflows/alerting-update-module.yml new file mode 100644 index 00000000000..d1646d21718 --- /dev/null +++ b/.github/workflows/alerting-update-module.yml @@ -0,0 +1,137 @@ +name: Update Alerting Module + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + update-grafana: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + with: + persist-credentials: false + - name: Check if update branch exists + run: | + if git ls-remote --heads origin update-alerting-module | grep -q 'update-alerting-module'; then + echo "Branch 'update-alerting-module' already exists. There might be an open PR with Grafana updates." + echo "Please review and merge/close the existing PR before running this workflow again." + exit 1 + fi + + - name: Setup Go + uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # 5.3.0 + with: + "go-version-file": "go.mod" + + - name: Extract current commit hash of alerting module + id: current-commit + run: | + FROM_COMMIT=$(go list -m -json github.com/grafana/alerting | jq -r '.Version' | grep -oP '(?<=-)[a-f0-9]+$') + echo "from_commit=$FROM_COMMIT" >> $GITHUB_OUTPUT + + - name: Get current branch name + id: current-branch-name + run: echo "name=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> "$GITHUB_OUTPUT" + + - name: Get latest commit + id: latest-commit + env: + GH_TOKEN: ${{ github.token }} + run: | + BRANCH="${{ steps.current-branch-name.outputs.name }}" + TO_COMMIT=$(gh api repos/grafana/alerting/commits/$BRANCH --jq '.sha') + if [ -z "$TO_COMMIT" ]; then + echo "Branch $BRANCH not found in alerting repo, falling back to main branch" + exit 1 + fi + echo "to_commit=$TO_COMMIT" >> $GITHUB_OUTPUT + + - name: Compare commit hashes + run: | + FROM_COMMIT="${{ steps.current-commit.outputs.from_commit }}" + TO_COMMIT="${{ steps.latest-commit.outputs.to_commit }}" + + # Compare just the length of the shorter hash + SHORT_TO_COMMIT="${TO_COMMIT:0:${#FROM_COMMIT}}" + + if [ "$FROM_COMMIT" = "$SHORT_TO_COMMIT" ]; then + echo "Current version ($FROM_COMMIT) is already at latest ($SHORT_TO_COMMIT). No update needed." + exit 0 + fi + echo "Updates available: $FROM_COMMIT -> $TO_COMMIT" + + - name: Check for commit history + id: check-commits + env: + GH_TOKEN: ${{ github.token }} + run: | + # get all commits that contains 'Alerting:' in the message + ALERTING_COMMITS=$(gh api repos/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }} \ + --jq '.commits[].commit.message | split("\n")[0]') || true + + # Use printf instead of echo -e for better multiline handling + printf "%s\n" "$ALERTING_COMMITS" + + # make the list for markdown and replace PR numbers with links + ALERTING_COMMITS_FORMATTED=$(echo "$ALERTING_COMMITS" | while read -r line; do echo "- $line" | sed -E 's/\(#([0-9]+)\)/[#\1](https:\/\/github.com\/grafana\/grafana\/pull\/\1)/g'; done) + + echo "alerting_commits<> $GITHUB_OUTPUT + echo "$ALERTING_COMMITS_FORMATTED" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Update alerting module + env: + GOSUMDB: off + run: | + go get github.com/grafana/alerting@${{ steps.latest-commit.outputs.to_commit }} + make update-workspace + + - id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] + with: + repo_secrets: | + GITHUB_APP_ID=alerting-team:app-id + GITHUB_APP_PRIVATE_KEY=alerting-team:private-key + + - name: "Generate token" + id: generate_token + uses: actions/create-github-app-token@0d564482f06ca65fa9e77e2510873638c82206f2 # 1.11.5 + with: + app-id: ${{ env.GITHUB_APP_ID }} + private-key: ${{ env.GITHUB_APP_PRIVATE_KEY }} + + - name: Create Pull Request + uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # 7.0.6 + id: create-pr + with: + token: '${{ steps.generate_token.outputs.token }}' + title: 'Alerting: Update alerting module to ${{ steps.latest-commit.outputs.to_commit }}' + branch: alerting/update-alerting-module + delete-branch: true + body: | + Updates Grafana Alerting module to latest version. + + Compare changes: https://github.com/grafana/alerting/compare/${{ steps.current-commit.outputs.from_commit }}...${{ steps.latest-commit.outputs.to_commit }} +
+ Commits + + ${{ steps.check-commits.outputs.alerting_commits }} + +
+ + Created by: [GitHub Action Job](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) + - name: Add PR URL to Summary + if: steps.create-pr.outputs.pull-request-url != '' + run: | + echo "## Pull Request Created" >> $GITHUB_STEP_SUMMARY + echo "🔗 [View Pull Request](${{ steps.create-pr.outputs.pull-request-url }})" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/analytics-events-report.yml b/.github/workflows/analytics-events-report.yml new file mode 100644 index 00000000000..9c5c6f3ef09 --- /dev/null +++ b/.github/workflows/analytics-events-report.yml @@ -0,0 +1,25 @@ +name: Analytics Events Report + +on: + workflow_dispatch: + +jobs: + generate-report: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Generate analytics report + run: yarn analytics-report diff --git a/.github/workflows/auto-milestone.yml b/.github/workflows/auto-milestone.yml index 0697888fba0..77daec36bca 100644 --- a/.github/workflows/auto-milestone.yml +++ b/.github/workflows/auto-milestone.yml @@ -21,7 +21,7 @@ jobs: # Note: Github will not trigger other actions from this because it uses # the GITHUB_TOKEN token - name: Run auto-milestone - uses: grafana/grafana-github-actions-go/auto-milestone@main + uses: grafana/grafana-github-actions-go/auto-milestone@d4c452f92ed826d515dccf1f62923e537953acd8 # main with: pr: ${{ github.event.pull_request.number }} token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/auto-triager/labels.txt b/.github/workflows/auto-triager/labels.txt new file mode 100644 index 00000000000..517a6ad867d --- /dev/null +++ b/.github/workflows/auto-triager/labels.txt @@ -0,0 +1,124 @@ +area/admin/user +area/alerting +area/annotations +area/auth +area/auth/ldap +area/auth/oauth +area/auth/rbac +area/auth/serviceaccount +area/backend +area/backend/api +area/backend/db +area/backend/db/migration +area/backend/db/mysql +area/backend/db/postgres +area/backend/db/sql +area/backend/db/sqlite +area/configuration +area/dashboard/annotations +area/dashboard/data-links +area/dashboard/edit +area/dashboard/folders +area/dashboard/import +area/dashboard/kiosk +area/dashboard/links +area/dashboard/rows +area/dashboard/scenes +area/dashboard/settings +area/dashboard/snapshot +area/dashboard/templating +area/dashboard/timerange +area/dashboard/tv +area/dashboard/variable +area/dashboards/panel +area/data/export +area/explore +area/expressions +area/field/overrides +area/frontend/library-panels +area/frontend/login +area/image-rendering +area/internationalization +area/legend +area/library-panel +area/metricsdrilldown +area/navigation +area/panel/annotation-list +area/panel/barchart +area/panel/bargauge +area/panel/candlestick +area/panel/canvas +area/panel/dashboard-list +area/panel/edit +area/panel/edit +area/panel/field-override +area/panel/flame-graph +area/panel/gauge +area/panel/geomap +area/panel/heatmap +area/panel/histogram +area/panel/logs +area/panel/node-graph +area/panel/node-graph +area/panel/piechart +area/panel/repeat +area/panel/singlestat +area/panel/stat +area/panel/state-timeline +area/panel/status-history +area/panel/table +area/panel/timeseries +area/panel/traceview +area/panel/trend +area/panel/xychart +area/permissions +area/playlist +area/plugins +area/plugins-catalog +area/provisioning +area/provisioning/datasources +area/public-dashboards +area/query-library +area/recorded-queries +area/scenes +area/search +area/security +area/streaming +area/templating/repeating +area/tooltip +area/transformations +datagrid +datasource/Alertmanager +datasource/Azure +datasource/azure-cosmosdb +datasource/BigQuery +datasource/CloudWatch +datasource/CloudWatch Logs +datasource/CSV +datasource/Elasticsearch +datasource/GitHub +datasource/GoogleCloudMonitoring +datasource/GoogleSheets +datasource/grafana-pyroscope +datasource/Graphite +datasource/InfluxDB +datasource/Jaeger +datasource/JSON +datasource/Loki +datasource/MSSQL +datasource/MySQL +datasource/OpenSearch +datasource/OpenTSDB +datasource/Parca +datasource/Phlare +datasource/Postgres +datasource/Prometheus +datasource/SiteWIse +datasource/Splunk +datasource/Tempo +datasource/TestDataDB +datasource/Timestream +datasource/X-Ray +datasource/Zabbix +datasource/Zipkin +team/grafana-aws-datasources diff --git a/.github/workflows/auto-triager/prompt.txt b/.github/workflows/auto-triager/prompt.txt new file mode 100644 index 00000000000..69be81328fc --- /dev/null +++ b/.github/workflows/auto-triager/prompt.txt @@ -0,0 +1,25 @@ +You are an expert Grafana issues categorizer. + +You are provided with a Grafana issue. Your task is to categorize the issue by analyzing the issue title and description to determine the most relevant category and type from the provided lists. Focus on precision and clarity, selecting only the most pertinent labels based on the issue details. Ensure that your selections reflect the core problem or functionality affected. + +The output should be a valid JSON object with the following fields: +* id (string): The ID of the current issue. +* categoryLabel (array of strings): The category labels for the current issue, emphasizing key terms and context. +* typeLabel (array of strings): The type of the current issue, emphasizing clarity and relevance. + +**Instructions**: +1. **Contextual Analysis**: Understand the context and intent behind the issue description. Analyze the overall narrative and relationships between different components within Grafana. Consider dependencies and related components to inform your decision. +2. **Category and Type Differentiation**: Use language cues and patterns to differentiate between similar categories and types. Provide examples and counterexamples to clarify distinctions. Prioritize primary components over secondary ones unless they are critical to the issue. +3. **Historical Data Utilization**: Compare current issues with past resolved issues by analyzing similarities in problem descriptions, leveraging patterns to inform categorization. Use historical data to recognize patterns and inform your decision-making. +4. **Confidence Scoring**: Implement a confidence scoring mechanism to flag issues for review if the confidence is below a predefined threshold. Clearly indicate thresholds for high and low confidence predictions. Provide clarifying questions if data is ambiguous. +5. **Feedback Loop Integration**: Integrate feedback from incorrect predictions to refine understanding and improve future predictions. Conduct error analysis to identify patterns in misclassifications and adapt your approach accordingly. +6. **Semantic Analysis**: Evaluate the underlying intent of the issue using semantic analysis, considering broader implications and context. Leverage metadata or historical patterns to improve accuracy. +7. **Avoid Over-Specification**: Maintain precision and conciseness, avoiding unnecessary details. Prioritize clarity and flag for further review if uncertain. +8. **Consistent JSON Formatting**: Ensure the output maintains a consistent JSON structure with uniform formatting for readability and scalability. + +**Next Steps and Insights**: +- Suggest potential next steps or resources that could help address the issue, providing actionable insights to enhance user engagement. +- Regularly test responses against edge cases to ensure robustness and adaptability. +- Stay updated with changes in category and type lists to remain current. + +Provide a brief explanation of the categorization decision, highlighting key terms or context that influenced the choice. Use user-centric language and technical details to ensure the explanation is comprehensive and insightful. diff --git a/.github/workflows/auto-triager/types.txt b/.github/workflows/auto-triager/types.txt new file mode 100644 index 00000000000..4aff8e3845c --- /dev/null +++ b/.github/workflows/auto-triager/types.txt @@ -0,0 +1,30 @@ +type/accessibility +type/angular-2-react +type/browser-compatibility +type/bug +type/build-packaging +type/chore +type/ci +type/cleanup +type/codegen +type/community +type/debt +type/design +type/discussion +type/docs +type/duplicate +type/e2e +type/epic +type/feature-request +type/feature-toggle-enable +type/feature-toggle-removal +type/performance +type/poc +type/project +type/proposal +type/question +type/refactor +type/regression +type/roadmap +type/tech +type/ux diff --git a/.github/workflows/backend-code-checks.yml b/.github/workflows/backend-code-checks.yml new file mode 100644 index 00000000000..b55c07aed22 --- /dev/null +++ b/.github/workflows/backend-code-checks.yml @@ -0,0 +1,74 @@ +name: Backend Code Checks +description: Validate go.mod and OpenAPI specifications + +on: + pull_request: + paths-ignore: + - '*.md' + - 'docs/**' + - 'latest.json' + push: + branches: + - main + paths-ignore: + - '*.md' + - 'docs/**' + - 'latest.json' + +permissions: + contents: read + id-token: write + +jobs: + validate-configs: + name: Validate Backend Configs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Setup Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + # Explicitly set Go version to 1.24.1 to ensure consistent OpenAPI spec generation + # The crypto/x509 package has additional fields in Go 1.24.1 that affect the generated specs + # This ensures the GHAs environment matches what we use in the Drone pipeline + go-version: 1.24.1 + cache: true + + - name: Verify code generation + run: | + CODEGEN_VERIFY=1 make gen-cue + CODEGEN_VERIFY=1 make gen-jsonnet + + - name: Validate go.mod + run: go run scripts/modowners/modowners.go check go.mod + + # Enterprise setup is needed for complete OpenAPI spec generation + # We only do this for internal PRs + - name: Setup Grafana Enterprise + if: github.event.pull_request.head.repo.fork == false + uses: ./.github/actions/setup-enterprise + + - name: Generate and Validate OpenAPI Specs + run: | + # For PRs from forks, we'll just run the basic swagger-gen without validation + if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then + echo "PR is from a fork, skipping enterprise-based validation" + make swagger-gen + exit 0 + fi + + # Clean and regenerate OpenAPI specs + make swagger-clean && make openapi3-gen + + # Check if the generated specs differ from what's in the repository + for f in public/api-merged.json public/openapi3.json; do git add $f; done + if [ -z "$(git diff --name-only --cached)" ]; then + echo "OpenAPI specs are up to date!" + else + echo "OpenAPI specs are OUT OF DATE!" + git diff --cached + echo "Please ensure the branch is up-to-date, then regenerate the specification by running make swagger-clean && make openapi3-gen" + exit 1 + fi diff --git a/.github/workflows/backend-unit-tests.yml b/.github/workflows/backend-unit-tests.yml new file mode 100644 index 00000000000..2c0c238d502 --- /dev/null +++ b/.github/workflows/backend-unit-tests.yml @@ -0,0 +1,71 @@ +name: Backend Unit Tests + +on: + pull_request: + paths-ignore: + - 'docs/**' + - '**/*.md' + push: + branches: + - main + - release-*.*.* + paths-ignore: + - 'docs/**' + - '**/*.md' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +permissions: {} + +jobs: + grafana: + # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, + # the `pr-backend-unit-tests-enterprise` workflow will run instead + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true + name: Grafana + runs-on: ubuntu-latest-8-cores + continue-on-error: true + permissions: + contents: read + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Setup Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: go.mod + - name: Generate Go code + run: make gen-go + - name: Run unit tests + run: make test-go-unit + + grafana-enterprise: + # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + name: Grafana Enterprise + runs-on: ubuntu-latest-8-cores + permissions: + contents: read + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Setup Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: go.mod + - name: Setup Enterprise + uses: ./.github/actions/setup-enterprise + with: + github-app-name: 'grafana-ci-bot' + - name: Generate Go code + run: make gen-go + - name: Run unit tests + run: make test-go-unit diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 5f21ab276ee..fc65898f652 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -5,23 +5,28 @@ on: - closed - labeled +permissions: + contents: write + pull-requests: write + jobs: main: if: github.repository == 'grafana/grafana' runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - run: git config --global user.email '132647405+grafana-delivery-bot[bot]@users.noreply.github.com' - - run: git config --global user.name 'grafana-delivery-bot[bot]' - - run: git remote set-url origin "https://grafana-delivery-bot:${{ steps.generate_token.outputs.token }}@github.com/grafana/grafana.git" + persist-credentials: false + - run: git config --local user.name "github-actions[bot]" + - run: git config --local user.email "github-actions[bot]@users.noreply.github.com" + - run: git config --local --add --bool push.autoSetupRemote true + - name: Set remote URL + env: + GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git remote set-url origin "https://grafana-delivery-bot:$GIT_TOKEN@github.com/grafana/grafana.git" - name: Run backport - uses: grafana/grafana-github-actions-go/backport@main + uses: grafana/grafana-github-actions-go/backport@main # zizmor: ignore[unpinned-uses] with: - token: ${{ steps.generate_token.outputs.token }} + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/bump-version.yml b/.github/workflows/bump-version.yml index 5f0b7cf3020..8db9fb1e5c3 100644 --- a/.github/workflows/bump-version.yml +++ b/.github/workflows/bump-version.yml @@ -11,33 +11,37 @@ on: dry_run: default: false required: false + +permissions: + contents: write + pull-requests: write + jobs: - main: + bump-version: runs-on: ubuntu-latest steps: - name: Checkout Grafana - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Update package.json versions uses: ./pkg/build/actions/bump-version with: version: ${{ inputs.version }} - - if: ${{ inputs.push }} - name: Generate token - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - if: ${{ inputs.push }} name: Push & Create PR + env: + VERSION: ${{ inputs.version }} + DRY_RUN: ${{ inputs.dry_run }} + REF_NAME: ${{ github.ref_name }} + RUN_ID: ${{ github.run_id }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git config --local user.name "github-actions[bot]" git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local --add --bool push.autoSetupRemote true - git checkout -b "bump-version/${{ github.run_id }}/${{ inputs.version }}" + git checkout -b "bump-version/${RUN_ID}/${VERSION}" git add . - git commit -m "bump version ${{ inputs.version }}" + git commit -m "bump version ${VERSION}" git push - gh pr create --dry-run=${{ inputs.dry_run }} -l "type/ci" -l "no-changelog" -B "${{ github.ref_name }}" --title "Release: Bump version to ${{ inputs.version }}" --body "Updated version to ${{ inputs.version }}" - env: - GH_TOKEN: ${{ steps.generate_token.outputs.token }} + gh pr create --dry-run=$DRY_RUN -l "type/ci" -l "no-changelog" -B "$REF_NAME" --title "Release: Bump version to ${VERSION}" --body "Updated version to ${VERSION}" diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index d23ca18e612..094279e0c9a 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -51,15 +51,20 @@ on: default: false type: boolean -permissions: - contents: write - pull-requests: write +permissions: {} jobs: main: + env: + RUN_ID: ${{ github.run_id }} + VERSION: ${{ inputs.version }} + PREVIOUS_VERISON: ${{ inputs.previous_version }} + TARGET: ${{ inputs.target }} + DRY_RUN: ${{ inputs.dry_run }} runs-on: ubuntu-latest permissions: contents: write + pull-requests: write steps: - name: "Generate token" id: generate_token @@ -68,7 +73,7 @@ jobs: app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: "Checkout Grafana repo" - uses: "actions/checkout@v4" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" with: ref: main sparse-checkout: | @@ -79,8 +84,9 @@ jobs: .prettierrc.js fetch-depth: 0 fetch-tags: true + persist-credentials: false - name: Setup nodejs environment - uses: actions/setup-node@v4 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version-file: .nvmrc - name: "Configure git user" @@ -89,7 +95,7 @@ jobs: git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local --add --bool push.autoSetupRemote true - name: "Create branch" - run: git checkout -b "changelog/${{ github.run_id }}/${{ inputs.version }}" + run: git checkout -b "changelog/${RUN_ID}/${VERSION}" - name: "Generate changelog" id: changelog uses: ./.github/workflows/actions/changelog @@ -103,24 +109,24 @@ jobs: # Prepare CHANGELOG.md content with version delimiters ( echo - echo "# ${{ inputs.version}} ($(date '+%F'))" + echo "# ${VERSION} ($(date '+%F'))" echo cat changelog_items.md ) > CHANGELOG.part # Check if a version exists in the changelog - if grep -q "" + echo "" cat CHANGELOG.part - echo "" + echo "" cat CHANGELOG.md ) > CHANGELOG.tmp mv CHANGELOG.tmp CHANGELOG.md @@ -138,11 +144,11 @@ jobs: - name: "Create changelog PR" run: > gh pr create \ - --dry-run=${{ inputs.dry_run }} \ + --dry-run=${DRY_RUN} \ --label "no-backport" \ --label "no-changelog" \ - -B "${{ inputs.target }}" \ - --title "Release: update changelog for ${{ inputs.version }}" \ - --body "Changelog changes for release ${{ inputs.version }}" + -B "${TARGET}" \ + --title "Release: update changelog for ${VERSION}" \ + --body "Changelog changes for release ${VERSION}" env: - GH_TOKEN: ${{ steps.generate_token.outputs.token }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/close-milestone.yml b/.github/workflows/close-milestone.yml deleted file mode 100644 index 11613b5fab9..00000000000 --- a/.github/workflows/close-milestone.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Close milestone -on: - workflow_dispatch: - inputs: - version: - required: true - description: Needs to match, exactly, the name of a milestone - workflow_call: - inputs: - version_call: - description: Needs to match, exactly, the name of a milestone - required: true - type: string - -jobs: - main: - if: github.repository == 'grafana/grafana' - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - - name: Install Actions - run: npm install --production --prefix ./actions - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: Close milestone (manually invoked) - if: ${{ github.event.inputs.version != '' }} - uses: ./actions/close-milestone - with: - token: ${{ steps.generate_token.outputs.token }} - - name: Close milestone (workflow invoked) - if: ${{ inputs.version_call != '' }} - uses: ./actions/close-milestone - with: - version_call: ${{ inputs.version_call }} - token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/codeowners-validator.yml b/.github/workflows/codeowners-validator.yml index 12184b2f680..f98bee4e213 100644 --- a/.github/workflows/codeowners-validator.yml +++ b/.github/workflows/codeowners-validator.yml @@ -9,9 +9,11 @@ jobs: runs-on: ubuntu-latest steps: # Checks-out your repository, which is validated in the next step - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: GitHub CODEOWNERS Validator - uses: mszostok/codeowners-validator@v0.7.4 + uses: mszostok/codeowners-validator@7f3f5e28c6d7b8dfae5731e54ce2272ca384592f # input parameters with: # ==== GitHub Auth ==== diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 14981389deb..06772bac55c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -3,18 +3,19 @@ # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. -name: "CodeQL" +name: "CodeQL checks" on: workflow_dispatch: push: - branches: [main, v1.8.x, v2.0.x, v2.1.x, v2.6.x, v3.0.x, v3.1.x, v4.0.x, v4.1.x, v4.2.x, v4.3.x, v4.4.x, v4.5.x, v4.6.x, v4.7.x, v5.0.x, v5.1.x, v5.2.x, v5.3.x, v5.4.x, v6.0.x, v6.1.x, v6.2.x, v6.3.x, v6.4.x, v6.5.x, v6.6.x, v6.7.x, v7.0.x, v7.1.x, v7.2.x] + branches: ['**'] # run on all branches paths-ignore: - '**/*.cue' - '**/*.json' - '**/*.md' - '**/*.txt' - '**/*.yml' + - pkg/storage/unified/sql/db/dbimpl/db.go # Ignoring warnings on the whole file for now while inline comments is not supported in Go (https://github.com/github/codeql/issues/11427) schedule: - cron: '0 4 * * 6' @@ -25,6 +26,7 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest + continue-on-error: true # doesn't block PRs from being merged if this fails if: github.repository == 'grafana/grafana' strategy: @@ -38,21 +40,22 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 + persist-credentials: false - if: matrix.language == 'go' name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: go-version-file: go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -67,4 +70,4 @@ jobs: make build-go - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml index f733b2f244d..cf61d859443 100644 --- a/.github/workflows/commands.yml +++ b/.github/workflows/commands.yml @@ -12,9 +12,7 @@ on: concurrency: group: issue-commands-${{ github.event.issue.number }} -permissions: - contents: read - id-token: write +permissions: {} jobs: config: @@ -34,10 +32,13 @@ jobs: needs: config if: needs.config.outputs.has-secrets runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | @@ -52,11 +53,12 @@ jobs: private_key: ${{ env.GH_APP_PEM }} - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions diff --git a/.github/workflows/community-release.yml b/.github/workflows/community-release.yml index 86e7703e5c4..73c72749baa 100644 --- a/.github/workflows/community-release.yml +++ b/.github/workflows/community-release.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Run community-release (manually invoked) - uses: grafana/grafana-github-actions-go/community-release@main + uses: grafana/grafana-github-actions-go/community-release@main # zizmor: ignore[unpinned-uses] with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ inputs.version }} diff --git a/.github/workflows/core-plugins-build-and-release.yml b/.github/workflows/core-plugins-build-and-release.yml index e4803a2208f..d2aebbc41e8 100644 --- a/.github/workflows/core-plugins-build-and-release.yml +++ b/.github/workflows/core-plugins-build-and-release.yml @@ -33,6 +33,8 @@ permissions: jobs: build-and-publish: + env: + PLUGIN_ID: ${{ inputs.plugin_id }} name: Build and publish ${{ inputs.plugin_id }} runs-on: ubuntu-latest outputs: @@ -41,12 +43,14 @@ jobs: version: ${{ steps.build_frontend.outputs.version }} steps: - name: checkout - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Verify inputs run: | - if [ -z ${{ inputs.plugin_id }} ]; then echo "Missing plugin ID"; exit 1; fi + if [ -z $PLUGIN_ID ]; then echo "Missing plugin ID"; exit 1; fi - id: get-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana// path in Vault repo_secrets: | @@ -54,13 +58,13 @@ jobs: PLUGINS_GRAFANA_API_KEY=core-plugins-build-and-release:PLUGINS_GRAFANA_API_KEY PLUGINS_GCOM_TOKEN=core-plugins-build-and-release:PLUGINS_GCOM_TOKEN - name: 'Authenticate to Google Cloud' - uses: 'google-github-actions/auth@v2' + uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' with: credentials_json: '${{ env.PLUGINS_GOOGLE_CREDENTIALS }}' - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v2' + uses: 'google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a' - name: Setup nodejs environment - uses: actions/setup-node@v4 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version-file: .nvmrc cache: yarn @@ -70,7 +74,7 @@ jobs: run: | dir=$(dirname \ $(egrep -lir --include=plugin.json --exclude-dir=dist \ - '"id": "${{ inputs.plugin_id }}"' \ + '"id": "${PLUGIN_ID}"' \ public/app/plugins \ ) \ ) @@ -85,19 +89,19 @@ jobs: working-directory: ${{ steps.get_dir.outputs.dir }} run: | [ ! -d ./bin ] && mkdir -pv ./bin || true - curl -fL -o ./bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${{ env.GRABPL_VERSION }}/grabpl + curl -fL -o ./bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v$GRABPL_VERSION/grabpl chmod 0755 ./bin/grabpl - name: Check backend id: check_backend shell: bash run: | - if egrep -qr --include=main.go 'datasource.Manage\("${{ inputs.plugin_id }}"' pkg/tsdb; then + if egrep -qr --include=main.go 'datasource.Manage\("$PLUGIN_ID"' pkg/tsdb; then echo "has_backend=true" >> $GITHUB_OUTPUT else echo "has_backend=false" >> $GITHUB_OUTPUT fi - name: Setup golang environment - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 if: steps.check_backend.outputs.has_backend == 'true' with: go-version-file: go.mod @@ -151,7 +155,7 @@ jobs: # Release branch, do not add commit hash to version command="plugin:build" fi - yarn $command --scope="@grafana-plugins/${{ inputs.plugin_id }}" + yarn $command --scope="@grafana-plugins/$PLUGIN_ID" version=$(cat ${{ steps.get_dir.outputs.dir }}/dist/plugin.json | jq -r .info.version) echo "version=${version}" >> $GITHUB_OUTPUT - name: build:backend @@ -160,7 +164,7 @@ jobs: env: VERSION: ${{ steps.build_frontend.outputs.version }} run: | - make build-plugin-go PLUGIN_ID=${{ inputs.plugin_id }} + make build-plugin-go PLUGIN_ID=$PLUGIN_ID - name: package working-directory: ${{ steps.get_dir.outputs.dir }} run: | @@ -175,7 +179,7 @@ jobs: VERSION: ${{ steps.build_frontend.outputs.version }} run: | api_res=$(curl -X 'GET' -H "Authorization: Bearer $GCOM_TOKEN" \ - '${{ env.GCOM_API}}/api/plugins/${{ inputs.plugin_id }}?version=$VERSION' \ + '${{ env.GCOM_API}}/api/plugins/$PLUGIN_ID?version=$VERSION' \ -H 'accept: application/json') api_res_code=$(echo $api_res | jq -r .code) if [ "$api_res_code" = "NotFound" ]; then @@ -197,10 +201,10 @@ jobs: run: | echo "Publish release to Google Cloud Storage:" touch ci/packages/windows ci/packages/darwin ci/packages/linux ci/packages/any - gsutil -m cp -r ci/packages/*windows* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/windows - gsutil -m cp -r ci/packages/*linux* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux - gsutil -m cp -r ci/packages/*darwin* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/darwin - gsutil -m cp -r ci/packages/*any* gs://${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/any + gsutil -m cp -r ci/packages/*windows* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/windows + gsutil -m cp -r ci/packages/*linux* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux + gsutil -m cp -r ci/packages/*darwin* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/darwin + gsutil -m cp -r ci/packages/*any* gs://${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/any - name: Publish new plugin version on grafana.com if: steps.check_backend.outputs.has_backend == 'true' working-directory: ${{ steps.get_dir.outputs.dir }} @@ -214,27 +218,27 @@ jobs: \"url\": \"https://github.com/grafana/grafana/tree/main/${{ steps.get_dir.outputs.dir }}\", \"download\": { \"linux-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux/${{ inputs.plugin_id }}-${VERSION}.linux_amd64.zip\", + \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux/$PLUGIN_ID-${VERSION}.linux_amd64.zip\", \"md5\": \"$(cat ci/packages/info-linux_amd64.json | jq -r .plugin.md5)\" }, \"linux-arm64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux/${{ inputs.plugin_id }}-${VERSION}.linux_arm64.zip\", + \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux/$PLUGIN_ID-${VERSION}.linux_arm64.zip\", \"md5\": \"$(cat ci/packages/info-linux_arm64.json | jq -r .plugin.md5)\" }, \"linux-arm\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/linux/${{ inputs.plugin_id }}-${VERSION}.linux_arm.zip\", + \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/linux/$PLUGIN_ID-${VERSION}.linux_arm.zip\", \"md5\": \"$(cat ci/packages/info-linux_arm.json | jq -r .plugin.md5)\" }, \"windows-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/windows/${{ inputs.plugin_id }}-${VERSION}.windows_amd64.zip\", + \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/windows/$PLUGIN_ID-${VERSION}.windows_amd64.zip\", \"md5\": \"$(cat ci/packages/info-windows_amd64.json | jq -r .plugin.md5)\" }, \"darwin-amd64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/darwin/${{ inputs.plugin_id }}-${VERSION}.darwin_amd64.zip\", + \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/darwin/$PLUGIN_ID-${VERSION}.darwin_amd64.zip\", \"md5\": \"$(cat ci/packages/info-darwin_amd64.json | jq -r .plugin.md5)\" }, \"darwin-arm64\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/darwin/${{ inputs.plugin_id }}-${VERSION}.darwin_arm64.zip\", + \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/darwin/$PLUGIN_ID-${VERSION}.darwin_arm64.zip\", \"md5\": \"$(cat ci/packages/info-darwin_arm64.json | jq -r .plugin.md5)\" } } @@ -257,7 +261,7 @@ jobs: \"url\": \"https://github.com/grafana/grafana/tree/main/${{ steps.get_dir.outputs.dir }}\", \"download\": { \"any\": { - \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/${{ inputs.plugin_id }}/release/${VERSION}/any/${{ inputs.plugin_id }}-${VERSION}.any.zip\", + \"url\": \"https://storage.googleapis.com/${{ env.GCP_BUCKET }}/$PLUGIN_ID/release/${VERSION}/any/$PLUGIN_ID-${VERSION}.any.zip\", \"md5\": \"$(cat ci/packages/info-any.json | jq -r .plugin.md5)\" } } diff --git a/.github/workflows/create-next-release-branch.yml b/.github/workflows/create-next-release-branch.yml index 8fc01cd442d..1107842a765 100644 --- a/.github/workflows/create-next-release-branch.yml +++ b/.github/workflows/create-next-release-branch.yml @@ -46,7 +46,7 @@ jobs: private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: Create release branch id: branch - uses: grafana/grafana-github-actions-go/bump-release@main + uses: grafana/grafana-github-actions-go/bump-release@main # zizmor: ignore[unpinned-uses] with: ownerRepo: ${{ inputs.ownerRepo }} source: ${{ inputs.source }} diff --git a/.github/workflows/create-security-patch-from-security-mirror.yml b/.github/workflows/create-security-patch-from-security-mirror.yml index f85239241ad..7499d925236 100644 --- a/.github/workflows/create-security-patch-from-security-mirror.yml +++ b/.github/workflows/create-security-patch-from-security-mirror.yml @@ -17,7 +17,7 @@ on: jobs: trigger_downstream_create_security_patch: concurrency: create-patch-${{ github.ref_name }} - uses: grafana/security-patch-actions/.github/workflows/create-patch.yml@main + uses: grafana/security-patch-actions/.github/workflows/create-patch.yml@main # zizmor: ignore[unpinned-uses] if: github.repository == 'grafana/grafana-security-mirror' with: repo: "${{ github.repository }}" @@ -25,5 +25,4 @@ jobs: patch_ref: "${{ github.base_ref }}" # this is the target branch name, Ex: "main" patch_repo: "grafana/grafana-security-patches" patch_prefix: "${{ github.event.pull_request.number }}" - secrets: inherit - + secrets: inherit # zizmor: ignore[secrets-inherit] diff --git a/.github/workflows/dashboards-issue-add-label.yml b/.github/workflows/dashboards-issue-add-label.yml index 766c89c7242..4072f062fa7 100644 --- a/.github/workflows/dashboards-issue-add-label.yml +++ b/.github/workflows/dashboards-issue-add-label.yml @@ -3,8 +3,11 @@ on: issues: types: [opened, closed, edited, reopened, assigned, unassigned, labeled, unlabeled] +permissions: + contents: read + id-token: write + env: - GITHUB_TOKEN: ${{ secrets.ISSUE_COMMANDS_TOKEN }} ORGANIZATION: ${{ github.repository_owner }} REPO: ${{ github.event.repository.name }} TARGET_PROJECT: 202 @@ -13,32 +16,35 @@ env: concurrency: group: issue-label-when-in-project-${{ github.event.number }} jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.ISSUE_COMMANDS_TOKEN != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - main: - needs: config - if: needs.config.outputs.has-secrets + if: github.repository == 'grafana/grafana' runs-on: ubuntu-latest steps: - - name: log in - run: gh api user -q .login + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] + with: + # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault + repo_secrets: | + GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id + GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + + - name: "Generate token" + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ env.GH_APP_ID }} + private_key: ${{ env.GH_APP_PEM }} - name: Check if issue is in target project + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + TARGET_PROJECT: ${{ env.TARGET_PROJECT }} run: | gh api graphql -f query=' query($org: String!, $repo: String!) { repository(name: $repo, owner: $org) { - issue (number: ${{ github.event.issue.number }}) { + issue (number: $ISSUE_NUMBER) { id projectItems(first:20) { nodes { @@ -51,17 +57,22 @@ jobs: } }' -f org=$ORGANIZATION -f repo=$REPO > projects_data.json - echo 'IN_TARGET_PROJ='$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number==${{ env.TARGET_PROJECT }}) | .project != null' projects_data.json) >> $GITHUB_ENV + echo 'IN_TARGET_PROJ='$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number=='"$TARGET_PROJECT"') | .project != null' projects_data.json) >> $GITHUB_ENV echo 'ITEM_ID='$(jq '.data.repository.issue.id' projects_data.json) >> $GITHUB_ENV - name: Set up label array if: env.IN_TARGET_PROJ + env: + LABEL_IDS: ${{ env.LABEL_IDS }} run: | - IFS=',' read -ra LABEL_IDs <<< "${{ env.LABEL_IDs }}" + IFS=',' read -ra LABEL_IDs <<< "$LABEL_IDS" for item in "${LABEL_IDs[@]}"; do echo "Item: $item" done - name: Add label to issue if: env.IN_TARGET_PROJ + env: + GH_TOKEN: ${{ steps.generate_token.outputs.token }} + LABEL_IDS: ${{ env.LABEL_IDS }} run: | gh api graphql -f query=' mutation ($labelableId: ID!, $labelIds: [ID!]!) { @@ -70,4 +81,4 @@ jobs: ) { clientMutationId } - }' -f labelableId=$ITEM_ID -f labelIds=${{ env.LABEL_IDs }} + }' -f labelableId=$ITEM_ID -f labelIds=$LABEL_IDS diff --git a/.github/workflows/deploy-pr-preview.yml b/.github/workflows/deploy-pr-preview.yml new file mode 100644 index 00000000000..5c8f4733eeb --- /dev/null +++ b/.github/workflows/deploy-pr-preview.yml @@ -0,0 +1,31 @@ +name: Deploy pr preview + +on: + pull_request: + types: + - opened + - synchronize + - closed + paths: + - "docs/sources/**" + +jobs: + deploy-pr-preview: + if: "!github.event.pull_request.head.repo.fork" + uses: grafana/writers-toolkit/.github/workflows/deploy-preview.yml@main # zizmor: ignore[unpinned-uses] + with: + branch: ${{ github.head_ref }} + event_number: ${{ github.event.number }} + repo: grafana + sha: ${{ github.event.pull_request.head.sha }} + sources: | + [ + { + "index_file": "content/docs/grafana/_index.md", + "relative_prefix": "/docs/grafana/latest/", + "repo": "grafana", + "source_directory": "docs/sources", + "website_directory": "content/docs/grafana/latest" + } + ] + title: ${{ github.event.pull_request.title }} diff --git a/.github/workflows/detect-breaking-changes-levitate.yml b/.github/workflows/detect-breaking-changes-levitate.yml index 8da09bdd753..a2517d5e0a2 100644 --- a/.github/workflows/detect-breaking-changes-levitate.yml +++ b/.github/workflows/detect-breaking-changes-levitate.yml @@ -6,6 +6,8 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: {} + on: pull_request: paths: @@ -20,14 +22,18 @@ jobs: defaults: run: working-directory: './pr' + permissions: + contents: read + id-token: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: './pr' - - uses: actions/setup-node@v4 + persist-credentials: false + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: - node-version: 20.9.0 + node-version: 22.11.0 - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -63,19 +69,22 @@ jobs: buildBase: name: Build Base packages artifacts runs-on: ubuntu-latest + permissions: + contents: read + id-token: write defaults: run: working-directory: './base' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: path: './base' ref: ${{ github.event.pull_request.base.ref }} - - uses: actions/setup-node@v4 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: - node-version: 20.9.0 + node-version: 22.11.0 - name: Get yarn cache directory path id: yarn-cache-dir-path @@ -119,10 +128,10 @@ jobs: id-token: 'write' steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: - node-version: 20.9.0 + node-version: 22.11.0 - name: Get built packages from pr uses: actions/download-artifact@v4 @@ -141,39 +150,29 @@ jobs: run: unzip -j base_built_packages.zip -d ./base && rm base_built_packages.zip - id: 'auth' - uses: 'google-github-actions/auth@v2' + uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' with: workload_identity_provider: ${{ secrets.WIF_PROVIDER }} service_account: ${{ secrets.LEVITATE_SA }} project_id: 'grafanalabs-global' - name: 'Set up Cloud SDK' - uses: 'google-github-actions/setup-gcloud@v2' + uses: 'google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a' with: version: '>= 363.0.0' project_id: 'grafanalabs-global' install_components: 'bq' - - name: Get link for the Github Action job - id: job - uses: actions/github-script@v6 - with: - script: | - const name = 'Detect breaking changes'; - const script = require('./.github/workflows/scripts/pr-get-job-link.js') - await script({name, github, context, core}) - - name: Detect breaking changes id: breaking-changes run: ./scripts/check-breaking-changes.sh env: FORCE_COLOR: 3 - GITHUB_JOB_LINK: ${{ steps.job.outputs.link }} - name: Persisting the check output run: | mkdir -p ./levitate - echo "{ \"exit_code\": ${{ steps.breaking-changes.outputs.is_breaking }}, \"message\": \"${{ steps.breaking-changes.outputs.message }}\", \"job_link\": \"${{ steps.job.outputs.link }}#step:${GITHUB_STEP_NUMBER}:1\", \"pr_number\": \"${{ github.event.pull_request.number }}\" }" > ./levitate/result.json + echo "{ \"exit_code\": ${{ steps.breaking-changes.outputs.is_breaking }}, \"message\": \"${{ steps.breaking-changes.outputs.message }}\", \"pr_number\": \"${{ github.event.pull_request.number }}\" }" > ./levitate/result.json - name: Upload check output as artifact uses: actions/upload-artifact@v4 @@ -186,6 +185,9 @@ jobs: name: Report breaking changes in PR comment runs-on: ubuntu-latest needs: ['Detect'] + permissions: + contents: read + id-token: write steps: - name: "Generate token" @@ -195,7 +197,7 @@ jobs: app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - name: 'Download artifact' uses: actions/download-artifact@v4 @@ -219,15 +221,12 @@ jobs: PR_NUMBER: ${{ github.event.pull_request.number }} with: script: | - const { data } = await github.rest.issues.listLabelsOnIssue({ - issue_number: process.env.PR_NUMBER, + const { data: labels } = await github.rest.issues.listLabelsOnIssue({ + issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, }); - const labels = data.map(({ name }) => name); - const doesExist = labels.includes('levitate breaking change'); - - return doesExist ? 1 : 0; + return labels.some(label => label.name === 'levitate breaking change') ? 1 : 0 # put the markdown into a variable - name: Levitate Markdown @@ -247,7 +246,7 @@ jobs: # Comment on the PR - name: Comment on PR if: steps.levitate-run.outputs.exit_code == 1 - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 with: header: levitate-breaking-change-comment number: ${{ github.event.pull_request.number }} @@ -264,30 +263,48 @@ jobs: # Remove comment from the PR (no more breaking changes) - name: Remove comment from PR if: steps.levitate-run.outputs.exit_code == 0 - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 with: header: levitate-breaking-change-comment number: ${{ github.event.pull_request.number }} delete: true GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - # Posts a notification to Slack if a PR has a breaking change and it did not have a breaking change before - - name: Post to Slack + - name: Send Slack Message via Payload id: slack - if: steps.levitate-run.outputs.exit_code == 1 && steps.does-label-exist.outputs.result == 0 && env.HAS_SECRETS - uses: slackapi/slack-github-action@v1.26.0 + if: steps.levitate-run.outputs.exit_code == 1 && steps.does-label-exist.outputs.result == 0 && github.repository == 'grafana/grafana' + uses: grafana/shared-workflows/actions/send-slack-message@7b628e7352c2dea057c565cc4fcd5564d5f396c0 #v1.0.0 with: - payload: | + channel-id: "C031SLFH6G0" + payload: | { - "pr_link": "https://github.com/grafana/grafana/pull/${{ steps.levitate-run.outputs.pr_number }}", - "pr_number": "${{ steps.levitate-run.outputs.pr_number }}", - "job_link": "${{ steps.levitate-run.outputs.job_link }}", - "reporting_job_link": "${{ github.event.workflow_run.html_url }}", - "message": "${{ steps.levitate-run.outputs.message }}" + "channel": "C031SLFH6G0", + "text": ":warning: Possible breaking changes detected in *PR:* <${{ github.event.pull_request.html_url }}|#${{ github.event.pull_request.number }} :warning:", + "icon_emoji": ":grot:", + "username": "Levitate Bot", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*grafana/grafana* repository has possible breaking changes" + } + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*PR:* <${{ github.event.pull_request.html_url }}|#${{ github.event.pull_request.number }}>" + }, + { + "type": "mrkdwn", + "text": "*Job:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Job>" + } + ] + } + ] } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_LEVITATE_WEBHOOK_URL }} - HAS_SECRETS: ${{ (github.repository == 'grafana/grafana' || secrets.SLACK_LEVITATE_WEBHOOK_URL != '') || '' }} # Add the label - name: Add "levitate breaking change" label diff --git a/.github/workflows/doc-validator.yml b/.github/workflows/doc-validator.yml deleted file mode 100644 index 0e6f613da9b..00000000000 --- a/.github/workflows/doc-validator.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: "doc-validator" -on: - workflow_dispatch: - inputs: - include: - description: | - Regular expression that matches paths to include in linting. - - For example: docs/sources/(?:alerting|fundamentals)/.+\.md - required: true -jobs: - doc-validator: - runs-on: "ubuntu-latest" - container: - image: "grafana/doc-validator:v5.2.0" - steps: - - name: "Checkout code" - uses: "actions/checkout@v4" - - name: "Run doc-validator tool" - # Only run doc-validator on specific directories. - run: > - doc-validator - '--include=${{ inputs.include }}' - '--skip-checks=^(?:image.+|canonical-does-not-match-pretty-URL)$' - ./docs/sources - /docs/grafana/latest diff --git a/.github/workflows/documentation-ci.yml b/.github/workflows/documentation-ci.yml new file mode 100644 index 00000000000..9b8bc7dc53b --- /dev/null +++ b/.github/workflows/documentation-ci.yml @@ -0,0 +1,19 @@ +name: Documentation CI +on: + pull_request: + branches: ["main"] + paths: ["docs/sources/**"] + workflow_dispatch: +jobs: + vale: + runs-on: ubuntu-latest + container: + image: grafana/vale:latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - uses: grafana/writers-toolkit/vale-action@vale-action/v1 # zizmor: ignore[unpinned-uses] + with: + filter: '.Name in ["Grafana.GrafanaCom", "Grafana.WordList", "Grafana.Spelling", "Grafana.ProductPossessives"]' + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ephemeral-instances-pr-comment.yml b/.github/workflows/ephemeral-instances-pr-comment.yml index be07b7ef6ac..e60eca1bad4 100644 --- a/.github/workflows/ephemeral-instances-pr-comment.yml +++ b/.github/workflows/ephemeral-instances-pr-comment.yml @@ -41,12 +41,13 @@ jobs: private_key: ${{ secrets.EI_APP_PRIVATE_KEY }} - name: Checkout ephemeral instances repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: repository: grafana/ephemeral-grafana-instances-github-action token: ${{ steps.generate_token.outputs.token }} ref: main path: ephemeral + persist-credentials: false - name: build and deploy ephemeral instance uses: ./ephemeral diff --git a/.github/workflows/epic-add-to-platform-ux-parent-project.yml b/.github/workflows/epic-add-to-platform-ux-parent-project.yml deleted file mode 100644 index 97462269fce..00000000000 --- a/.github/workflows/epic-add-to-platform-ux-parent-project.yml +++ /dev/null @@ -1,149 +0,0 @@ -name: When epic issues changed in Platform UX squad projects, check if epic is part of specified child projects and update on Platform UX parent project - -on: - issues: - types: [opened, closed, edited, reopened, assigned, unassigned, labeled, unlabeled] - labels: - - 'type/epic' - -env: - GH_TOKEN: ${{ secrets.GH_BOT_PROJECTS_ACCESS_TOKEN }} - ORGANIZATION: ${{ github.repository_owner }} - REPO: ${{ github.event.repository.name }} - PARENT_PROJECT: 304 - CHILD_PROJECT_1: 78 - CHILD_PROJECT_2: 111 - CHILD_PROJECT_3: 202 - -concurrency: - group: issue-add-to-parent-project-${{ github.event.number }} -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GH_BOT_PROJECTS_ACCESS_TOKEN != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - main: - needs: config - if: needs.config.outputs.has-secrets && contains(github.event.issue.labels.*.name, 'type/epic') - runs-on: ubuntu-latest - steps: - - name: Check if issue is in child or parent projects - run: | - gh api graphql -f query=' - query($org: String!, $repo: String!) { - repository(name: $repo, owner: $org) { - issue (number: ${{ github.event.issue.number }}) { - projectItems(first:20) { - nodes { - id, - project { - number, - title - }, - fieldValueByName(name:"Status") { - ... on ProjectV2ItemFieldSingleSelectValue { - optionId - name - } - } - } - } - } - } - }' -f org=$ORGANIZATION -f repo=$REPO > projects_data.json - - echo 'IN_PARENT_PROJ='$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number==${{ env.PARENT_PROJECT }}) | .project != null' projects_data.json) >> $GITHUB_ENV - echo 'PARENT_PROJ_STATUS_ID='$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number==${{ env.PARENT_PROJECT }}) | select(.fieldValueByName != null) | .fieldValueByName.optionId' projects_data.json) >> $GITHUB_ENV - echo 'ITEM_ID='$(jq '.data.repository.issue.projectItems.nodes[] | select(.project.number==${{ env.PARENT_PROJECT }}) | .id' projects_data.json) >> $GITHUB_ENV - echo 'IN_CHILD_PROJ='$(jq 'first(.data.repository.issue.projectItems.nodes[] | select(.project.number==${{ env.CHILD_PROJECT_1 }} or .project.number==${{ env.CHILD_PROJECT_2 }} or .project.number==${{ env.CHILD_PROJECT_3 }}) | .project != null)' projects_data.json) >> $GITHUB_ENV - echo 'CHILD_PROJ_STATUS='$(jq -r '.data.repository.issue.projectItems.nodes[] | select(.project.number==${{ env.CHILD_PROJECT_1 }} or .project.number==${{ env.CHILD_PROJECT_2 }} or .project.number==${{ env.CHILD_PROJECT_3 }}) | select(.fieldValueByName != null) | .fieldValueByName.name' projects_data.json) >> $GITHUB_ENV - - name: Get parent project project data - if: env.IN_CHILD_PROJ - run: | - gh api graphql -f query=' - query($org: String!, $number: Int!) { - organization(login: $org){ - projectV2(number: $number) { - id - fields(first:20) { - nodes { - ... on ProjectV2Field { - id - name - } - ... on ProjectV2SingleSelectField { - id - name - options { - id - name - } - } - } - } - } - } - }' -f org=$ORGANIZATION -F number=$PARENT_PROJECT > project_data.json - - echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV - echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV - echo 'TODO_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Todo") |.id' project_data.json) >> $GITHUB_ENV - echo 'PROGRESS_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="In Progress") |.id' project_data.json) >> $GITHUB_ENV - echo 'DONE_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .options[] | select(.name=="Done") |.id' project_data.json) >> $GITHUB_ENV - - name: Add issue to parent project - if: env.IN_CHILD_PROJ && !env.IN_PARENT_PROJ - run: | - item_id="$( gh api graphql -f query=' - mutation($project:ID!, $issue:ID!) { - addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) { - item { - id - } - } - }' -f project=$PROJECT_ID -f issue=${{ github.event.issue.node_id }} --jq '.data.addProjectV2ItemById.item.id')" - - echo 'ITEM_ID='$item_id >> $GITHUB_ENV - - name: Set parent project status Done - if: contains(env.CHILD_PROJ_STATUS, 'Done') - run: | - echo 'OPTION_ID='$DONE_OPTION_ID >> $GITHUB_ENV - - name: Set parent project status In Progress - if: contains(env.CHILD_PROJ_STATUS, 'In ') || contains(env.CHILD_PROJ_STATUS, 'Blocked') - run: | - echo 'OPTION_ID='$PROGRESS_OPTION_ID >> $GITHUB_ENV - - name: Set parent project status To do - if: env.CHILD_PROJ_STATUS && !contains(env.CHILD_PROJ_STATUS, 'In ') && !contains(env.CHILD_PROJ_STATUS, 'Blocked') && ! contains(env.CHILD_PROJ_STATUS, 'Done') - run: | - echo 'OPTION_ID='$TODO_OPTION_ID >> $GITHUB_ENV - - name: Set issue status in parent project - if: env.OPTION_ID && (env.OPTION_ID != env.PARENT_PROJ_STATUS_ID) - run: | - gh api graphql -f query=' - mutation ( - $project: ID! - $item: ID! - $status_field: ID! - $status_value: String! - ) { - set_status: updateProjectV2ItemFieldValue(input: { - projectId: $project - itemId: $item - fieldId: $status_field - value: { - singleSelectOptionId: $status_value - } - }) { - projectV2Item { - id - } - } - }' -f project=$PROJECT_ID -f item=$ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.OPTION_ID }} --silent diff --git a/.github/workflows/feature-toggles-ci.yml b/.github/workflows/feature-toggles-ci.yml new file mode 100644 index 00000000000..7aa1dbd0b42 --- /dev/null +++ b/.github/workflows/feature-toggles-ci.yml @@ -0,0 +1,25 @@ +name: Feature toggles CI + +on: + pull_request: + paths: + - 'pkg/services/featuremgmt/toggles_gen_test.go' + - 'pkg/services/featuremgmt/registry.go' + - 'docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md' + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + + - name: Set up Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: 'go.mod' + cache: true + + - name: Run feature toggle tests + run: go test -v -run TestFeatureToggleFiles ./pkg/services/featuremgmt/ diff --git a/.github/workflows/frontend-lint.yml b/.github/workflows/frontend-lint.yml new file mode 100644 index 00000000000..42533e0127d --- /dev/null +++ b/.github/workflows/frontend-lint.yml @@ -0,0 +1,133 @@ +name: Lint Frontend +on: + pull_request: + push: + branches: + - main + - release-*.*.* + +permissions: {} + +jobs: + lint-frontend-verify-i18n: + name: Verify i18n + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - run: yarn install --immutable --check-cache + - run: | + extract_error_message='::error::Extraction failed. Make sure that you have no dynamic translation phrases, such as "t(`preferences.theme.{themeID}`, themeName)" and that no translation key is used twice. Search the output for '[warning]' to find the offending file.' + make i18n-extract || (echo "${extract_error_message}" && false) + - run: | + uncommited_error_message="::error::Translation extraction has not been committed. Please run 'make i18n-extract', commit the changes and push again." + file_diff=$(git diff --dirstat public/locales) + if [ -n "$file_diff" ]; then + echo $file_diff + echo "${uncommited_error_message}" + exit 1 + fi + lint-frontend-prettier: + permissions: + contents: read + id-token: write + # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, + # the `lint-frontend-prettier-enterprise` workflow will run instead + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - run: yarn install --immutable --check-cache + - run: yarn run prettier:check + - run: yarn run lint + lint-frontend-prettier-enterprise: + permissions: + contents: read + id-token: write + # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - name: Setup Enterprise + uses: ./.github/actions/setup-enterprise + with: + github-app-name: 'grafana-ci-bot' + - run: yarn install --immutable --check-cache + - run: yarn run prettier:check + - run: yarn run lint + lint-frontend-typecheck: + permissions: + contents: read + id-token: write + # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, + # the `lint-frontend-typecheck-enterprise` workflow will run instead + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true + name: Typecheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - run: yarn install --immutable --check-cache + - run: yarn run typecheck + lint-frontend-typecheck-enterprise: + permissions: + contents: read + id-token: write + # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + name: Typecheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - name: Setup Enterprise + uses: ./.github/actions/setup-enterprise + with: + github-app-name: 'grafana-ci-bot' + - run: yarn install --immutable --check-cache + - run: yarn run typecheck + lint-frontend-betterer: + permissions: + contents: read + id-token: write + name: Betterer + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - run: yarn install --immutable --check-cache + - run: yarn run betterer:ci diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 8cbe3030ffe..211c9d90fd2 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -40,7 +40,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Create GitHub release (manually invoked) - uses: grafana/grafana-github-actions-go/github-release@main + uses: grafana/grafana-github-actions-go/github-release@main # zizmor: ignore[unpinned-uses] with: token: ${{ secrets.GITHUB_TOKEN }} version: ${{ inputs.version }} diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index 0925dcf02d0..0c65982f9c8 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -16,15 +16,17 @@ jobs: lint-go: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 with: go-version-file: ./go.mod - run: make gen-go - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd with: - version: v1.64.2 + version: v2.0.2 args: | --verbose $(go list -m -f '{{.Dir}}' | xargs -I{} sh -c 'test ! -f {}/.nolint && echo {}/...') install-mode: binary diff --git a/.github/workflows/i18n-crowdin-create-tasks.yml b/.github/workflows/i18n-crowdin-create-tasks.yml new file mode 100644 index 00000000000..dbe2be9a5b8 --- /dev/null +++ b/.github/workflows/i18n-crowdin-create-tasks.yml @@ -0,0 +1,27 @@ +name: Crowdin Create Tasks + +on: + workflow_dispatch: + # schedule: + # - cron: "0 0 * * *" + +jobs: + create-tasks-in-crowdin: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + + - name: Create tasks + env: + CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }} + CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }} + run: node ./.github/workflows/scripts/crowdin/create-tasks.js diff --git a/.github/workflows/i18n-crowdin-download.yml b/.github/workflows/i18n-crowdin-download.yml index cba14abe43b..d287c7ee06f 100644 --- a/.github/workflows/i18n-crowdin-download.yml +++ b/.github/workflows/i18n-crowdin-download.yml @@ -3,7 +3,7 @@ name: Crowdin Download Action on: workflow_dispatch: schedule: - - cron: "0 * * * *" + - cron: "0 0 * * *" jobs: download-sources-from-crowdin: @@ -12,6 +12,7 @@ jobs: permissions: contents: write # needed to commit changes into the PR pull-requests: write # needed to update PR description, labels, etc + id-token: write # needed to get vault secrets steps: - name: Generate token @@ -21,14 +22,15 @@ jobs: app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: ref: ${{ github.head_ref }} token: ${{ steps.generate_token.outputs.token }} + persist-credentials: false - name: Download sources id: crowdin-download - uses: crowdin/github-action@v2 + uses: crowdin/github-action@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 with: upload_sources: false upload_translations: false @@ -41,17 +43,11 @@ jobs: pull_request_body: | :robot: Automatic download of translations from Crowdin. - Steps for merging: - 1. A quick sanity check of the changes and approve. Things to look out for: - - No changes in the English file. The source of truth is in the main branch, NOT in Crowdin. - - Translations maybe be removed if the English phrase was removed, but there should not be many of these - - Anything else that looks 'funky'. Ask if you're not sure. - 2. Approve & (Auto-)merge. :tada: + This runs once per day and will merge automatically if all the required checks pass. - If there's a conflict, close the pull request and **delete the branch**. A GH action will recreate the pull request. - Remember, the longer this pull request is open, the more likely it is that it'll get conflicts. + If there's a conflict, close the pull request and **delete the branch**. + You can then either wait for the schedule to trigger a new PR, or rerun the action manually. pull_request_labels: 'area/frontend, area/internationalization, no-changelog, no-backport' - pull_request_reviewers: 'grafana-frontend-platform' pull_request_base_branch_name: 'main' base_url: 'https://grafana.api.crowdin.com' config: 'crowdin.yml' @@ -77,7 +73,7 @@ jobs: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - name: Get project board ID - uses: octokit/graphql-action@v2.x + uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 id: get-project-id if: steps.crowdin-download.outputs.pull_request_url with: @@ -97,7 +93,7 @@ jobs: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - name: Add to project board - uses: octokit/graphql-action@v2.x + uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 if: steps.crowdin-download.outputs.pull_request_url with: projectid: ${{ fromJson(steps.get-project-id.outputs.data).organization.projectV2.id }} @@ -114,8 +110,50 @@ jobs: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - name: Run auto-milestone - uses: grafana/grafana-github-actions-go/auto-milestone@main + uses: grafana/grafana-github-actions-go/auto-milestone@main # zizmor: ignore[unpinned-uses] if: steps.crowdin-download.outputs.pull_request_url with: pr: ${{ steps.crowdin-download.outputs.pull_request_number }} token: ${{ steps.generate_token.outputs.token }} + + - name: Get vault secrets + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] + with: + # Secrets placed in ci/repo/grafana/grafana/grafana-pr-approver + repo_secrets: | + GRAFANA_PR_APPROVER_APP_ID=grafana-pr-approver:app-id + GRAFANA_PR_APPROVER_APP_PEM=grafana-pr-approver:private-key + + - name: Generate approver token + if: steps.crowdin-download.outputs.pull_request_url + id: generate_approver_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ env.GRAFANA_PR_APPROVER_APP_ID }} + private_key: ${{ env.GRAFANA_PR_APPROVER_APP_PEM }} + + - name: Approve and automerge PR + if: steps.crowdin-download.outputs.pull_request_url + shell: bash + # Only approve if: + # - the PR does not modify files other than json files under the public/locales/ directory + # - the PR does not modify the en-US locale + run: | + filesChanged=$(gh pr diff --name-only ${{ steps.crowdin-download.outputs.pull_request_url }}) + + if [[ $(echo $filesChanged | grep -v 'public/locales/[a-zA-Z\-]*/grafana.json' | wc -l) -ne 0 ]]; then + echo "Non-i18n changes detected, not approving" + exit 1 + fi + + if [[ $(echo $filesChanged | grep "public/locales/en-US" | wc -l) -ne 0 ]]; then + echo "public/locales/en-US changes detected, not approving" + exit 1 + fi + + echo "Approving and enabling automerge" + gh pr review ${{ steps.crowdin-download.outputs.pull_request_url }} --approve + gh pr merge --auto --squash ${{ steps.crowdin-download.outputs.pull_request_url }} + env: + GITHUB_TOKEN: ${{ steps.generate_approver_token.outputs.token }} diff --git a/.github/workflows/i18n-crowdin-upload.yml b/.github/workflows/i18n-crowdin-upload.yml index 39a89c5aad2..d7d1a130fcd 100644 --- a/.github/workflows/i18n-crowdin-upload.yml +++ b/.github/workflows/i18n-crowdin-upload.yml @@ -14,10 +14,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Upload sources - uses: crowdin/github-action@v2 + uses: crowdin/github-action@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 with: upload_sources: true upload_sources_args: '--dest=public/locales/en-US/grafana.json' diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml deleted file mode 100644 index 09513a7d5cc..00000000000 --- a/.github/workflows/issue-labeled.yml +++ /dev/null @@ -1,99 +0,0 @@ -name: Notify Slack channel based on new issue label - -on: - issues: - types: [labeled] - -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.SLACK_WEBHOOK_URL != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - notify: - needs: config - if: needs.config.outputs.has-secrets - runs-on: ubuntu-latest - steps: - - name: "Download teams.yml to know which label is for which team" - run: wget https://raw.githubusercontent.com/grafana/grafana/main/.github/teams.yml - - - name: "Determine which team to notify" - run: | - # Default to null values. - CHANNEL="null" - TEAM="null" - - echo "${{ github.event.label.name }} label added" - export CURRENT_LABEL="${{ github.event.label.name }}" # Enable the use of the label in yq evaluations - # yq is installed by default in ubuntu-latest - if [[ $(yq e 'keys | .[] | select(. == env(CURRENT_LABEL))' teams.yml ) ]]; then - # Check if we have a channel set to notify on comments. - if [[ $(yq '.[env(CURRENT_LABEL)] | has("channel-label")' teams.yml ) == true ]]; then - CHANNEL=$(yq '.[env(CURRENT_LABEL)].channel-label' teams.yml) - echo "Ready to send issue to channel ID ${CHANNEL}" - fi - - if [[ $(yq '.[env(CURRENT_LABEL)] | has("exclude-github-team")' teams.yml ) == true ]]; then - TEAM=$(yq '.[env(CURRENT_LABEL)].exclude-github-team' teams.yml) - echo "Will not send issue to channel if issue author is part of the team ${TEAM}" - fi - fi - - # set environment for next steps - echo "CHANNEL=${CHANNEL}" >> "$GITHUB_ENV" - echo "TEAM=${TEAM}" >> "$GITHUB_ENV" - - - name: "Prepare payload" - uses: frabert/replace-string-action@v2.5 - id: preparePayload - with: - # replace double quotes with single quotes to avoid breaking the JSON payload sent to Slack - string: ${{ github.event.issue.title }} - pattern: '"' - replace-with: "'" - flags: 'g' - - - name: Get Token - id: get_workflow_token - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a - with: - app_id: ${{ secrets.APP_GRAFANA_TEAM_CHECKER_ID }} - private_key: ${{ secrets.APP_GRAFANA_TEAM_CHECKER_KEY }} - - - name: "Check that issue author is not part of the team" - if: ${{ env.TEAM != 'null' }} - run: | - response=$(gh api /orgs/grafana/teams/${{ env.TEAM }}/memberships/${{ github.event.issue.user.login }} -i -H "Accept: application/vnd.github.v3+json") - STATUS_CODE=$(echo "$response" | head -n 1 | cut -d' ' -f2) - if [ "$STATUS_CODE" -eq "404" ]; then - echo "The user was not found in the team." - echo "USER_FOUND=false" >> "$GITHUB_ENV" - else - echo "The user was potentially found in the team" - echo "USER_FOUND=maybe" >> "$GITHUB_ENV" - fi - env: - GITHUB_TOKEN: ${{ steps.get_workflow_token.outputs.token }} - - - name: "Send Slack notification" - if: ${{ (env.CHANNEL != 'null') && ((env.USER_FOUND == 'false') || (env.TEAM != 'null')) }} - uses: slackapi/slack-github-action@v1.26.0 - with: - payload: > - { - "icon_emoji": ":grafana:", - "username": "Grafana issue labeled", - "text": "Issue \"${{ steps.preparePayload.outputs.replaced }}\" labeled \"${{ github.event.label.name }}\": ${{ github.event.issue.html_url }}, please triage.", - "channel": "${{ env.CHANNEL }}" - } - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/issue-opened.yml b/.github/workflows/issue-opened.yml index 15b9f593a98..91767a5310b 100644 --- a/.github/workflows/issue-opened.yml +++ b/.github/workflows/issue-opened.yml @@ -10,22 +10,24 @@ on: concurrency: group: issue-opened-${{ github.event.issue.number }} -permissions: - contents: read - id-token: write +permissions: {} jobs: main: runs-on: ubuntu-latest if: github.repository == 'grafana/grafana' + permissions: + contents: read + id-token: write steps: - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions @@ -37,7 +39,7 @@ jobs: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot path in Vault repo_secrets: | @@ -60,13 +62,16 @@ jobs: auto-triage: needs: [main] + permissions: + contents: read + id-token: write if: github.repository == 'grafana/grafana' && github.event.issue.author_association != 'MEMBER' && github.event.issue.author_association != 'OWNER' runs-on: ubuntu-latest steps: - name: "Get vault secrets" id: vault-secrets - uses: grafana/shared-workflows/actions/get-vault-secrets@main + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] with: # Secrets placed in the ci/repo/grafana/grafana/plugins_platform_issue_triager path in Vault repo_secrets: | @@ -82,31 +87,24 @@ jobs: app_id: ${{ env.GH_APP_ID }} private_key: ${{ env.GH_APP_PEM }} - - name: Checkout auto-triager repository - uses: actions/checkout@v4 - with: - repository: grafana/auto-triager - path: auto-triager - token: ${{ steps.generate_token.outputs.token }} + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Send issue to the auto triager action id: auto_triage - # https://github.com/grafana/auto-triager/blob/main/action.yml - #uses: grafana/auto-triager@main - uses: ./auto-triager + uses: grafana/auto-triager@main # zizmor: ignore[unpinned-uses] with: token: ${{ steps.generate_token.outputs.token }} issue_number: ${{ github.event.issue.number }} openai_api_key: ${{ env.AUTOTRIAGER_OPENAI_API_KEY }} add_labels: true - - - name: Labels from auto triage - run: | - echo ${{ steps.auto_triage.outputs.triage_labels }} + labels_file: ${{ github.workspace }}/.github/workflows/auto-triager/labels.txt + types_file: ${{ github.workspace }}/.github/workflows/auto-triager/types.txt + prompt_file: ${{ github.workspace }}/.github/workflows/auto-triager/prompt.txt - name: "Send Slack notification" if: ${{ steps.auto_triage.outputs.triage_labels != '' }} - uses: slackapi/slack-github-action@v1.27.0 + uses: slackapi/slack-github-action@37ebaef184d7626c5f204ab8d3baff4262dd30f0 # v1.27.0 with: payload: > { diff --git a/.github/workflows/lint-build-docs.yml b/.github/workflows/lint-build-docs.yml new file mode 100644 index 00000000000..1477cb2797a --- /dev/null +++ b/.github/workflows/lint-build-docs.yml @@ -0,0 +1,62 @@ +name: Documentation + +on: + pull_request: + paths: + - '*.md' + - 'docs/**' + - 'packages/**/*.md' + - 'latest.json' + push: + branches: + - main + paths: + - '*.md' + - 'docs/**' + - 'packages/**/*.md' + - 'latest.json' + +jobs: + docs: + name: Build & Verify Docs + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version: '22.11.0' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable + + - name: Lint docs + run: yarn run prettier:checkDocs + env: + # Increase memory for prettier due to large number of files + NODE_OPTIONS: --max_old_space_size=8192 + + - name: Build docs website + run: | + # Create and start a container from the docs-base image in detached mode + docker run -d --name docs-builder grafana/docs-base:latest tail -f /dev/null + + # Create the directory structure inside the container + docker exec docs-builder mkdir -p /hugo/content/docs/grafana/latest + + # Create the _index.md file + docker exec docs-builder /bin/sh -c "echo -e '---\nredirectURL: /docs/grafana/latest/\ntype: redirect\nversioned: true\n---\n' > /hugo/content/docs/grafana/_index.md" + + # Copy the docs sources from the host to the container + docker cp docs/sources/. docs-builder:/hugo/content/docs/grafana/latest/ + + # Run the make prod command inside the container + docker exec -w /hugo docs-builder make prod || echo "Build completed with warnings" + + # Clean up the container + docker rm -f docs-builder diff --git a/.github/workflows/metrics-collector.yml b/.github/workflows/metrics-collector.yml index 2e22a830a88..281dc6fb350 100644 --- a/.github/workflows/metrics-collector.yml +++ b/.github/workflows/metrics-collector.yml @@ -15,6 +15,9 @@ on: issues: types: [opened, closed] +permissions: + contents: read + jobs: config: runs-on: "ubuntu-latest" @@ -35,11 +38,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions - name: Run metrics collector diff --git a/.github/workflows/migrate-prs.yml b/.github/workflows/migrate-prs.yml index 31bb8f9f9da..c40a34a6ebb 100644 --- a/.github/workflows/migrate-prs.yml +++ b/.github/workflows/migrate-prs.yml @@ -51,7 +51,7 @@ jobs: app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: Migrate PRs - uses: grafana/grafana-github-actions-go/migrate-open-prs@main + uses: grafana/grafana-github-actions-go/migrate-open-prs@main # zizmor: ignore[unpinned-uses] with: token: ${{ steps.generate_token.outputs.token }} ownerRepo: ${{ inputs.ownerRepo }} diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml deleted file mode 100644 index f686dee7d55..00000000000 --- a/.github/workflows/milestone.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Close Milestone -on: - workflow_dispatch: - inputs: - version_input: - description: 'The version to be released please respect: major.minor.patch, major.minor.patch-preview or major.minor.patch-preview format. example: 7.4.3, 7.4.3-preview or 7.4.3-preview1' - required: true -jobs: - call-remove-milestone: - uses: grafana/grafana/.github/workflows/remove-milestone.yml@main - with: - version_call: ${{ github.event.inputs.version_input }} - secrets: inherit - call-close-milestone: - uses: grafana/grafana/.github/workflows/close-milestone.yml@main - with: - version_call: ${{ github.event.inputs.version_input }} - secrets: inherit - needs: call-remove-milestone diff --git a/.github/workflows/pr-backend-coverage.yml b/.github/workflows/pr-backend-coverage.yml new file mode 100644 index 00000000000..4d95497e3ec --- /dev/null +++ b/.github/workflows/pr-backend-coverage.yml @@ -0,0 +1,71 @@ +name: Coverage + +on: + workflow_dispatch: + push: + branches: + - main + paths-ignore: + - 'docs/**' + - '**/*.md' + +permissions: + contents: read + id-token: write + +env: + EDITION: 'oss' + WIRE_TAGS: 'oss' + +jobs: + main: + name: Backend Unit Tests + runs-on: ubuntu-latest-8-cores + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Setup Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: go.mod + cache: true + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential shared-mime-info + go install github.com/mfridman/tparse@c1754a1f484ac5cd422697b0fec635177ddc8507 # v0.17.0 + - name: Generate Go code + run: make gen-go + - name: Run unit tests + run: COVER_OPTS="-coverprofile=be-unit.cov -coverpkg=github.com/grafana/grafana/..." GO_TEST_OUTPUT="/tmp/unit.log" make test-go-unit-cov + - name: Process and upload coverage + uses: ./.github/actions/test-coverage-processor + with: + test-type: 'be-unit' + # Needs to be named 'unit.cov' based on the Makefile command `make test-go-unit` + coverage-file: 'unit.cov' + codecov-token: ${{ secrets.CODECOV_TOKEN }} + codecov-flag: 'be-unit' + codecov-name: 'be-unit' + + - name: Install Grafana Bench + # We can't allow forks here, as we need secret access. + if: ${{ github.event_name != 'pull_request' }} + uses: ./.github/actions/setup-grafana-bench + + - name: Process output for Bench + if: ${{ github.event_name != 'pull_request' }} + run: | + grafana-bench report \ + --trigger pr-backend-unit-tests-oss \ + --report-input go \ + --report-output log \ + --grafana-version "$(git rev-parse HEAD)" \ + --suite-name grafana-oss-unit-tests \ + /tmp/unit.log || true + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index ae2b0898f73..68317f57a0e 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -31,11 +31,12 @@ jobs: if: github.event.pull_request.draft == false steps: - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions - name: Run PR Checks diff --git a/.github/workflows/pr-codeql-analysis-go.yml b/.github/workflows/pr-codeql-analysis-go.yml deleted file mode 100644 index ce9082f4400..00000000000 --- a/.github/workflows/pr-codeql-analysis-go.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: "CodeQL for PR / go" - -on: - workflow_dispatch: - pull_request: - branches: [main] - paths: - - '**/*.go' - -permissions: - security-events: write - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - if: github.repository == 'grafana/grafana' - - steps: - - name: "Generate token" - id: generate_token - continue-on-error: true - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - - name: Checkout repository - uses: actions/checkout@v4 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - token: ${{ steps.generate_token.outputs.token }} - - - name: Set go version - uses: actions/setup-go@v4 - with: - go-version-file: go.mod - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: "go" - - - name: Build go files - run: | - go mod verify - make build-go - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/pr-codeql-analysis-javascript.yml b/.github/workflows/pr-codeql-analysis-javascript.yml index 6c5264c926a..43457ad4af0 100644 --- a/.github/workflows/pr-codeql-analysis-javascript.yml +++ b/.github/workflows/pr-codeql-analysis-javascript.yml @@ -20,17 +20,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: "javascript" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr-codeql-analysis-python.yml b/.github/workflows/pr-codeql-analysis-python.yml index aea55365afc..992a886497d 100644 --- a/.github/workflows/pr-codeql-analysis-python.yml +++ b/.github/workflows/pr-codeql-analysis-python.yml @@ -18,17 +18,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. fetch-depth: 2 + persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: "python" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/pr-commands.yml b/.github/workflows/pr-commands.yml index 51838dc7ae7..db93ad7e70b 100644 --- a/.github/workflows/pr-commands.yml +++ b/.github/workflows/pr-commands.yml @@ -30,11 +30,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Actions - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: repository: "grafana/grafana-github-actions" path: ./actions ref: main + persist-credentials: false - name: Install Actions run: npm install --production --prefix ./actions - name: "Generate token" diff --git a/.github/workflows/pr-dependabot-update-go-workspace.yml b/.github/workflows/pr-dependabot-update-go-workspace.yml new file mode 100644 index 00000000000..48a005ba6f7 --- /dev/null +++ b/.github/workflows/pr-dependabot-update-go-workspace.yml @@ -0,0 +1,69 @@ +name: "Update Go Workspace for Dependabot PRs" +on: + pull_request: + branches: [main] + paths: + - .github/workflows/pr-dependabot-update-go-workspace.yml + - go.mod + - go.sum + - go.work + - go.work.sum + - '**/go.mod' + - '**/go.sum' + - '**.go' +permissions: + contents: write + id-token: write +jobs: + update: + runs-on: "ubuntu-latest" + if: ${{ github.actor == 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository }} + continue-on-error: true + steps: + - name: Retrieve GitHub App secrets + id: get-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@get-vault-secrets-v1.0.1 # zizmor: ignore[unpinned-uses] + with: + repo_secrets: | + APP_ID=grafana-go-workspace-bot:app-id + APP_INSTALLATION_ID=grafana-go-workspace-bot:app-installation-id + PRIVATE_KEY=grafana-go-workspace-bot:private-key + + - name: Generate GitHub App token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ env.APP_ID }} + private-key: ${{ env.PRIVATE_KEY }} + + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + repository: ${{ github.event.pull_request.head.repo.full_name }} + ref: ${{ github.event.pull_request.head.ref }} + token: ${{ steps.generate_token.outputs.token }} + persist-credentials: false + + - name: Set go version + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 + with: + go-version-file: go.mod + + - name: Configure Git + run: | + git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git config --local --add --bool push.autoSetupRemote true + + - name: Update workspace + run: make update-workspace + + - name: Commit and push workspace changes + env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + run: | + if ! git diff --exit-code --quiet; then + echo "Committing and pushing workspace changes" + git commit -a -m "update workspace" + git push origin $BRANCH_NAME + fi diff --git a/.github/workflows/pr-e2e-tests.yml b/.github/workflows/pr-e2e-tests.yml new file mode 100644 index 00000000000..74e64e5bf38 --- /dev/null +++ b/.github/workflows/pr-e2e-tests.yml @@ -0,0 +1,72 @@ +name: End-to-end tests + +on: + pull_request: + push: + branches: + - main + - release-*.*.* + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + build-grafana: + name: Build & Package Grafana + runs-on: ubuntu-latest-16-cores + outputs: + artifact: ${{ steps.artifact.outputs.artifact }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + repository: 'grafana/grafana-build' + ref: 'main' + persist-credentials: false + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + path: ./grafana + - run: echo "GRAFANA_GO_VERSION=$(grep "go 1." grafana/go.work | cut -d\ -f2)" >> "$GITHUB_ENV" + - uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e + with: + verb: run + args: go run ./cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir=grafana --go-version=${GRAFANA_GO_VERSION} > out.txt + - run: mv $(cat out.txt) grafana.tar.gz + - run: echo "artifact=grafana-e2e-${{github.run_number}}" >> "$GITHUB_OUTPUT" + id: artifact + - uses: actions/upload-artifact@v4 + id: upload + with: + retention-days: 1 + name: ${{ steps.artifact.outputs.artifact }} + path: grafana.tar.gz + e2e-matrix: + name: ${{ matrix.suite }} + strategy: + matrix: + suite: + - various-suite + - dashboards-suite + - smoke-tests-suite + - panels-suite + needs: + - build-grafana + uses: ./.github/workflows/run-e2e-suite.yml + with: + package: ${{ needs.build-grafana.outputs.artifact }} + suite: ${{ matrix.suite }} + e2e-matrix-old-arch: + name: ${{ matrix.suite }} (old arch) + strategy: + matrix: + suite: + - old-arch/various-suite + - old-arch/dashboards-suite + - old-arch/smoke-tests-suite + - old-arch/panels-suite + needs: + - build-grafana + uses: ./.github/workflows/run-e2e-suite.yml + with: + package: ${{ needs.build-grafana.outputs.artifact }} + suite: ${{ matrix.suite }} diff --git a/.github/workflows/pr-frontend-unit-tests.yml b/.github/workflows/pr-frontend-unit-tests.yml new file mode 100644 index 00000000000..e26e3847f8c --- /dev/null +++ b/.github/workflows/pr-frontend-unit-tests.yml @@ -0,0 +1,69 @@ +name: Frontend tests +on: + pull_request: + push: + branches: + - main + - release-*.*.* + +permissions: {} + +jobs: + frontend-unit-tests: + permissions: + contents: read + id-token: write + # Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`, + # the `frontend-unit-tests-enterprise` workflow will run instead + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true + runs-on: ubuntu-latest-8-cores + name: "Unit tests (${{ matrix.chunk }} / 8)" + strategy: + fail-fast: false + matrix: + chunk: [1, 2, 3, 4, 5, 6, 7, 8] + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - run: yarn install --immutable --check-cache + - run: yarn run test:ci + env: + TEST_MAX_WORKERS: 2 + TEST_SHARD: ${{ matrix.chunk }} + TEST_SHARD_TOTAL: 8 + + frontend-unit-tests-enterprise: + permissions: + contents: read + id-token: write + # Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks) + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false + runs-on: ubuntu-latest-8-cores + name: "Unit tests (${{ matrix.chunk }} / 8)" + strategy: + fail-fast: false + matrix: + chunk: [1, 2, 3, 4, 5, 6, 7, 8] + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: '.nvmrc' + cache: 'yarn' + cache-dependency-path: 'yarn.lock' + - name: Setup Enterprise + uses: ./.github/actions/setup-enterprise + with: + github-app-name: 'grafana-ci-bot' + - run: yarn install --immutable --check-cache + - run: yarn run test:ci + env: + TEST_MAX_WORKERS: 2 + TEST_SHARD: ${{ matrix.chunk }} + TEST_SHARD_TOTAL: 8 diff --git a/.github/workflows/pr-go-workspace-check.yml b/.github/workflows/pr-go-workspace-check.yml index bc5385ef0e0..d54178f2e35 100644 --- a/.github/workflows/pr-go-workspace-check.yml +++ b/.github/workflows/pr-go-workspace-check.yml @@ -4,6 +4,15 @@ on: workflow_dispatch: pull_request: branches: [main] + paths: + - .github/workflows/pr-go-workspace-check.yml + - go.mod + - go.sum + - go.work + - go.work.sum + - '**/go.mod' + - '**/go.sum' + - '**.go' jobs: check: @@ -12,11 +21,14 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: + cache: false go-version-file: go.mod - name: Update workspace @@ -32,4 +44,4 @@ jobs: exit 1 fi - name: Ensure Dockerfile contains submodule COPY commands - run: ./scripts/go-workspace/validate-dockerfile.sh \ No newline at end of file + run: ./scripts/go-workspace/validate-dockerfile.sh diff --git a/.github/workflows/pr-k8s-codegen-check.yml b/.github/workflows/pr-k8s-codegen-check.yml index 71dd29b6354..9a72f469caa 100644 --- a/.github/workflows/pr-k8s-codegen-check.yml +++ b/.github/workflows/pr-k8s-codegen-check.yml @@ -9,6 +9,7 @@ on: - "pkg/aggregator/apis/**" - "pkg/apimachinery/apis/**" - "hack/**" + - "apps/**" - "*.sum" jobs: @@ -18,10 +19,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false - name: Set go version - uses: actions/setup-go@v4 + uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639 with: go-version-file: go.mod @@ -35,4 +38,4 @@ jobs: git diff echo "Please run './hack/update-codegen.sh' and commit the changes." exit 1 - fi \ No newline at end of file + fi diff --git a/.github/workflows/pr-patch-check-event.yml b/.github/workflows/pr-patch-check-event.yml index 03dd31a7539..f7605f033ce 100644 --- a/.github/workflows/pr-patch-check-event.yml +++ b/.github/workflows/pr-patch-check-event.yml @@ -3,7 +3,7 @@ name: Dispatch check for patch conflicts run-name: dispatch-check-patch-conflicts-${{ github.base_ref }}-${{ github.head_ref }} on: - pull_request: + pull_request_target: types: - opened - reopened @@ -13,10 +13,22 @@ on: - "v*.*.*" - "release-*" +permissions: {} + # Since this is run on a pull request, we want to apply the patches intended for the # target branch onto the source branch, to verify compatibility before merging. jobs: dispatch-job: + permissions: + contents: read + actions: write + env: + HEAD_REF: ${{ github.head_ref }} + BASE_REF: ${{ github.base_ref }} + REPO: ${{ github.repository }} + SENDER: ${{ github.event.sender.login }} + SHA: ${{ github.sha }} + PR_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} runs-on: ubuntu-latest steps: - name: "Generate token" @@ -26,24 +38,25 @@ jobs: # App needs Actions: Read/Write for the grafana/security-patch-actions repo app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: "Dispatch job" uses: actions/github-script@v7 with: github-token: ${{ steps.generate_token.outputs.token }} script: | + const {HEAD_REF, BASE_REF, REPO, SENDER, SHA, PR_COMMIT_SHA} = process.env; + await github.rest.actions.createWorkflowDispatch({ owner: 'grafana', repo: 'security-patch-actions', workflow_id: 'test-patches-event.yml', ref: 'main', inputs: { - src_repo: "${{ github.repository }}", - src_ref: "${{ github.head_ref }}", - src_merge_sha: "${{ github.sha }}", - src_pr_commit_sha: "${{ github.event.pull_request.head.sha }}", - patch_repo: "${{ github.repository }}-security-patches", - patch_ref: "${{ github.base_ref }}", - triggering_github_handle: "${{ github.event.sender.login }}" + src_repo: REPO, + src_ref: HEAD_REF, + src_merge_sha: SHA, + src_pr_commit_sha: PR_COMMIT_SHA, + patch_repo: REPO + '-security-patches', + patch_ref: BASE_REF, + triggering_github_handle: SENDER } }) diff --git a/.github/workflows/pr-test-integration.yml b/.github/workflows/pr-test-integration.yml new file mode 100644 index 00000000000..b49183889d5 --- /dev/null +++ b/.github/workflows/pr-test-integration.yml @@ -0,0 +1,89 @@ +name: Integration Tests + +on: + push: + branches: + - main + - release-*.*.* + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} + +jobs: + sqlite: + name: Sqlite + runs-on: ubuntu-latest-8-cores + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Setup Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: go.mod + cache: true + - run: | + make gen-go + go test -tags=sqlite -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) + mysql: + name: MySQL + runs-on: ubuntu-latest-8-cores + env: + GRAFANA_TEST_DB: mysql + MYSQL_HOST: 127.0.0.1 + services: + mysql: + image: mysql:8.0.32 + env: + MYSQL_ROOT_PASSWORD: rootpass + MYSQL_DATABASE: grafana_tests + MYSQL_USER: grafana + MYSQL_PASSWORD: password + options: --health-cmd="mysqladmin ping --silent" --health-interval=10s --health-timeout=5s --health-retries=3 + ports: + - 3306:3306 + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Setup Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: go.mod + cache: true + - run: | + sudo apt-get update -yq && sudo apt-get install mariadb-client + cat devenv/docker/blocks/mysql_tests/setup.sql | mariadb -h 127.0.0.1 -P 3306 -u root -prootpass --disable-ssl-verify-server-cert + make gen-go + go test -tags=mysql -p=1 -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) + postgres: + name: Postgres + runs-on: ubuntu-latest-8-cores + services: + postgres: + image: postgres:12.3-alpine + env: + POSTGRES_USER: grafanatest + POSTGRES_PASSWORD: grafanatest + POSTGRES_DB: grafanatest + ports: + - 5432:5432 + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Setup Go + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: go.mod + cache: true + - env: + GRAFANA_TEST_DB: postgres + PGPASSWORD: grafanatest + POSTGRES_HOST: 127.0.0.1 + run: | + sudo apt-get update -yq && sudo apt-get install postgresql-client + psql -p 5432 -h 127.0.0.1 -U grafanatest -d grafanatest -f devenv/docker/blocks/postgres_tests/setup.sql + make gen-go + go test -p=1 -tags=postgres -timeout=5m -run '^TestIntegration' $(find ./pkg -type f -name '*_test.go' -exec grep -l '^func TestIntegration' '{}' '+' | grep -o '\(.*\)/' | sort -u) diff --git a/.github/workflows/publish-kinds-next.yml b/.github/workflows/publish-kinds-next.yml index 4aed6cf36c3..ed290abbd2d 100644 --- a/.github/workflows/publish-kinds-next.yml +++ b/.github/workflows/publish-kinds-next.yml @@ -29,12 +29,13 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout Grafana repo" - uses: "actions/checkout@v4" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" with: fetch-depth: 0 + persist-credentials: false - name: "Setup Go" - uses: "actions/setup-go@v4" + uses: "actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639" with: go-version-file: go.mod diff --git a/.github/workflows/publish-kinds-release.yml b/.github/workflows/publish-kinds-release.yml index 691cdff3867..1e60f72ed79 100644 --- a/.github/workflows/publish-kinds-release.yml +++ b/.github/workflows/publish-kinds-release.yml @@ -31,13 +31,14 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout Grafana repo" - uses: "actions/checkout@v4" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" with: # required for the `grafana/grafana-github-actions/has-matching-release-tag` action to work fetch-depth: 0 + persist-credentials: false - name: "Setup Go" - uses: "actions/setup-go@v4" + uses: "actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639" with: go-version-file: go.mod @@ -45,7 +46,7 @@ jobs: run: go run .github/workflows/scripts/kinds/verify-kinds.go - name: "Checkout Actions library" - uses: "actions/checkout@v4" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" with: repository: "grafana/grafana-github-actions" path: "./actions" diff --git a/.github/workflows/publish-technical-documentation-next.yml b/.github/workflows/publish-technical-documentation-next.yml index 6b2cd7489b3..9d67f1724c5 100644 --- a/.github/workflows/publish-technical-documentation-next.yml +++ b/.github/workflows/publish-technical-documentation-next.yml @@ -15,7 +15,7 @@ jobs: id-token: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 # zizmor: ignore[unpinned-uses] with: website_directory: content/docs/grafana/next diff --git a/.github/workflows/publish-technical-documentation-release.yml b/.github/workflows/publish-technical-documentation-release.yml index 57d779660c5..d5f597686ca 100644 --- a/.github/workflows/publish-technical-documentation-release.yml +++ b/.github/workflows/publish-technical-documentation-release.yml @@ -17,10 +17,11 @@ jobs: id-token: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: fetch-depth: 0 - - uses: grafana/writers-toolkit/publish-technical-documentation-release@publish-technical-documentation-release/v2 + persist-credentials: false + - uses: grafana/writers-toolkit/publish-technical-documentation-release@publish-technical-documentation-release/v2 # zizmor: ignore[unpinned-uses] with: release_tag_regexp: "^v(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" release_branch_regexp: "^release-(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$" diff --git a/.github/workflows/release-comms.yml b/.github/workflows/release-comms.yml index 008b6552883..e5532ca12ca 100644 --- a/.github/workflows/release-comms.yml +++ b/.github/workflows/release-comms.yml @@ -30,18 +30,16 @@ jobs: release_branch: ${{ steps.output.outputs.release_branch }} dry_run: ${{ steps.output.outputs.dry_run }} latest: ${{ steps.output.outputs.latest }} + env: + HEAD_REF: ${{ github.head_ref }} + DRY_RUN: ${{ inputs.dry_run }} + LATEST: ${{ inputs.latest && '1' || '0' }} + VERSION: ${{ inputs.version }} runs-on: ubuntu-latest steps: - # The github-release action expects a `LATEST` value of a string of either '1' or '0' - - if: ${{ github.event_name == 'workflow_dispatch' }} - run: | - echo setting up GITHUB_ENV for ${{ github.event_name }} - echo "VERSION=${{ inputs.version }}" >> $GITHUB_ENV - echo "DRY_RUN=${{ inputs.dry_run }}" >> $GITHUB_ENV - echo "LATEST=${{ inputs.latest && '1' || '0' }}" >> $GITHUB_ENV - if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') }} run: | - echo "VERSION=$(echo ${{ github.head_ref }} | sed -e 's/release\/.*\//v/g')" >> $GITHUB_ENV + echo "VERSION=$(echo ${HEAD_REF} | sed -e 's/release\/.*\//v/g')" >> $GITHUB_ENV echo "DRY_RUN=${{ contains(github.event.pull_request.labels.*.name, 'release/dry-run') }}" >> $GITHUB_ENV echo "LATEST=${{ contains(github.event.pull_request.labels.*.name, 'release/latest') && '1' || '0' }}" >> $GITHUB_ENV - id: output @@ -120,7 +118,10 @@ jobs: post_on_slack: needs: setup runs-on: ubuntu-latest + env: + DRY_RUN: ${{ needs.setup.outputs.dry_run }} + VERSION: ${{ needs.setup.outputs.version }} steps: - run: | - echo announce on slack that ${{ needs.setup.outputs.version }} has been released - echo dry run: ${{ needs.setup.outputs.dry_run }} + echo announce on slack that $VERSION has been released + echo dry run: $DRY_RUN diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 004d1ab3299..4d66838b7ec 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -33,12 +33,13 @@ on: default: false type: boolean -permissions: - contents: write - pull-requests: write +permissions: {} jobs: push-changelog-to-main: + permissions: + contents: write + pull-requests: write name: Create PR to main to update the changelog uses: ./.github/workflows/changelog.yml with: @@ -50,39 +51,44 @@ jobs: secrets: GRAFANA_DELIVERY_BOT_APP_ID: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} GRAFANA_DELIVERY_BOT_APP_PEM: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} + create-prs: + permissions: + contents: write + pull-requests: write name: Create Release PR runs-on: ubuntu-latest if: github.repository == 'grafana/grafana' + env: + VERSION: ${{ inputs.version }} + LATEST: ${{ inputs.latest }} + DRY_RUN: ${{ inputs.dry_run }} steps: - - name: Generate bot token - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: Get release branch id: branch - uses: grafana/grafana-github-actions-go/latest-release-branch@main + uses: grafana/grafana-github-actions-go/latest-release-branch@main # zizmor: ignore[unpinned-uses] with: - token: ${{ steps.generate_token.outputs.token }} + token: ${{ secrets.GITHUB_TOKEN }} ownerRepo: 'grafana/grafana' pattern: ${{ inputs.target }} - name: Checkout Grafana - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: ref: ${{ steps.branch.outputs.branch }} - fetch-depth: 0 fetch-tags: true + token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Checkout Grafana (main) - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: ref: main fetch-depth: '0' fetch-tags: 'false' path: .grafana-main + token: ${{ secrets.GITHUB_TOKEN }} + persist-credentials: false - name: Setup nodejs environment - uses: actions/setup-node@v4 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e with: node-version-file: .nvmrc - name: Configure git user @@ -92,37 +98,43 @@ jobs: git config --local --add --bool push.autoSetupRemote true - name: Create branch - run: git checkout -b "release/${{ github.run_id }}/${{ inputs.version }}" + run: git checkout -b "release/${{ github.run_id }}/$VERSION" + - name: Generate changelog token + id: generate_changelog_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} + private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - name: Generate changelog id: changelog uses: ./.grafana-main/.github/workflows/actions/changelog with: - github_token: ${{ steps.generate_token.outputs.token }} - target: v${{ inputs.version }} + github_token: ${{ steps.generate_changelog_token.outputs.token }} + target: v${{ env.VERSION }} output_file: changelog_items.md - name: Patch CHANGELOG.md run: | # Prepare CHANGELOG.md content with version delimiters ( echo - echo "# ${{ inputs.version}} ($(date '+%F'))" + echo "# $VERSION ($(date '+%F'))" echo cat changelog_items.md ) > CHANGELOG.part # Check if a version exists in the changelog - if grep -q "" + echo "" cat CHANGELOG.part - echo "" + echo "" cat CHANGELOG.md ) > CHANGELOG.tmp mv CHANGELOG.tmp CHANGELOG.md @@ -144,35 +156,46 @@ jobs: - name: Add package.json changes run: | git add package.json lerna.json yarn.lock packages public - git commit -m "Update version to ${{ inputs.version }}" + test -e e2e/test-plugins && git add e2e/test-plugins + git commit -m "Update version to $VERSION" - name: Git push if: ${{ inputs.dry_run }} != true - run: git push --set-upstream origin release/${{ github.run_id }}/${{ inputs.version }} + run: git push --set-upstream origin "release/${{ github.run_id }}/$VERSION" - name: Create PR without backports if: "${{ inputs.backport == '' }}" - run: > + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: ${{ steps.branch.outputs.branch }} + run: | + LATEST_FLAG="" + if [ "$LATEST" = "true" ]; then + LATEST_FLAG='-l "release/latest"' + fi gh pr create \ - $( [ "x${{ inputs.latest }}" == "xtrue" ] && printf %s '-l "release/latest"') \ + $LATEST_FLAG \ -l "no-changelog" \ - --dry-run=${{ inputs.dry_run }} \ - -B "${{ steps.branch.outputs.branch }}" \ - --title "Release: ${{ inputs.version }}" \ + --dry-run="$DRY_RUN" \ + -B "$BRANCH" \ + --title "Release: $VERSION" \ --body "These code changes must be merged after a release is complete" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create PR with backports if: "${{ inputs.backport != '' }}" - run: > + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: ${{ steps.branch.outputs.branch }} + run: | + LATEST_FLAG="" + if [ "$LATEST" = "true" ]; then + LATEST_FLAG='-l "release/latest"' + fi gh pr create \ - $( [ "x${{ inputs.latest }}" == "xtrue" ] && printf %s '-l "release/latest"') \ + $LATEST_FLAG \ -l "product-approved" \ -l "no-changelog" \ - --dry-run=${{ inputs.dry_run }} \ - -B "${{ steps.branch.outputs.branch }}" \ - --title "Release: ${{ inputs.version }}" \ + --dry-run="$DRY_RUN" \ + -B "$BRANCH" \ + --title "Release: $VERSION" \ --body "These code changes must be merged after a release is complete" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/remove-milestone.yml b/.github/workflows/remove-milestone.yml deleted file mode 100644 index d41b63f1f51..00000000000 --- a/.github/workflows/remove-milestone.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Remove milestone -on: - workflow_dispatch: - inputs: - version: - required: true - description: Needs to match, exactly, the name of a milestone - workflow_call: - inputs: - version_call: - description: Needs to match, exactly, the name of a milestone - required: true - type: string - -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' && secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '') || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - main: - needs: config - if: needs.config.outputs.has-secrets - permissions: - issues: write - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v4 - with: - repository: "grafana/grafana-github-actions" - path: ./actions - ref: main - - name: Install Actions - run: npm install --production --prefix ./actions - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: Remove milestone from open issues (manually invoked) - if: ${{ github.event.inputs.version != '' }} - uses: ./actions/remove-milestone - with: - token: ${{ steps.generate_token.outputs.token }} - - name: Remove milestone from open issues (workflow invoked) - if: ${{ inputs.version_call != '' }} - uses: ./actions/remove-milestone - with: - version_call: ${{ inputs.version_call }} - token: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/run-dashboard-search-e2e.yml b/.github/workflows/run-dashboard-search-e2e.yml new file mode 100644 index 00000000000..7d59bd49fb5 --- /dev/null +++ b/.github/workflows/run-dashboard-search-e2e.yml @@ -0,0 +1,130 @@ +name: run-dashboard-search-e2e + +on: + workflow_run: + workflows: + - trigger-dashboard-search-e2e + types: + - completed + workflow_dispatch: + +env: + ARCH: linux-amd64 + +permissions: {} + +jobs: + setup: + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + outputs: + ini_files: ${{ steps.get_files.outputs.ini_files }} + + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Pin Go version to mod file + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: 'go.mod' + cache: true + - run: go version + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version: 20 + cache: 'yarn' + - name: Cache Node Modules + id: cache-node-modules + uses: actions/cache@v3 + with: + path: | + node_modules + /home/runner/.cache/Cypress + key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: yarn install --immutable + - name: Install Cypress dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f + with: + runTests: false + - name: Cache Grafana Build and Dependencies + id: cache-grafana + uses: actions/cache@v3 + with: + path: | + bin/ + scripts/grafana-server/ + tools/ + public/ + conf/ + e2e/test-plugins/ + devenv/ + key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }} + # only rebuild grafana if search files have changed ( or dependencies ) + - name: Build Grafana (Runs Only If Not Cached) + if: steps.cache-grafana.outputs.cache-hit != 'true' + run: make build + + - name: Get list of .ini files + id: get_files + run: | + INI_FILES=$(ls ${{ github.workspace }}/e2e/dashboards-search-suite/*.ini | jq -R -s -c 'split("\n")[:-1]') + echo "ini_files=$INI_FILES" >> $GITHUB_OUTPUT + shell: bash + + run_tests: + needs: setup + runs-on: ubuntu-latest + continue-on-error: true + if: github.event.pull_request.draft == false + strategy: + matrix: + ini_file: ${{ fromJson(needs.setup.outputs.ini_files) }} + + permissions: + contents: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Restore Cached Node Modules + uses: actions/cache@v3 + with: + path: | + node_modules + /home/runner/.cache/Cypress + key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }} + + - name: Restore Cached Grafana Build and Dependencies + uses: actions/cache@v3 + with: + path: | + bin/ + scripts/grafana-server/ + tools/ + public/ + conf/ + e2e/test-plugins/ + devenv/ + key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }} + - name: Set the step name + id: set_file_name + env: + INI_NAME: ${{ matrix.ini_file }} + run: | + FILE_NAME=$(basename "$env.INI_NAME" .ini) + echo "FILE_NAME=$FILE_NAME" >> $GITHUB_OUTPUT + - name: Run tests for ${{ steps.set_file_name.outputs.FILE_NAME }} + env: + INI_NAME: ${{ matrix.ini_file }} + run: | + cp -rf $INI_NAME ${{ github.workspace }}/scripts/grafana-server/custom.ini + yarn e2e:dashboards-search || echo "Test failed but marking as success since unified search is behind a feature flag and should not block PRs" diff --git a/.github/workflows/run-e2e-suite.yml b/.github/workflows/run-e2e-suite.yml new file mode 100644 index 00000000000..b99d0ea0b30 --- /dev/null +++ b/.github/workflows/run-e2e-suite.yml @@ -0,0 +1,39 @@ +name: e2e suite + +on: + workflow_call: + inputs: + package: + type: string + required: true + suite: + type: string + required: true + +jobs: + main: + runs-on: ubuntu-latest-8-cores + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - uses: actions/download-artifact@v4 + with: + name: ${{ inputs.package }} + - uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e + if: inputs.old-arch == false + with: + verb: run + args: go run ./pkg/build/e2e --package=grafana.tar.gz --suite=${{ inputs.suite }} + - name: Set suite name + id: set-suite-name + env: + SUITE: ${{ inputs.suite }} + run: | + echo "suite=$(echo $SUITE | sed 's/\//-/g')" >> $GITHUB_OUTPUT + - uses: actions/upload-artifact@v4 + if: ${{ always() && inputs.old-arch != true }} + with: + name: e2e-${{ steps.set-suite-name.outputs.suite }}-${{github.run_number}} + path: videos + retention-days: 1 diff --git a/.github/workflows/run-schema-v2-e2e.yml b/.github/workflows/run-schema-v2-e2e.yml new file mode 100644 index 00000000000..62975992acb --- /dev/null +++ b/.github/workflows/run-schema-v2-e2e.yml @@ -0,0 +1,46 @@ +name: Run dashboard schema v2 e2e + +on: + push: + branches: + - main + pull_request: + branches: + - '**' + +env: + ARCH: linux-amd64 + +jobs: + dashboard-schema-v2-e2e: + runs-on: ubuntu-latest + continue-on-error: true + if: github.event.pull_request.draft == false + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - name: Pin Go version to mod file + uses: actions/setup-go@111f3307d8850f501ac008e886eec1fd1932a34 + with: + go-version-file: 'go.mod' + - run: go version + - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version: 20 + cache: 'yarn' + - name: Install dependencies + run: yarn install --immutable + - name: Build grafana + run: make build + - name: Install Cypress dependencies + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f + with: + runTests: false + - name: Run dashboard scenes e2e + run: yarn e2e:schema-v2 || echo "Test failed but marking as success since schema V2 is behind a feature flag and should not block PRs" + + - name: Always succeed # This is a workaround to make the job pass even if the previous step fails + if: failure() + run: exit 0 diff --git a/.github/workflows/sbom-report.yml b/.github/workflows/sbom-report.yml deleted file mode 100644 index c913b76331a..00000000000 --- a/.github/workflows/sbom-report.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: syft-sbom-ci - -on: - release: - types: [created] - -jobs: - syft-sbom: - - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Anchore SBOM Action - uses: anchore/sbom-action@v0.14.2 - with: - artifact-name: ${{ github.event.repository.name }}-spdx.json - diff --git a/.github/workflows/scripts/crowdin/create-tasks.js b/.github/workflows/scripts/crowdin/create-tasks.js new file mode 100644 index 00000000000..d3085f8afa0 --- /dev/null +++ b/.github/workflows/scripts/crowdin/create-tasks.js @@ -0,0 +1,84 @@ +const crowdin = require('@crowdin/crowdin-api-client'); +const TRANSLATED_CONNECTOR_DESCRIPTION = '{{tos_service_type: premium}}'; + +const API_TOKEN = process.env.CROWDIN_PERSONAL_TOKEN; +if (!API_TOKEN) { + console.error('Error: CROWDIN_PERSONAL_TOKEN environment variable is not set'); + process.exit(1); +} + +const PROJECT_ID = process.env.CROWDIN_PROJECT_ID; +if (!PROJECT_ID) { + console.error('Error: CROWDIN_PROJECT_ID environment variable is not set'); + process.exit(1); +} + +const { tasksApi, projectsGroupsApi, sourceFilesApi } = new crowdin.default({ + token: API_TOKEN, + organization: 'grafana' +}); + +const languages = await getLanguages(); +const fileIds = await getFileIds(); +console.log('Languages: ', languages); +console.log('File IDs: ', fileIds); + +// for (const language of languages) { +// const { name, id } = language; +// await createTask(`Translate to ${name}`, id, fileIds); +// } + +async function getLanguages() { + try { + const project = await projectsGroupsApi.getProject(PROJECT_ID); + const languages = project.data.targetLanguages; + return languages; + } catch (error) { + console.error('Failed to fetch languages: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function getFileIds() { + try { + const response = await sourceFilesApi.listProjectFiles(PROJECT_ID); + const files = response.data; + const fileIds = files.map(file => file.data.id); + return fileIds; + } catch (error) { + console.error('Failed to fetch file IDs: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} + +async function createTask(title, languageId, fileIds) { + try { + const taskParams = { + title, + description: TRANSLATED_CONNECTOR_DESCRIPTION, + languageId, + type: 2, // Translation by vendor + workflowStepId: 78, // Translation step ID + skipAssignedStrings: true, + fileIds, + }; + + console.log(`Creating Crowdin task: "${title}" for language ${languageId}`); + + const response = await tasksApi.addTask(PROJECT_ID, taskParams); + console.log(`Task created successfully! Task ID: ${response.data.id}`); + return response.data; + } catch (error) { + console.error('Failed to create Crowdin task: ', error.message); + if (error.response && error.response.data) { + console.error('Error details: ', JSON.stringify(error.response.data, null, 2)); + } + process.exit(1); + } +} diff --git a/.github/workflows/scripts/pr-get-job-link.js b/.github/workflows/scripts/pr-get-job-link.js deleted file mode 100644 index b1b49c89941..00000000000 --- a/.github/workflows/scripts/pr-get-job-link.js +++ /dev/null @@ -1,9 +0,0 @@ - -module.exports = async ({ name, github, context, core }) => { - const { owner, repo } = context.repo; - const url = `https://api.github.com/repos/${owner}/${repo}/actions/runs/${context.runId}/jobs` - const result = await github.request(url); - const job = result.data.jobs.find(j => j.name === name); - - core.setOutput('link', `${job.html_url}?check_suite_focus=true`); -} diff --git a/.github/workflows/skye-add-to-project.yml b/.github/workflows/skye-add-to-project.yml new file mode 100644 index 00000000000..321f4c40b2a --- /dev/null +++ b/.github/workflows/skye-add-to-project.yml @@ -0,0 +1,106 @@ +name: Add issues and PRs to Skye project board +on: + workflow_dispatch: + inputs: + manual_issue_number: + description: 'Issue/PR number to add to project' + required: false + type: number + issues: + types: [opened] + pull_request: + types: [opened] + +permissions: + contents: read + id-token: write + +env: + ORGANIZATION: grafana + REPO: grafana + PROJECT_ID: "PVT_kwDOAG3Mbc4AxfcI" # Retrieved manually from GitHub GraphQL Explorer + +concurrency: + group: skye-add-to-project-${{ github.event.number }} + +jobs: + main: + if: github.repository == 'grafana/grafana' + runs-on: ubuntu-latest + steps: + - name: "Get vault secrets" + id: vault-secrets + uses: grafana/shared-workflows/actions/get-vault-secrets@main # zizmor: ignore[unpinned-uses] + with: + # Vault secret paths: + # - ci/repo/grafana/grafana/plugins_platform_issue_commands_github_bot + # - ci/repo/grafana/grafana/frontend_platform_skye_usernames (comma separated list of usernames) + repo_secrets: | + GH_APP_ID=plugins_platform_issue_commands_github_bot:app_id + GH_APP_PEM=plugins_platform_issue_commands_github_bot:app_pem + ALLOWED_USERS=frontend_platform_skye_usernames:allowed_users + + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 + with: + app_id: ${{ env.GH_APP_ID }} + private_key: ${{ env.GH_APP_PEM }} + + # Check if the user is in the list from the secret + - name: Check if user is allowed + id: check_user + env: + ALLOWED_USERS: ${{ env.ALLOWED_USERS }} + USERNAME: ${{ github.event.sender.login }} + run: | + # Convert the comma-separated list to an array + IFS=',' read -ra ALLOWED_USERS <<< "$ALLOWED_USERS" + + # Check if user is in the allowed list + for allowed_user in "${ALLOWED_USERS[@]}"; do + if [ "$allowed_user" = "$USERNAME" ]; then + echo "user_allowed=true" >> $GITHUB_OUTPUT + exit 0 + fi + done + echo "user_allowed=false" >> $GITHUB_OUTPUT + + # Convert the issue/PR number to a node ID for the GraphQL API + - name: Get node ID for item + if: steps.check_user.outputs.user_allowed == 'true' + id: get_node_id + uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 + with: + query: | + query getNodeId($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + issueOrPullRequest(number: $number) { + ... on Issue { id } + ... on PullRequest { id } + } + } + } + variables: | + owner: ${{ env.ORGANIZATION }} + repo: ${{ env.REPO }} + number: ${{ github.event.number || github.event.inputs.manual_issue_number }} + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + + # Finally, add the issue/PR to the project board + - name: Add to project board + if: steps.check_user.outputs.user_allowed == 'true' + uses: octokit/graphql-action@51bf543c240dcd14761320e2efc625dc32ec0d32 + with: + query: | + mutation addItem($projectid: ID!, $itemid: ID!) { + addProjectV2ItemById(input: {projectId: $projectid, contentId: $itemid}) { + item { id } + } + } + variables: | + projectid: ${{ env.PROJECT_ID }} + itemid: ${{ fromJSON(steps.get_node_id.outputs.data).repository.issueOrPullRequest.id }} + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} diff --git a/.github/workflows/storybook-verification.yml b/.github/workflows/storybook-verification.yml new file mode 100644 index 00000000000..99f0c6dfe82 --- /dev/null +++ b/.github/workflows/storybook-verification.yml @@ -0,0 +1,48 @@ +name: Verify Storybook + +on: + pull_request: + paths: + - 'packages/grafana-ui/**' + - '!docs/**' + - '!*.md' + push: + branches: + - main + paths: + - 'packages/grafana-ui/**' + - '!docs/**' + - '!*.md' + +jobs: + verify-storybook: + name: Verify Storybook + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e + with: + node-version-file: 'package.json' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --immutable + + - name: Run Storybook and E2E tests + uses: cypress-io/github-action@108b8684ae52e735ff7891524cbffbcd4be5b19f + with: + browser: chrome + start: yarn storybook --quiet + wait-on: 'http://localhost:9001' + wait-on-timeout: 60 + command: yarn e2e:storybook + install: false + env: + HOST: localhost + PORT: 9001 diff --git a/.github/workflows/sync-mirror-event.yml b/.github/workflows/sync-mirror-event.yml index b1a1466fdf9..b9387e02069 100644 --- a/.github/workflows/sync-mirror-event.yml +++ b/.github/workflows/sync-mirror-event.yml @@ -10,10 +10,21 @@ on: - "v*.*.*" - "release-*" +permissions: {} + # This is run after the pull request has been merged, so we'll run against the target branch jobs: dispatch-job: runs-on: ubuntu-latest + permissions: + contents: read + actions: write + env: + REF_NAME: ${{ github.ref_name }} + REPO: ${{ github.repository }} + SENDER: ${{ github.event.sender.login }} + SHA: ${{ github.sha }} + PR_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} steps: - name: "Generate token" id: generate_token @@ -28,16 +39,18 @@ jobs: with: github-token: ${{ steps.generate_token.outputs.token }} script: | + const {HEAD_REF, BASE_REF, REPO, SENDER, SHA} = process.env; + await github.rest.actions.createWorkflowDispatch({ owner: 'grafana', repo: 'security-patch-actions', workflow_id: 'mirror-branch-and-apply-patches-event.yml', ref: 'main', inputs: { - src_ref: "${{ github.ref_name }}", - src_repo: "${{ github.repository }}", - src_sha: "${{ github.sha }}", - dest_repo: "${{ github.repository }}-security-mirror", - patch_repo: "${{ github.repository }}-security-patches" + src_ref: REF_NAME, + src_repo: REPO, + src_sha: SHA, + dest_repo: REPO + "-security-mirror", + patch_repo: REPO + "-security-patches" } }) diff --git a/.github/workflows/trigger-dashboard-search-e2e.yml b/.github/workflows/trigger-dashboard-search-e2e.yml new file mode 100644 index 00000000000..db7025f17c1 --- /dev/null +++ b/.github/workflows/trigger-dashboard-search-e2e.yml @@ -0,0 +1,28 @@ +name: trigger-dashboard-search-e2e +# triggers the dashboard search e2e tests which runs async +# doesn't block prs, allows setting up notifications from grafana +on: + push: + branches: + - main + paths: + - public/app/features/search/**/*.ts + - public/app/features/search/**/*.tsx + - pkg/storage/**/*.go + pull_request: + branches: + - main + paths: + - public/app/features/search/**/*.ts + - public/app/features/search/**/*.tsx + - pkg/storage/**/*.go +env: + ARCH: linux-amd64 + +jobs: + trigger-search-e2e: + runs-on: ubuntu-latest + if: github.event.pull_request.draft == false + steps: + - name: Trigger Dashboard Search E2E + run: echo "Triggered Dashboard Search e2e..." \ No newline at end of file diff --git a/.github/workflows/trivy-scan.yml b/.github/workflows/trivy-scan.yml index f3a2c32f53e..921d25b76ff 100644 --- a/.github/workflows/trivy-scan.yml +++ b/.github/workflows/trivy-scan.yml @@ -4,48 +4,64 @@ on: # only run on PRs where go.mod/go.sum/etc have been updated paths: - go.* + - .github/workflows/trivy-scan.yml push: branches: - main paths: - go.* + - .github/workflows/trivy-scan.yml jobs: trivy-scan: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v4 - - name: Run Trivy vulnerability scanner (table output) - uses: aquasecurity/trivy-action@0.24.0 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 with: - # scan the filesystem, rather than building a Docker image prior - the - # downside is we won't catch dependencies that are only installed in the - # image, but the upside is we'll only catch vulnerabilities that are - # explicitly in the our dependencies - scan-type: 'fs' - scanners: 'vuln' - format: 'table' - exit-code: 1 - ignore-unfixed: true - vuln-type: 'os,library' - severity: 'CRITICAL,HIGH' - trivyignores: .trivyignore - # for the PR check, ignore JS-related issues - skip-files: 'yarn.lock,package.json' - - name: Run Trivy vulnerability scanner (SARIF) - uses: aquasecurity/trivy-action@0.24.0 + persist-credentials: false + - name: Install Trivy + uses: aquasecurity/setup-trivy@9ea583eb67910444b1f64abf338bd2e105a0a93d with: - scan-type: 'fs' - scanners: 'vuln' - # Note: The SARIF format ignores severity and uploads all vulns for - # later triage. The table-format step above is used to fail the build - # if there are any critical or high vulnerabilities. - # See https://github.com/aquasecurity/trivy-action/issues/95 - format: 'sarif' - output: 'trivy-results.sarif' - ignore-unfixed: true - vuln-type: 'os,library' - trivyignores: .trivyignore + version: v0.56.2 + cache: true + - name: Download Trivy DB + run: | + trivy fs --no-progress --download-db-only --db-repository public.ecr.aws/aquasecurity/trivy-db + - name: Run Trivy vulnerability scanner (table output) + # Use the trivy binary rather than the aquasecurity/trivy-action action + # to avoid a few bugs. + # + # We scan the file system rather than building the Docker image to only scan + # our direct dependencies. The Docker images are still scanned by + # Vulnerability Observability: + # - OSS: https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/projects/sources/1 + # - Enterprise: https://ops.grafana-ops.net/a/grafana-vulnerabilityobs-app/projects/sources/12 + # (If these links are outdated, just go to the list and find the images manually.) + run: | + trivy fs \ + --scanners vuln \ + --format table \ + --exit-code 1 \ + --ignore-unfixed \ + --pkg-types os,library \ + --severity CRITICAL,HIGH \ + --ignorefile .trivyignore \ + --skip-files yarn.lock,package.json \ + --skip-db-update \ + . + - name: Run Trivy vulnerability scanner (SARIF) + # Use the trivy binary rather than the aquasecurity/trivy-action action + # to avoid a few bugs + run: | + trivy fs \ + --scanners vuln \ + --format sarif \ + --output trivy-results.sarif \ + --ignore-unfixed \ + --pkg-types os,library \ + --ignorefile .trivyignore \ + --skip-db-update \ + . if: always() && github.repository == 'grafana/grafana' - name: Upload Trivy scan results to GitHub Security tab uses: github/codeql-action/upload-sarif@v3 diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml deleted file mode 100644 index db22ab1fb96..00000000000 --- a/.github/workflows/update-changelog.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Update changelog -on: - workflow_dispatch: - inputs: - version: - required: true - description: 'Needs to match, exactly, the name of a milestone. The version to be released please respect: major.minor.patch, major.minor.patch-preview or major.minor.patch-preview format. example: 7.4.3, 7.4.3-preview or 7.4.3-preview1' - skip_pr: - required: false - default: "0" - skip_community_post: - required: false - default: "0" -jobs: - config: - runs-on: "ubuntu-latest" - outputs: - has-secrets: ${{ steps.check.outputs.has-secrets }} - steps: - - name: "Check for secrets" - id: check - shell: bash - run: | - if [ -n "${{ (secrets.GRAFANA_DELIVERY_BOT_APP_ID != '' && - secrets.GRAFANA_DELIVERY_BOT_APP_PEM != '' && - secrets.GRAFANA_MISC_STATS_API_KEY != '' && - secrets.GRAFANABOT_FORUM_KEY != '' - ) || '' }}" ]; then - echo "has-secrets=1" >> "$GITHUB_OUTPUT" - fi - - main: - needs: config - if: needs.config.outputs.has-secrets - runs-on: ubuntu-latest - steps: - - name: "Generate token" - id: generate_token - uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 - with: - app_id: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_ID }} - private_key: ${{ secrets.GRAFANA_DELIVERY_BOT_APP_PEM }} - - name: Run update changelog (manually invoked) - uses: grafana/grafana-github-actions-go/update-changelog@main - with: - token: ${{ steps.generate_token.outputs.token }} - version: ${{ inputs.version }} - metrics_api_key: ${{ secrets.GRAFANA_MISC_STATS_API_KEY }} - community_api_key: ${{ secrets.GRAFANABOT_FORUM_KEY }} - community_api_username: grafanabot - skip_pr: ${{ inputs.skip_pr }} - skip_community_post: ${{ inputs.skip_community_post }} diff --git a/.github/workflows/update-make-docs.yml b/.github/workflows/update-make-docs.yml index 49b64504bd0..7d727f284df 100644 --- a/.github/workflows/update-make-docs.yml +++ b/.github/workflows/update-make-docs.yml @@ -8,8 +8,10 @@ jobs: if: github.repository == 'grafana/grafana' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: grafana/writers-toolkit/update-make-docs@update-make-docs/v1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + persist-credentials: false + - uses: grafana/writers-toolkit/update-make-docs@update-make-docs/v1 # zizmor: ignore[unpinned-uses] with: pr_options: > --label 'backport v10.1.x' diff --git a/.github/workflows/verify-kinds.yml b/.github/workflows/verify-kinds.yml index 88b45660d45..ce0a7a00b8a 100644 --- a/.github/workflows/verify-kinds.yml +++ b/.github/workflows/verify-kinds.yml @@ -11,12 +11,13 @@ jobs: runs-on: "ubuntu-latest" steps: - name: "Checkout Grafana repo" - uses: "actions/checkout@v4" + uses: "actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683" with: fetch-depth: 0 + persist-credentials: false - name: "Setup Go" - uses: "actions/setup-go@v4" + uses: "actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639" with: go-version-file: go.mod diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 00000000000..7b8321cce14 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,23 @@ +name: Zizmor GitHub Actions static analysis +on: + pull_request: + push: + branches: + - main + +jobs: + zizmor: + name: Analyse with Zizmor + + permissions: + actions: read + contents: read + # required to comment on pull requests with the results of the check + pull-requests: write + # required to upload the results to GitHub's code scanning service + security-events: write + + uses: grafana/shared-workflows/.github/workflows/reusable-zizmor.yml@main # zizmor: ignore[unpinned-uses] + with: + fail-severity: high + min-severity: high diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 00000000000..fba4a80be24 --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,31 @@ +rules: + unpinned-uses: + config: + policies: + "*": hash-pin + actions/*: any + github/*: any + grafana/*: any + forbidden-uses: + config: + deny: + # Policy-banned by our security team due to CVE-2025-30066 & CVE-2025-30154. + # https://www.cisa.gov/news-events/alerts/2025/03/18/supply-chain-compromise-third-party-tj-actionschanged-files-cve-2025-30066-and-reviewdogaction + # https://nvd.nist.gov/vuln/detail/cve-2025-30066 + # https://nvd.nist.gov/vuln/detail/cve-2025-30154 + - reviewdog/* + cache-poisoning: + ignore: + - backend-unit-tests.yml + - frontend-lint.yml + - pr-frontend-unit-tests.yml + - pr-test-integration.yml + - publish-kinds-release.yml + dangerous-triggers: + ignore: + - auto-milestone.yml + - backport.yml + - pr-checks.yml + - pr-commands.yml + - pr-patch-check-event.yml + - run-dashboard-search-e2e.yml diff --git a/pkg/build/actions/bump-version/action.yml b/pkg/build/actions/bump-version/action.yml index 783145097a2..24ae182daf2 100644 --- a/pkg/build/actions/bump-version/action.yml +++ b/pkg/build/actions/bump-version/action.yml @@ -11,7 +11,7 @@ runs: with: go-version-file: go.mod - name: Bump versions - uses: dagger/dagger-for-github@v5 + uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e with: verb: run args: go run ./pkg/build/actions/bump-version -version=${{ inputs.version }}