From 5006af43158bd2e39ad1b4620aaab86c242c95a6 Mon Sep 17 00:00:00 2001 From: ThunderMiracle Date: Sat, 10 Jun 2023 21:31:30 +0900 Subject: [PATCH] feat: automatically add changeset to renovate prs (#87) --- .changeset/ten-jokes-add.md | 5 ++ .github/workflows/renovate-changesets.yml | 99 +++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 .changeset/ten-jokes-add.md create mode 100644 .github/workflows/renovate-changesets.yml diff --git a/.changeset/ten-jokes-add.md b/.changeset/ten-jokes-add.md new file mode 100644 index 0000000..e94d76c --- /dev/null +++ b/.changeset/ten-jokes-add.md @@ -0,0 +1,5 @@ +--- +"filter-data": patch +--- + +feat: automatically add changeset to renovate prs diff --git a/.github/workflows/renovate-changesets.yml b/.github/workflows/renovate-changesets.yml new file mode 100644 index 0000000..15467fc --- /dev/null +++ b/.github/workflows/renovate-changesets.yml @@ -0,0 +1,99 @@ +name: Add Renovate changeset +on: + pull_request_target: + paths: + - '.github/workflows/renovate-changesets.yml' + - '**/pnpm-lock.yaml' + +jobs: + generate-changeset: + runs-on: ubuntu-latest + if: github.actor == 'renovate[bot]' && github.repository == 'thundermiracle/filter-data' + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 2 + ref: ${{ github.head_ref }} + token: ${{ secrets.PAT_GITHUB }} + + - name: Configure Git + run: | + git config --global user.email noreply@thundermiracle.com + git config --global user.name 'Github changeset workflow' + + - name: Generate changeset + uses: actions/github-script@v6 + 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")); + if (!data.private) { + names.push(data.name); + } + } + return names; + } + + async function createChangeset(fileName, packageBumps, packages) { + let message = ""; + for (const [pkg, bump] of packageBumps) { + message = message + `Updated dependency \`${pkg}\` to \`${bump}\`.\n`; + } + + const pkgs = packages.map((pkg) => `'${pkg}': patch`).join("\n"); + const body = `---\n${pkgs}\n---\n\n${message.trim()}\n`; + await fs.writeFile(fileName, body); + } + + async function getBumps(files) { + const bumps = new Map(); + for (const file of files) { + const { stdout: changes } = await exec.getExecOutput("git", [ + "show", + file, + ]); + for (const change of changes.split("\n")) { + if (!change.startsWith("+ ")) { + continue; + } + const match = change.match(/"(.*?)"/g); + bumps.set(match[0].replace(/"/g, ""), match[1].replace(/"/g, "")); + } + } + return bumps; + } + + const branch = await exec.getExecOutput("git branch --show-current"); + if (!branch.stdout.startsWith("renovate/")) { + console.log("Not a renovate 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")); + const packageNames = await getPackagesNames(files); + if (!packageNames.length) { + console.log("No package.json changes to published packages, skipping"); + return; + } + const { stdout: shortHash } = await exec.getExecOutput( + "git rev-parse --short HEAD" + ); + const fileName = `.changeset/renovate-${shortHash.trim()}.md`; + + const packageBumps = await getBumps(files); + await createChangeset(fileName, packageBumps, packageNames); + await exec.exec("git", ["add", fileName]); + await exec.exec("git commit -C HEAD --amend --no-edit"); + await exec.exec("git push --force");