# Only runs if anything under the packages/ directory changes. --- name: Levitate / Detect breaking changes in PR concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read id-token: write on: pull_request: paths: - 'packages/**' branches: - 'main' jobs: buildPR: name: Build PR packages artifacts runs-on: ubuntu-latest defaults: run: working-directory: './pr' steps: - uses: actions/checkout@v4 with: path: './pr' - uses: actions/setup-node@v4 with: node-version: 22.11.0 - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "dir=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn cache uses: actions/cache@v4 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} key: yarn-cache-folder-${{ hashFiles('**/yarn.lock', '.yarnrc.yml') }} restore-keys: | yarn-cache-folder- - name: Install dependencies run: yarn install --immutable - name: Build packages run: yarn packages:build - name: Pack packages run: yarn packages:pack --out ./%s.tgz - name: Zip built tarballed packages run: zip -r ./pr_built_packages.zip ./packages/**/*.tgz - name: Upload build output as artifact uses: actions/upload-artifact@v4 with: name: buildPr path: './pr/pr_built_packages.zip' buildBase: name: Build Base packages artifacts runs-on: ubuntu-latest defaults: run: working-directory: './base' steps: - uses: actions/checkout@v4 with: path: './base' ref: ${{ github.event.pull_request.base.ref }} - uses: actions/setup-node@v4 with: node-version: 22.11.0 - name: Get yarn cache directory path id: yarn-cache-dir-path run: echo "dir=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn cache uses: actions/cache@v4 id: yarn-cache with: path: ${{ steps.yarn-cache-dir-path.outputs.dir }} key: yarn-cache-folder-${{ hashFiles('**/yarn.lock', '.yarnrc.yml') }} restore-keys: | yarn-cache-folder- - name: Install dependencies run: yarn install --immutable - name: Build packages run: yarn packages:build - name: Pack packages run: yarn packages:pack --out ./%s.tgz - name: Zip built tarballed packages run: zip -r ./base_built_packages.zip ./packages/**/*.tgz - name: Upload build output as artifact uses: actions/upload-artifact@v4 with: name: buildBase path: './base/base_built_packages.zip' Detect: name: Detect breaking changes between PR and base runs-on: ubuntu-latest needs: ['buildPR', 'buildBase'] env: GITHUB_STEP_NUMBER: 8 permissions: contents: 'read' id-token: 'write' steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 22.11.0 - name: Get built packages from pr uses: actions/download-artifact@v4 with: name: buildPr - name: Get built packages from base uses: actions/download-artifact@v4 with: name: buildBase - name: Unzip artifact from pr run: unzip -j pr_built_packages.zip -d ./pr && rm pr_built_packages.zip - name: Unzip artifact from base run: unzip -j base_built_packages.zip -d ./base && rm base_built_packages.zip - id: 'auth' uses: 'google-github-actions/auth@v2' 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' with: version: '>= 363.0.0' project_id: 'grafanalabs-global' install_components: 'bq' - name: Detect breaking changes id: breaking-changes run: ./scripts/check-breaking-changes.sh env: FORCE_COLOR: 3 - name: Persisting the check output run: | mkdir -p ./levitate 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 with: name: levitate path: levitate/ Report: name: Report breaking changes in PR comment runs-on: ubuntu-latest needs: ['Detect'] steps: - name: "Generate token" id: generate_token uses: tibdex/github-app-token@b62528385c34dbc9f38e5f4225ac829252d1ea92 with: app_id: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_ID }} private_key: ${{ secrets.GRAFANA_PR_AUTOMATION_APP_PEM }} - uses: actions/checkout@v4 - name: 'Download artifact' uses: actions/download-artifact@v4 with: name: levitate - name: Parsing levitate result uses: actions/github-script@v6 id: levitate-run with: script: | const filePath = 'result.json'; const script = require('./.github/workflows/scripts/json-file-to-job-output.js'); await script({ core, filePath }); # Check if label exists - name: Check if "levitate breaking change" label exists id: does-label-exist uses: actions/github-script@v6 env: PR_NUMBER: ${{ github.event.pull_request.number }} with: script: | const { data: labels } = await github.rest.issues.listLabelsOnIssue({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, }); return labels.some(label => label.name === 'levitate breaking change') ? 1 : 0 # put the markdown into a variable - name: Levitate Markdown id: levitate-markdown run: | if [ -f "levitate.md" ]; then { echo 'levitate_markdown<> "$GITHUB_OUTPUT" else echo "levitate_markdown=No breaking changes detected" >> "$GITHUB_OUTPUT" fi # Comment on the PR - name: Comment on PR if: steps.levitate-run.outputs.exit_code == 1 uses: marocchino/sticky-pull-request-comment@v2 with: header: levitate-breaking-change-comment number: ${{ github.event.pull_request.number }} message: | ⚠️   **Possible breaking changes (md version)**   ⚠️ ${{ steps.levitate-markdown.outputs.levitate_markdown }} [Read our guideline](https://github.com/grafana/grafana/blob/main/contribute/breaking-changes-guide/breaking-changes-guide.md) * Your pull request merge won't be blocked. GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} # 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 with: header: levitate-breaking-change-comment number: ${{ github.event.pull_request.number }} delete: true GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} - name: Send Slack Message via Payload id: slack 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: channel-id: "C031SLFH6G0" payload: | { "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>" } ] } ] } # Add the label - name: Add "levitate breaking change" label if: steps.levitate-run.outputs.exit_code == 1 && steps.does-label-exist.outputs.result == 0 uses: actions/github-script@v6 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: github-token: ${{ steps.generate_token.outputs.token }} script: | await github.rest.issues.addLabels({ issue_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, labels: ['levitate breaking change'] }) # Remove label (no more breaking changes) - name: Remove "levitate breaking change" label if: steps.levitate-run.outputs.exit_code == 0 && steps.does-label-exist.outputs.result == 1 uses: actions/github-script@v6 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: github-token: ${{ steps.generate_token.outputs.token }} script: | await github.rest.issues.removeLabel({ issue_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, name: 'levitate breaking change' }) # Add reviewers # This is very weird, the actual request goes through (comes back with a 201), but does not assign the team. # Related issue: https://github.com/renovatebot/renovate/issues/1908 - name: Add "grafana/plugins-platform-frontend" as a reviewer if: steps.levitate-run.outputs.exit_code == 1 uses: actions/github-script@v6 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: github-token: ${{ steps.generate_token.outputs.token }} script: | await github.rest.pulls.requestReviewers({ pull_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, reviewers: [], team_reviewers: ['plugins-platform-frontend'] }); # Remove reviewers (no more breaking changes) - name: Remove "grafana/plugins-platform-frontend" from the list of reviewers if: steps.levitate-run.outputs.exit_code == 0 uses: actions/github-script@v6 env: PR_NUMBER: ${{ steps.levitate-run.outputs.pr_number }} with: github-token: ${{ steps.generate_token.outputs.token }} script: | await github.rest.pulls.removeRequestedReviewers({ pull_number: process.env.PR_NUMBER, owner: context.repo.owner, repo: context.repo.repo, reviewers: [], team_reviewers: ['plugins-platform-frontend'] }); - name: Exit run: | if [ "${{ steps.levitate-run.outputs.exit_code }}" -ne 0 ]; then echo "Breaking changes detected. Please check the levitate report in your pull request. This workflow won't block merging." fi exit ${{ steps.levitate-run.outputs.exit_code }} shell: bash