Skip to content

Commit d20c137

Browse files
tonygaetaniharrisonmuskat
authored andcommitted
Break each replacement out of the update-content file and provide a system for writing more replacements (#29)
Co-authored-by: Harrison Muskat <hmuskat@6river.com>
1 parent 5944118 commit d20c137

File tree

5 files changed

+144
-43
lines changed

5 files changed

+144
-43
lines changed

README.md

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,18 @@ For each repo, this tool will:
1515

1616
## Installation
1717

18-
```bash
18+
```shell
1919
npm install -g github-default-branch
2020
```
2121

22+
## Development
23+
24+
```shell
25+
git clone https://github.com/6RiverSystems/github-default-branch.git
26+
cd github-default-branch
27+
npm ci
28+
```
29+
2230
## Authentication
2331

2432
[Create a personal access token](https://github.com/settings/tokens/new?scopes=repo&description=github-default-branch) with the `repo` scope. This is the value for `<token>` in the examples.
@@ -58,3 +66,54 @@ Run with the `--verbose` flag to see debug information
5866
| --old | The name of the branch to rename | master |
5967
| --new | The new branch name | main |
6068
| --confirm | Run without prompting for confirmation | false |
69+
70+
## Replacements
71+
72+
Part of this script checks for the existence of files and updates their contents. Replacements are the mechanism for these updates.
73+
74+
### How it Works
75+
76+
Each .js file in the src/replacements folder is given a chance to run during the content updating step of the script. Each file in src/replacements is expected to export a function, that function receives all of the options that are available to the outmost script.
77+
78+
If there is nothing to replace, then the script moves on to the next replacement.
79+
80+
### How to Add a Replacement
81+
82+
Add a file to src/replacements with a .js extension
83+
84+
Like this:
85+
86+
```javascript
87+
module.exports = function ({
88+
owner, // string - repo owner
89+
repo, // string - repo name
90+
old, // string - old branch name
91+
target, // string - new branch name
92+
octokit, // Octokit - oktokit instance
93+
verbose, // boolean - verbose flag
94+
isDryRun, // boolean - dry run flag
95+
}) {
96+
// code goes here
97+
return {
98+
path: '<path to file in repo>',
99+
replacements: [
100+
{
101+
from: '<from pattern>',
102+
to: '<to pattern>',
103+
},
104+
{
105+
from: '<from pattern>',
106+
to: '<to pattern>',
107+
},
108+
],
109+
};
110+
}
111+
112+
```
113+
114+
The file with the path in your repo will have any line matching `from` be swapped out with `to`
115+
116+
117+
### Known Issues
118+
119+
The replacement system gives you octokit, that's great! Unfortunately replacement functions do not currently support asynchronous calls, that's bad.

bin/github-default-branch

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,9 @@
105105
const updateContent = require("../src/update-content");
106106
const updateBranchProtection = require("../src/branch-protection");
107107

108-
const old = argv.old || "master";
109-
const target = argv.new || "main";
108+
const old = argv.old;
109+
const target = argv.new;
110+
const verbose = argv.verbose;
110111

111112
// Make sure they want to do this
112113
if (!(await confirmation(argv.confirm, old, target))) {
@@ -117,7 +118,7 @@
117118
auth: argv.pat || process.env.GITHUB_TOKEN,
118119
});
119120

120-
if (argv.verbose) {
121+
if (verbose) {
121122
const {
122123
data: {
123124
rate: { remaining },
@@ -133,7 +134,7 @@
133134
return;
134135
}
135136
for (let r of repos) {
136-
if (argv.verbose) {
137+
if (verbose) {
137138
console.log(`✏️ Processing ${r}`);
138139
}
139140

@@ -148,12 +149,12 @@
148149
continue;
149150
}
150151

151-
if (argv.verbose) {
152+
if (verbose) {
152153
console.log(`✏️ Creating branch [${target}] at [${currentMasterSha}]`);
153154
}
154155

155156
if (!isDryRun) {
156-
const mainBranch = await createBranch(
157+
await createBranch(
157158
owner,
158159
repo,
159160
target,
@@ -180,7 +181,7 @@
180181
continue;
181182
}
182183

183-
if (argv.verbose) {
184+
if (verbose) {
184185
console.log(
185186
`✏️ Updating pull request [#${pr.number}] in [${repo}] from [${pr.base.ref}] to [${target}]`
186187
);
@@ -196,11 +197,18 @@
196197
}
197198
}
198199

