# Only runs if anything under the packages/ directory changes. --- name: Levitate / Detect breaking changes in PR on: pull_request: paths: - 'packages/**' branches: - 'main' jobs: buildPR: name: Build PR 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: 20.9.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 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: 20.9.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 runs-on: ubuntu-latest needs: ['buildPR', 'buildBase'] env: GITHUB_STEP_NUMBER: 8 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20.9.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 - 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 - name: Upload check output as artifact uses: actions/upload-artifact@v4 with: name: levitate path: levitate/ Report: name: Report breaking changes in PR 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 } = await github.rest.issues.listLabelsOnIssue({ issue_number: process.env.PR_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; # 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) [Console output](${{ steps.levitate-run.outputs.job_link }}) 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 }} # 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 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 with: 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 }}" } 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 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: exit ${{ steps.levitate-run.outputs.exit_code }} shell: bash