From ea243b536c6fe96d0f85ac9a267d222e5c568b02 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Fri, 2 Feb 2024 09:19:30 +0100 Subject: [PATCH] Levitate: fix markdown diff format (#81477) * Add some fake breaking changes * try to generate another breaking chnage * Keep trying * Update levitate script * Add fake breaking changes * Use latest * Strip ansi * Test * Remove ansi stripping * ClearAnsi again * another regex * Revert some changes used for testing * Rename function * Fix indentation in levitate workflow * Remove test breaking changes * Remove breaking changes * Update actions * Trigger breaking change * restore file --- .../detect-breaking-changes-levitate.yml | 442 +++++++++--------- scripts/check-breaking-changes.sh | 8 +- scripts/levitate-parse-json-report.js | 10 +- 3 files changed, 232 insertions(+), 228 deletions(-) diff --git a/.github/workflows/detect-breaking-changes-levitate.yml b/.github/workflows/detect-breaking-changes-levitate.yml index 3b46b5b912f..6ef0ac3514a 100644 --- a/.github/workflows/detect-breaking-changes-levitate.yml +++ b/.github/workflows/detect-breaking-changes-levitate.yml @@ -1,5 +1,5 @@ # Only runs if anything under the packages/ directory changes. - +--- name: Levitate / Detect breaking changes in PR on: @@ -18,44 +18,43 @@ jobs: working-directory: './pr' steps: - - uses: actions/checkout@v4 - with: - path: './pr' - - uses: actions/setup-node@v4 - with: - node-version: 20.9.0 + - 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: 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: Restore yarn cache - uses: actions/cache@v3.3.1 - 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: Install dependencies - run: yarn install --immutable + - name: Build packages + run: yarn packages:build - - name: Build packages - run: yarn packages:build + - name: Pack packages + run: yarn packages:pack --out ./%s.tgz - - 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: Zip built tarballed packages - run: zip -r ./pr_built_packages.zip ./packages/**/*.tgz - - - name: Upload build output as artifact - uses: actions/upload-artifact@v3 - with: - name: buildPr - path: './pr/pr_built_packages.zip' + - name: Upload build output as artifact + uses: actions/upload-artifact@v4 + with: + name: buildPr + path: './pr/pr_built_packages.zip' buildBase: name: Build Base @@ -65,45 +64,45 @@ jobs: working-directory: './base' steps: - - uses: actions/checkout@v4 - with: - path: './base' - ref: ${{ github.event.pull_request.base.ref }} + - 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 + - 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: 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@v3.3.1 - 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: 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: Install dependencies + run: yarn install --immutable - - name: Build packages - run: yarn packages:build + - name: Build packages + run: yarn packages:build - - name: Pack packages - run: yarn packages:pack --out ./%s.tgz + - 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: Zip built tarballed packages + run: zip -r ./base_built_packages.zip ./packages/**/*.tgz - - name: Upload build output as artifact - uses: actions/upload-artifact@v3 - with: - name: buildBase - path: './base/base_built_packages.zip' + - 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 @@ -114,14 +113,17 @@ jobs: 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@v3 + uses: actions/download-artifact@v4 with: name: buildPr - name: Get built packages from base - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: buildBase @@ -153,7 +155,7 @@ jobs: 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@v3 + uses: actions/upload-artifact@v4 with: name: levitate path: levitate/ @@ -165,174 +167,174 @@ jobs: 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 }} + - 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 + - uses: actions/checkout@v4 - - name: 'Download artifact' - uses: actions/download-artifact@v3 - with: - name: levitate + - 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 }); + - 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'); + # 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; + 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 + # 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)**   ⚠️ + # 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 }} + ${{ 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 }} + [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 }} + # 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.24.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 != '') || '' }} + # 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.24.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'] - }) + # 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' - }) + # 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 - 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'] - }); + # 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'] - }); + # 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 + - name: Exit + run: exit ${{ steps.levitate-run.outputs.exit_code }} + shell: bash diff --git a/scripts/check-breaking-changes.sh b/scripts/check-breaking-changes.sh index 01f96fa8bb3..e4e0b7cad6f 100755 --- a/scripts/check-breaking-changes.sh +++ b/scripts/check-breaking-changes.sh @@ -30,9 +30,9 @@ while IFS=" " read -r -a package; do # Run the comparison and record the exit code echo "" echo "" - echo "${PACKAGE_PATH}" + echo "$PACKAGE_PATH" echo "=================================================" - npm exec -- @grafana/levitate compare --prev "$PREV" --current "$CURRENT" --json >data.json + npm exec -- @grafana/levitate@latest compare --prev "$PREV" --current "$CURRENT" --json >data.json # Check if the comparison returned with a non-zero exit code # Record the output, maybe with some additional information @@ -40,7 +40,7 @@ while IFS=" " read -r -a package; do CURRENT_REPORT=$(node ./scripts/levitate-parse-json-report.js) # Final exit code # (non-zero if any of the packages failed the checks) - if [ $STATUS -gt 0 ]; then + if [ "$STATUS" -gt 0 ]; then EXIT_CODE=1 GITHUB_MESSAGE="${GITHUB_MESSAGE}**\\\`${PACKAGE_PATH}\\\`** has possible breaking changes ([more info](${GITHUB_JOB_LINK}#step:${GITHUB_STEP_NUMBER}:1))
" GITHUB_LEVITATE_MARKDOWN+="

${PACKAGE_PATH}

${CURRENT_REPORT}
" @@ -52,7 +52,7 @@ done <<<"$PACKAGES" echo "is_breaking=$EXIT_CODE" >>"$GITHUB_OUTPUT" echo "message=$GITHUB_MESSAGE" >>"$GITHUB_OUTPUT" mkdir -p ./levitate -echo $GITHUB_LEVITATE_MARKDOWN >./levitate/levitate.md +echo "$GITHUB_LEVITATE_MARKDOWN" >./levitate/levitate.md # We will exit the workflow accordingly at another step exit 0 diff --git a/scripts/levitate-parse-json-report.js b/scripts/levitate-parse-json-report.js index cbebdf44195..fd82fb34280 100644 --- a/scripts/levitate-parse-json-report.js +++ b/scripts/levitate-parse-json-report.js @@ -2,7 +2,9 @@ const fs = require('fs'); const data = JSON.parse(fs.readFileSync('data.json', 'utf8')); -const stripAnsi = (string) => string.replace(/\u001b\[.*?m/g, ''); +function stripAnsi(str) { + return str.replace(/\x1b\[[0-9;]*m/g, ''); +} const printSection = (title, items) => { let output = `

${title}

`; @@ -10,9 +12,9 @@ const printSection = (title, items) => { const language = item.declaration ? 'typescript' : 'diff'; const code = item.declaration ? item.declaration : stripAnsi(item.diff); - output += `${item.name}
`; - output += `${item.location}
`; - output += `
${code}

`; + output += `${item.name}
\n`; + output += `${item.location}
\n`; + output += `
\n${code}\n

\n`; }); return output; };