199-
if (argv.verbose) {
200+
if (verbose) {
200201
console.log(`✏️ Updating default branch to [${target}] in [${repo}]`);
201202
}
202203

203-
if (!isDryRun) {
204+
const {
205+
data: { default_branch: defaultBranch },
206+
} = await octokit.repos.get({
207+
owner,
208+
repo
209+
});
210+
211+
if (!isDryRun && defaultBranch === old) {
204212
// Update the default branch in the repo
205213
await octokit.repos.update({
206214
owner,
@@ -209,15 +217,15 @@
209217
});
210218
}
211219

212-
if (argv.verbose) {
220+
if (verbose) {
213221
console.log(`✏️ Changing branch protections`);
214222
}
215223

216224
if (!isDryRun) {
217225
await updateBranchProtection(owner, repo, old, target, octokit);
218226
}
219227

220-
if (argv.verbose) {
228+
if (verbose) {
221229
console.log(`✏️ Deleting old branch [${old}] in ${repo}`);
222230
}
223231

@@ -229,18 +237,18 @@
229237
}
230238

231239
// Update all content on the branch
232-
await updateContent(
240+
await updateContent({
233241
owner,
234242
repo,
235243
old,
236244
target,
237245
octokit,
238-
argv.verbose,
246+
verbose,
239247
isDryRun
240-
);
248+
});
241249

242250
// Add an empty new line to break up the output for each repo
243-
if (argv.verbose) {
251+
if (verbose) {
244252
console.log("");
245253
}
246254
}

src/replacements/circleci.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module.exports = function ({old, target}) {
2+
return {
3+
path: ".circleci/config.yml",
4+
replacements: [
5+
{
6+
from: `- ${old}`,
7+
to: `- ${target}`
8+
},
9+
{
10+
from : `branch: ${old}`,
11+
to: `branch: ${target}`
12+
},
13+
{
14+
from: `only: ${old}`,
15+
to: `only: ${target}`
16+
},
17+
{
18+
from: `"${old}"`,
19+
to: `"${target}"`
20+
}
21+
]
22+
};
23+
}

src/replacements/readme.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = function ({owner, repo, old, target}) {
2+
return {
3+
path: "README.md",
4+
replacements: [
5+
{
6+
from: `@${old}`,
7+
to: `@${target}`,
8+
},
9+
{
10+
from: `${owner}/${repo}.svg?branch=${old}`,
11+
to: `${owner}/${repo}.svg?branch=${target}`,
12+
},
13+
],
14+
};
15+
}

src/update-content.js

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,39 @@
1+
const fs = require('fs');
2+
const util = require('util');
13
const replaceAll = require("string.prototype.replaceall");
24

3-
module.exports = async function (
4-
owner,
5-
repo,
6-
old,
7-
target,
8-
octokit,
9-
isVerbose,
10-
isDryRun
11-
) {
12-
const replacements = {
13-
"README.md": [
14-
{
15-
from: `@${old}`,
16-
to: `@${target}`,
17-
},
18-
{
19-
from: `${owner}/${repo}.svg?branch=${old}`,
20-
to: `${owner}/${repo}.svg?branch=${target}`,
21-
},
22-
],
23-
};
5+
const ls = util.promisify(fs.readdir);
246

7+
module.exports = async function (options) {
8+
const {
9+
owner,
10+
repo,
11+
octokit,
12+
verbose,
13+
isDryRun,
14+
} = options;
15+
const replacementsDir = `${__dirname}/replacements`;
16+
const files = (await ls(replacementsDir)).filter((f) => f.endsWith('.js'));
17+
const replacements = files.reduce((acc, next) => {
18+
const { path, replacements } = require(`${replacementsDir}/${next}`)(options);
19+
return Object.assign(acc, { [path]: replacements });
20+
}, {});
2521
for (let path in replacements) {
2622
try {
2723
let file = await loadFile(owner, repo, path, octokit);
2824

2925
let content = file.content;
3026
for (let r of replacements[path]) {
31-
var re = new RegExp(r.from, "g");
32-
content = replaceAll(content, r.from, r.to);
27+
const re = new RegExp(r.from, "g");
28+
content = replaceAll(content, re, r.to);
3329
}
3430

3531
if (content !== file.content) {
36-
if (isVerbose) {
32+
if (verbose) {
3733
console.log(`✏️ Updating [${path}]`);
3834
}
3935
if (!isDryRun) {
40-
const r = await writeFile(
36+
await writeFile(
4137
owner,
4238
repo,
4339
path,
@@ -47,12 +43,12 @@ module.exports = async function (
4743
);
4844
}
4945
} else {
50-
if (isVerbose) {
46+
if (verbose) {
5147
console.log(`✏️ No changes detected in [${path}]`);
5248
}
5349
}
5450
} catch (e) {
55-
if (isVerbose) {
51+
if (verbose) {
5652
console.log(`✏️ Unable to update [${path}]`);
5753
}
5854
}

0 commit comments

Comments
 (0)