|
| 1 | +/** |
| 2 | + * Creates a changeset file for Dependabot PRs that update production |
| 3 | + * dependencies in published @knocklabs/* packages. |
| 4 | + * |
| 5 | + * Skips devDependency-only changes since those don't require a release. |
| 6 | + * |
| 7 | + * This script is only run from a workflow gated to dependabot[bot], |
| 8 | + * so we trust that the changes are dependency updates. |
| 9 | + * |
| 10 | + * Environment variables: |
| 11 | + * PR_TITLE - The pull request title (used as the changeset description) |
| 12 | + * PR_NUMBER - The pull request number (used in the changeset filename) |
| 13 | + * GITHUB_OUTPUT - Path to the GitHub Actions output file |
| 14 | + */ |
| 15 | + |
| 16 | +const { execSync } = require("child_process"); |
| 17 | +const fs = require("fs"); |
| 18 | +const path = require("path"); |
| 19 | + |
| 20 | +const prTitle = process.env.PR_TITLE; |
| 21 | +const prNumber = process.env.PR_NUMBER; |
| 22 | +const githubOutput = process.env.GITHUB_OUTPUT; |
| 23 | + |
| 24 | +if (!prTitle || !prNumber || !githubOutput) { |
| 25 | + console.error( |
| 26 | + "Missing required environment variables: PR_TITLE, PR_NUMBER, GITHUB_OUTPUT", |
| 27 | + ); |
| 28 | + process.exit(1); |
| 29 | +} |
| 30 | + |
| 31 | +const changesetFile = path.join(".changeset", `dependabot-pr-${prNumber}.md`); |
| 32 | + |
| 33 | +function setOutput(key, value) { |
| 34 | + fs.appendFileSync(githubOutput, `${key}=${value}\n`); |
| 35 | +} |
| 36 | + |
| 37 | +// If changeset already exists, skip |
| 38 | +if (fs.existsSync(changesetFile)) { |
| 39 | + console.log("Changeset already exists, skipping"); |
| 40 | + setOutput("created", "false"); |
| 41 | + process.exit(0); |
| 42 | +} |
| 43 | + |
| 44 | +// Find all package.json files changed in the latest commit |
| 45 | +const diffOutput = execSync( |
| 46 | + "git diff --name-only HEAD~1 HEAD -- '**/package.json'", |
| 47 | + { encoding: "utf-8" }, |
| 48 | +).trim(); |
| 49 | + |
| 50 | +if (!diffOutput) { |
| 51 | + console.log("No package.json files changed"); |
| 52 | + setOutput("created", "false"); |
| 53 | + process.exit(0); |
| 54 | +} |
| 55 | + |
| 56 | +const changedFiles = diffOutput.split("\n").filter(Boolean); |
| 57 | +const packages = []; |
| 58 | + |
| 59 | +for (const pkgFile of changedFiles) { |
| 60 | + const content = JSON.parse(fs.readFileSync(pkgFile, "utf-8")); |
| 61 | + const pkgName = content.name || ""; |
| 62 | + |
| 63 | + if (content.private || !pkgName.startsWith("@knocklabs/")) { |
| 64 | + continue; |
| 65 | + } |
| 66 | + |
| 67 | + // Only include packages where production dependencies changed, |
| 68 | + // not devDependency-only updates |
| 69 | + let oldDeps = {}; |
| 70 | + try { |
| 71 | + const oldContent = execSync(`git show "HEAD~1:${pkgFile}"`, { |
| 72 | + encoding: "utf-8", |
| 73 | + }); |
| 74 | + oldDeps = JSON.parse(oldContent).dependencies || {}; |
| 75 | + } catch { |
| 76 | + // File may not exist in previous commit |
| 77 | + } |
| 78 | + |
| 79 | + const newDeps = content.dependencies || {}; |
| 80 | + const sortedOld = JSON.stringify(oldDeps, Object.keys(oldDeps).sort()); |
| 81 | + const sortedNew = JSON.stringify(newDeps, Object.keys(newDeps).sort()); |
| 82 | + |
| 83 | + if (sortedOld === sortedNew) { |
| 84 | + console.log(`Only devDependency changes in ${pkgName}, skipping`); |
| 85 | + continue; |
| 86 | + } |
| 87 | + |
| 88 | + packages.push(pkgName); |
| 89 | +} |
| 90 | + |
| 91 | +const uniquePackages = [...new Set(packages)].sort(); |
| 92 | + |
| 93 | +if (uniquePackages.length === 0) { |
| 94 | + console.log("No published @knocklabs packages were affected"); |
| 95 | + setOutput("created", "false"); |
| 96 | + process.exit(0); |
| 97 | +} |
| 98 | + |
| 99 | +// Generate changeset file |
| 100 | +const lines = [ |
| 101 | + "---", |
| 102 | + ...uniquePackages.map((pkg) => `"${pkg}": patch`), |
| 103 | + "---", |
| 104 | + "", |
| 105 | + prTitle, |
| 106 | + "", |
| 107 | +]; |
| 108 | + |
| 109 | +fs.writeFileSync(changesetFile, lines.join("\n")); |
| 110 | + |
| 111 | +console.log(`Created changeset: ${changesetFile}`); |
| 112 | +console.log(fs.readFileSync(changesetFile, "utf-8")); |
| 113 | +setOutput("created", "true"); |
0 commit comments