From 6dfb8eb8484ab3700ac2abc7db3eb1d9a7048071 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 7 Jan 2022 16:34:05 +0100 Subject: [PATCH 1/3] Automatically generate dependabot changesets Signed-off-by: Johan Haals --- .../workflows/dependabot-changeset-maker.yml | 20 ++++ scripts/dependabot-changeset-maker.js | 99 +++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 .github/workflows/dependabot-changeset-maker.yml create mode 100644 scripts/dependabot-changeset-maker.js diff --git a/.github/workflows/dependabot-changeset-maker.yml b/.github/workflows/dependabot-changeset-maker.yml new file mode 100644 index 0000000000..b3bdb102a3 --- /dev/null +++ b/.github/workflows/dependabot-changeset-maker.yml @@ -0,0 +1,20 @@ +name: "Dependabot changeset maker" +on: +- pull_request_target + +jobs: + generate-changeset: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 2 + ref: ${{ github.head_ref }} + - name: Configure Git + run: | + git config --global user.email noreply@backstage.io + git config --global user.name 'Github changeset workflow' + - name: Generate changesets + run: node scripts/dependabot-changeset-maker.js diff --git a/scripts/dependabot-changeset-maker.js b/scripts/dependabot-changeset-maker.js new file mode 100644 index 0000000000..b1c6d60927 --- /dev/null +++ b/scripts/dependabot-changeset-maker.js @@ -0,0 +1,99 @@ +#!/usr/bin/env node +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const { promises: fs } = require('fs'); +const { execFile: execFileCb } = require('child_process'); +const { promisify } = require('util'); + +const execFile = promisify(execFileCb); + +// Parses package.json files and returns the package names +async function getPackagesNames(files) { + const names = []; + for (const file of files) { + const data = JSON.parse(await fs.readFile(file, 'utf8')); + names.push(data.name); + } + return names; +} + +async function createChangeset(fileName, commitMessage, packages) { + const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n'); + const message = commitMessage.replace(/(b|B)ump ([a-z-]+)/, 'Bump `$2`'); + const body = `---\n${pkgs}\n---\n\n${message}`; + await fs.writeFile(fileName, body); +} + +async function runPlain(cmd, ...args) { + try { + const { stdout } = await execFile(cmd, args, { shell: true }); + return stdout.trim(); + } catch (error) { + if (error.stderr) { + process.stderr.write(error.stderr); + } + if (!error.code) { + throw error; + } + throw new Error( + `Command '${[cmd, ...args].join(' ')}' failed with code ${error.code}`, + ); + } +} + +async function main() { + const branch = await runPlain('git', 'branch', '--show-current'); + if (!branch.startsWith('dependabot/')) { + console.log('Not a dependabot branch'); + return; + } + + const diffFiles = await runPlain('git', 'diff', '--name-only', 'HEAD~1'); + if (diffFiles.includes('.changeset')) { + console.log('Changeset already exists'); + return; + } + const files = diffFiles + .split('\n') + .filter(file => file !== 'package.json') // skip root package.json + .filter(file => file.includes('package.json')); + + if (!files.length) { + console.log('no package.json changes, skipping'); + return; + } + + const packageNames = await getPackagesNames(files); + const shortHash = await runPlain('git', 'rev-parse', '--short', 'HEAD'); + const fileName = `.changeset/dependabot-${shortHash.trim()}.md`; + const commitMessage = await runPlain( + 'git', + 'show', + '--pretty=format:%s', + '-s', + 'HEAD', + ); + await createChangeset(fileName, commitMessage, packageNames); + await runPlain('git', 'add', fileName); + await runPlain('git', 'commit', '-C', 'HEAD', '--amend', '--no-edit'); + await runPlain('git', 'push', '--force'); +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); From e1f24677dbd3ebf340899f4b8ebe9dc1c7585dcb Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 10 Jan 2022 16:48:54 +0100 Subject: [PATCH 2/3] Move script into actions definition Signed-off-by: Johan Haals --- .../workflows/dependabot-changeset-maker.yml | 58 ++++++++++- scripts/dependabot-changeset-maker.js | 99 ------------------- 2 files changed, 55 insertions(+), 102 deletions(-) delete mode 100644 scripts/dependabot-changeset-maker.js diff --git a/.github/workflows/dependabot-changeset-maker.yml b/.github/workflows/dependabot-changeset-maker.yml index b3bdb102a3..9b8ab6f035 100644 --- a/.github/workflows/dependabot-changeset-maker.yml +++ b/.github/workflows/dependabot-changeset-maker.yml @@ -5,7 +5,7 @@ on: jobs: generate-changeset: runs-on: ubuntu-latest - if: ${{ github.actor == 'dependabot[bot]' }} + if: github.actor == 'dependabot[bot]' steps: - name: Checkout uses: actions/checkout@v2 @@ -16,5 +16,57 @@ jobs: run: | git config --global user.email noreply@backstage.io git config --global user.name 'Github changeset workflow' - - name: Generate changesets - run: node scripts/dependabot-changeset-maker.js + - name: Generate changeset + uses: actions/github-script@v5 + with: + script: | + const { promises: fs } = require('fs'); + + // Parses package.json files and returns the package names + async function getPackagesNames(files) { + const names = []; + for (const file of files) { + const data = JSON.parse(await fs.readFile(file, 'utf8')); + names.push(data.name); + } + return names; + } + + async function createChangeset(fileName, commitMessage, packages) { + const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n'); + const message = commitMessage.replace(/(b|B)ump ([a-z-]+)/, 'Bump `$2`'); + const body = `---\n${pkgs}\n---\n\n${message}`; + await fs.writeFile(fileName, body); + } + + const branch = await exec.getExecOutput('git branch --show-current'); + if (!branch.stdout.startsWith('dependabot/')) { + console.log('Not a dependabot branch, skipping'); + return; + } + + const diffOutput = await exec.getExecOutput('git diff --name-only HEAD~1'); + const diffFiles = diffOutput.stdout.split('\n'); + + if (diffFiles.find(f => f.startsWith('.changeset'))) { + console.log('Changeset already exists, skipping'); + return; + } + + const files = diffFiles + .filter(file => file !== 'package.json') // skip root package.json + .filter(file => file.includes('package.json')); + + if (!files.length) { + console.log('No package.json changes, skipping'); + return; + } + + const packageNames = await getPackagesNames(files); + const { stdout: shortHash } = await exec.getExecOutput('git rev-parse --short HEAD'); + const fileName = `.changeset/dependabot-${shortHash.trim()}.md`; + const { stdout: commitMessage } = await exec.getExecOutput('git show --pretty=format:%s -s HEAD'); + await createChangeset(fileName, commitMessage, packageNames); + await exec.exec(`git add ${fileName}`); + await exec.exec('git commit -C HEAD --amend --no-edit'); + await exec.exec('git push --force'); diff --git a/scripts/dependabot-changeset-maker.js b/scripts/dependabot-changeset-maker.js deleted file mode 100644 index b1c6d60927..0000000000 --- a/scripts/dependabot-changeset-maker.js +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env node -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { promises: fs } = require('fs'); -const { execFile: execFileCb } = require('child_process'); -const { promisify } = require('util'); - -const execFile = promisify(execFileCb); - -// Parses package.json files and returns the package names -async function getPackagesNames(files) { - const names = []; - for (const file of files) { - const data = JSON.parse(await fs.readFile(file, 'utf8')); - names.push(data.name); - } - return names; -} - -async function createChangeset(fileName, commitMessage, packages) { - const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n'); - const message = commitMessage.replace(/(b|B)ump ([a-z-]+)/, 'Bump `$2`'); - const body = `---\n${pkgs}\n---\n\n${message}`; - await fs.writeFile(fileName, body); -} - -async function runPlain(cmd, ...args) { - try { - const { stdout } = await execFile(cmd, args, { shell: true }); - return stdout.trim(); - } catch (error) { - if (error.stderr) { - process.stderr.write(error.stderr); - } - if (!error.code) { - throw error; - } - throw new Error( - `Command '${[cmd, ...args].join(' ')}' failed with code ${error.code}`, - ); - } -} - -async function main() { - const branch = await runPlain('git', 'branch', '--show-current'); - if (!branch.startsWith('dependabot/')) { - console.log('Not a dependabot branch'); - return; - } - - const diffFiles = await runPlain('git', 'diff', '--name-only', 'HEAD~1'); - if (diffFiles.includes('.changeset')) { - console.log('Changeset already exists'); - return; - } - const files = diffFiles - .split('\n') - .filter(file => file !== 'package.json') // skip root package.json - .filter(file => file.includes('package.json')); - - if (!files.length) { - console.log('no package.json changes, skipping'); - return; - } - - const packageNames = await getPackagesNames(files); - const shortHash = await runPlain('git', 'rev-parse', '--short', 'HEAD'); - const fileName = `.changeset/dependabot-${shortHash.trim()}.md`; - const commitMessage = await runPlain( - 'git', - 'show', - '--pretty=format:%s', - '-s', - 'HEAD', - ); - await createChangeset(fileName, commitMessage, packageNames); - await runPlain('git', 'add', fileName); - await runPlain('git', 'commit', '-C', 'HEAD', '--amend', '--no-edit'); - await runPlain('git', 'push', '--force'); -} - -main().catch(error => { - console.error(error.stack); - process.exit(1); -}); From d6ed0e407d5e0ebcf5741f934959b3b669dc4f63 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 11 Jan 2022 09:21:22 +0100 Subject: [PATCH 3/3] format, pass args in array Signed-off-by: Johan Haals --- .../workflows/dependabot-changeset-maker.yml | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/.github/workflows/dependabot-changeset-maker.yml b/.github/workflows/dependabot-changeset-maker.yml index 9b8ab6f035..1387892489 100644 --- a/.github/workflows/dependabot-changeset-maker.yml +++ b/.github/workflows/dependabot-changeset-maker.yml @@ -1,72 +1,72 @@ -name: "Dependabot changeset maker" +name: 'Dependabot changeset maker' on: -- pull_request_target + - pull_request_target jobs: generate-changeset: runs-on: ubuntu-latest - if: github.actor == 'dependabot[bot]' + if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage' steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 2 - ref: ${{ github.head_ref }} - - name: Configure Git - run: | - git config --global user.email noreply@backstage.io - git config --global user.name 'Github changeset workflow' - - name: Generate changeset - uses: actions/github-script@v5 - with: - script: | - const { promises: fs } = require('fs'); + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 2 + ref: ${{ github.head_ref }} + - name: Configure Git + run: | + git config --global user.email noreply@backstage.io + git config --global user.name 'Github changeset workflow' + - name: Generate changeset + uses: actions/github-script@v5 + with: + script: | + const { promises: fs } = require('fs'); - // Parses package.json files and returns the package names - async function getPackagesNames(files) { - const names = []; - for (const file of files) { - const data = JSON.parse(await fs.readFile(file, 'utf8')); - names.push(data.name); + // Parses package.json files and returns the package names + async function getPackagesNames(files) { + const names = []; + for (const file of files) { + const data = JSON.parse(await fs.readFile(file, 'utf8')); + names.push(data.name); + } + return names; } - return names; - } - async function createChangeset(fileName, commitMessage, packages) { - const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n'); - const message = commitMessage.replace(/(b|B)ump ([a-z-]+)/, 'Bump `$2`'); - const body = `---\n${pkgs}\n---\n\n${message}`; - await fs.writeFile(fileName, body); - } + async function createChangeset(fileName, commitMessage, packages) { + const pkgs = packages.map(pkg => `'${pkg}': patch`).join('\n'); + const message = commitMessage.replace(/(b|B)ump ([a-z-]+)/, 'Bump `$2`'); + const body = `---\n${pkgs}\n---\n\n${message}`; + await fs.writeFile(fileName, body); + } - const branch = await exec.getExecOutput('git branch --show-current'); - if (!branch.stdout.startsWith('dependabot/')) { - console.log('Not a dependabot branch, skipping'); - return; - } + const branch = await exec.getExecOutput('git branch --show-current'); + if (!branch.stdout.startsWith('dependabot/')) { + console.log('Not a dependabot branch, skipping'); + return; + } - const diffOutput = await exec.getExecOutput('git diff --name-only HEAD~1'); - const diffFiles = diffOutput.stdout.split('\n'); + const diffOutput = await exec.getExecOutput('git diff --name-only HEAD~1'); + const diffFiles = diffOutput.stdout.split('\n'); - if (diffFiles.find(f => f.startsWith('.changeset'))) { - console.log('Changeset already exists, skipping'); - return; - } + if (diffFiles.find(f => f.startsWith('.changeset'))) { + console.log('Changeset already exists, skipping'); + return; + } - const files = diffFiles - .filter(file => file !== 'package.json') // skip root package.json - .filter(file => file.includes('package.json')); + const files = diffFiles + .filter(file => file !== 'package.json') // skip root package.json + .filter(file => file.includes('package.json')); - if (!files.length) { - console.log('No package.json changes, skipping'); - return; - } + if (!files.length) { + console.log('No package.json changes, skipping'); + return; + } - const packageNames = await getPackagesNames(files); - const { stdout: shortHash } = await exec.getExecOutput('git rev-parse --short HEAD'); - const fileName = `.changeset/dependabot-${shortHash.trim()}.md`; - const { stdout: commitMessage } = await exec.getExecOutput('git show --pretty=format:%s -s HEAD'); - await createChangeset(fileName, commitMessage, packageNames); - await exec.exec(`git add ${fileName}`); - await exec.exec('git commit -C HEAD --amend --no-edit'); - await exec.exec('git push --force'); + const packageNames = await getPackagesNames(files); + const { stdout: shortHash } = await exec.getExecOutput('git rev-parse --short HEAD'); + const fileName = `.changeset/dependabot-${shortHash.trim()}.md`; + const { stdout: commitMessage } = await exec.getExecOutput('git show --pretty=format:%s -s HEAD'); + await createChangeset(fileName, commitMessage, packageNames); + await exec.exec('git', ['add', fileName]); + await exec.exec('git commit -C HEAD --amend --no-edit'); + await exec.exec('git push --force');