Skip to content

Commit

Permalink
feat: Scripts for regenerating local modules and creating PRs (#434)
Browse files Browse the repository at this point in the history
Checking them in for use later. I can't actually generate the PRs from
these until the next "brm" is released.
  • Loading branch information
StephenWeatherford authored Jul 10, 2023
1 parent 7768140 commit f2ae200
Show file tree
Hide file tree
Showing 9 changed files with 447 additions and 2 deletions.
60 changes: 59 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"eslint": "^8.9.0",
"eslint-config-prettier": "^8.3.0",
"markdown-table": "^3.0.2",
"prettier": "2.8.6"
"prettier": "2.8.6",
"semver": "^7.3.5"
}
}
136 changes: 136 additions & 0 deletions scripts/local/create-prs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
This script creates a PR for each modules on the local disk that has changes. It will query interactively for each module.
Requires that gh be installed (https://cli.github.com/)
Usage:
cd repos/bicep-registry-modules
<Do something to make changes in one or modules (e.g. `node scripts/local/regenerate-all.js`)>
node scripts/local/create-pr.js
*/

const usage = `Usage:
cd repos/bicep-registry-modules
node scripts/local/create-prs.js <branch-prefix> <commit-message-prefix>
e.g.
node scripts/local/create-prs.js myname "feat: My commit message"
`;

const { runAsync } = require("./util/runAsync.js");
const { clearScreen, green, yellow, red, reset } = require("./util/colors");

const { argv } = require("process");

// eslint-disable-next-line prefer-const
let [branchPrefix, commitPrefix] = argv.slice(2);
if (!branchPrefix || !commitPrefix) {
console.error(usage);
process.exit(1);
}

branchPrefix = branchPrefix.endsWith("/")
? branchPrefix.slice(0, branchPrefix.length - 1)
: branchPrefix;
console.log(`${green}branch prefix: ${branchPrefix}${reset}`);
console.log(`${green}commit message: ${commitPrefix}${reset}`);

let runAll = false;

function queryUserAsync(question) {
const readline = require("readline").createInterface({
input: process.stdin,
output: process.stdout,
});

return new Promise((resolve) => {
readline.question(question, (answer) => {
readline.close();
resolve(answer);
});
});
}

async function queryRunAsync(cmds) {
let run = runAll === true;
if (!run) {
const answer = await queryUserAsync(
`${red}Run the following commands?\n${yellow}${cmds.join(
"\n"
)}"${reset}"? (y/n/a/q) `
);
console.log(`answer: ${answer}`);

if (answer === "y") {
run = true;
} else if (answer === "a") {
runAll = true;
run = true;
} else if (answer === "q") {
throw new Error("User aborted");
}
}

for (const cmd of cmds) {
await runAsync(cmd);
}
}

async function getChangedModulesAsync() {
const changedModules = (await runAsync(`git status modules`))
.split("\n")
.filter((line) => line.match(/modified/))
.map((line) => line.replace(/^.*\smodules\/([^/]+\/[^/]+)\/.*$/g, "$1"))
// dedupe the list
.filter((value, index, self) => self.indexOf(value) === index);

return changedModules;
}

async function CreatePRAsync(modulePath) {
console.log(
`${clearScreen}${green}=========== Creating PR for ${modulePath}...${reset}`
);

const branchName = `${branchPrefix}/auto/${modulePath}`;
await runAsync(`git checkout -b ${branchName}`);
await runAsync(`git add modules/${modulePath}`);
await runAsync(`git diff --cached`);

const commitMessage = `${commitPrefix} (${modulePath}) (auto)`;
const prTitle = `${commitPrefix} (${modulePath}) (auto-generated)`;
const prBody = `This PR was created by a script. Please review the changes and merge if they look good.`;
await queryRunAsync([
`git commit -m "${commitMessage}"`,
`git push -u origin ${branchName}`,
`gh pr create --title "${prTitle}" --body "${prBody}" --label "Auto-generated"`,
]);

await runAsync(`git checkout main`);
await runAsync(`git branch -d ${branchName}`);
console.log();
}

async function CreatePRs() {
const changedModules = await getChangedModulesAsync();

const currentBranch = await runAsync(`git symbolic-ref --short HEAD`, false);
console.log(`${green}Current branch: ${currentBranch}${reset}`);

await runAsync(`git checkout main`);

try {
for (const modulePath of changedModules) {
await CreatePRAsync(modulePath);
}
} finally {
await runAsync(`git checkout ${currentBranch}`, false);
console.log(`${green}Restored branch to ${currentBranch}${reset}`);
}
}

CreatePRs();
24 changes: 24 additions & 0 deletions scripts/local/fix-all-readme-examples.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
This script updates the examples in all README.md files to the *current* version.
Usage:
cd repos/bicep-registry-modules
node scripts/local/fix-all-readme-examples.js
*/

const { updateReadmeExamples } = require("./util/updateReadmeExamples.js");
const {
getLatestModuleVersionsAsync,
} = require("./util/getLatestModuleVersionsAsync");

async function regenerateAllAsync() {
const modules = await getLatestModuleVersionsAsync();

for (const [modulePath, version] of modules) {
updateReadmeExamples(modulePath, version, version);
}
}

regenerateAllAsync();
85 changes: 85 additions & 0 deletions scripts/local/regenerate-all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
This script is used to regenerate all the modules in the current repo. Also updates the examples
in the README.md to the *next* version of the module (making it ready for checkin).
Usage:
cd repos/bicep-registry-modules
node scripts/local/regenerate-all.js
*/

const { resolve } = require("path");
const { updateReadmeExamples } = require("./util/updateReadmeExamples.js");
const { runAsync } = require("./util/runAsync.js");
const { existsSync } = require("fs");
const {
getLatestModuleVersionsAsync,
} = require("./util/getLatestModuleVersionsAsync.js");
const { green, reset, yellow } = require("./util/colors");
const semver = require("semver");

let brm = `..\\bicep\\src\\Bicep.RegistryModuleTool\\Bin\\Debug\\net7.0\\Azure.Bicep.RegistryModuleTool`;
brm = existsSync(brm) ? brm : brm + ".exe";
brm = existsSync(brm) ? brm : "brm";
brm = resolve(brm);

console.warn(`Using this brm: ${brm}`);

function getNextVersion(version) {
return semver.inc(version, "patch");
}

async function regenerateAllAsync() {
const modules = await getLatestModuleVersionsAsync();

for (const [modulePath, version] of modules) {
var currentDir = process.cwd();
process.chdir(resolve(`modules/${modulePath}`));

try {
let needsGenerate = false;

try {
console.warn(`${yellow}Validating: ${modulePath}${reset}`);
await runAsync(`${brm} validate`);
} catch (err) {
if (
/Please run "brm generate"/.test(String(err)) ||
/The "summary" property in metadata.json does not match/.test(
String(err)
)
) {
needsGenerate = true;
} else {
throw err;
}
}

if (needsGenerate) {
console.error(
`${yellow}Generating: ${modulePath} (version ${version})${reset}`
);
await runAsync(`${brm} generate`);

console.error(
`${yellow}Updating README for ${modulePath} (version ${version})${reset}`
);
process.chdir(resolve(currentDir));
await updateReadmeExamples(
modulePath,
version,
getNextVersion(version)
);
} else {
console.error(
`${green}Does not need regeneration: ${modulePath}${reset}`
);
}
} finally {
process.chdir(resolve(currentDir));
}
}
}

regenerateAllAsync();
6 changes: 6 additions & 0 deletions scripts/local/util/colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
exports.red = "\u001b[31m";
exports.green = "\u001b[32m";
exports.blue = "\u001b[34m";
exports.yellow = "\u001b[33m";
exports.reset = "\u001b[0m";
exports.clearScreen = "\u001b[2J\u001b[0;0H";
Loading

0 comments on commit f2ae200

Please sign in to comment.