@rtorcato/repo-tooling 3.10.2 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base/github-settings.js +30 -4
- package/dist/cli/generators/build.js +4 -30
- package/dist/cli/generators/docs-site.js +5 -1
- package/dist/cli/generators/package-json.js +3 -5
- package/package.json +1 -3
- package/skills/ai-issue-loop/SKILL.md +8 -4
- package/tooling/docusaurus/sync-changelog.mjs +75 -11
- package/tooling/semantic-release/github.mjs +29 -17
|
@@ -115,7 +115,15 @@ async function probeRepo(exec) {
|
|
|
115
115
|
autoMerge: d.allow_auto_merge === true,
|
|
116
116
|
squashMerge: d.allow_squash_merge === true,
|
|
117
117
|
deleteOnMerge: d.delete_branch_on_merge === true,
|
|
118
|
-
|
|
118
|
+
mergeCommit: d.allow_merge_commit === true,
|
|
119
|
+
rebaseMerge: d.allow_rebase_merge === true,
|
|
120
|
+
mergeVisible: [
|
|
121
|
+
'allow_auto_merge',
|
|
122
|
+
'allow_squash_merge',
|
|
123
|
+
'delete_branch_on_merge',
|
|
124
|
+
'allow_merge_commit',
|
|
125
|
+
'allow_rebase_merge',
|
|
126
|
+
].every((k) => typeof d[k] === 'boolean'),
|
|
119
127
|
},
|
|
120
128
|
};
|
|
121
129
|
}
|
|
@@ -197,6 +205,16 @@ async function checkBranchProtection(exec, nwo, branch) {
|
|
|
197
205
|
};
|
|
198
206
|
return { check, status: 'ok', detail: `${branch} protected per standard` };
|
|
199
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Squash is required *and exclusive* (#410). Leaving merge-commit or rebase
|
|
210
|
+
* enabled only makes them available on the merge button, and one mis-click puts
|
|
211
|
+
* every intermediate branch commit on the default branch: semantic-release then
|
|
212
|
+
* reads subjects nobody reviewed (a stray `fix:` inside a docs PR cuts a
|
|
213
|
+
* release), and `ai-issue-loop`'s Pass 2 stops finding the `(#N)` squash subject
|
|
214
|
+
* it uses to confirm work landed, so worktrees leak. Observed on `js-common`
|
|
215
|
+
* #204, whose CONTRIBUTING already said squash-only — the rule existed, nothing
|
|
216
|
+
* enforced it.
|
|
217
|
+
*/
|
|
200
218
|
function checkMergeSettings(info) {
|
|
201
219
|
const check = 'Merge settings';
|
|
202
220
|
// The token can't see the merge-setting fields (no admin:read) — skip rather
|
|
@@ -210,14 +228,18 @@ function checkMergeSettings(info) {
|
|
|
210
228
|
deltas.push('squash-merge disabled');
|
|
211
229
|
if (!info.deleteOnMerge)
|
|
212
230
|
deltas.push('delete-branch-on-merge disabled');
|
|
231
|
+
if (info.mergeCommit)
|
|
232
|
+
deltas.push('merge commits allowed (squash only)');
|
|
233
|
+
if (info.rebaseMerge)
|
|
234
|
+
deltas.push('rebase merging allowed (squash only)');
|
|
213
235
|
if (deltas.length)
|
|
214
236
|
return {
|
|
215
237
|
check,
|
|
216
238
|
status: 'drift',
|
|
217
239
|
detail: deltas.join('; '),
|
|
218
|
-
hint: 'Enable auto-merge
|
|
240
|
+
hint: 'Enable auto-merge and delete-branch-on-merge, and make squash the only merge method',
|
|
219
241
|
};
|
|
220
|
-
return { check, status: 'ok', detail: '
|
|
242
|
+
return { check, status: 'ok', detail: 'squash-only, auto-merge and delete-on-merge on' };
|
|
221
243
|
}
|
|
222
244
|
async function checkWorkflowPermissions(exec, nwo) {
|
|
223
245
|
const check = 'Workflow permissions';
|
|
@@ -370,7 +392,7 @@ export function buildGhApplyCommands(state) {
|
|
|
370
392
|
const commands = [];
|
|
371
393
|
if (state.merge)
|
|
372
394
|
commands.push({
|
|
373
|
-
label: 'merge settings (auto-merge,
|
|
395
|
+
label: 'merge settings (squash-only, auto-merge, delete-on-merge)',
|
|
374
396
|
args: [
|
|
375
397
|
'api',
|
|
376
398
|
'-X',
|
|
@@ -382,6 +404,10 @@ export function buildGhApplyCommands(state) {
|
|
|
382
404
|
'allow_squash_merge=true',
|
|
383
405
|
'-F',
|
|
384
406
|
'delete_branch_on_merge=true',
|
|
407
|
+
'-F',
|
|
408
|
+
'allow_merge_commit=false',
|
|
409
|
+
'-F',
|
|
410
|
+
'allow_rebase_merge=false',
|
|
385
411
|
],
|
|
386
412
|
});
|
|
387
413
|
if (state.protection)
|
|
@@ -123,41 +123,15 @@ export default mergeConfig(preset, defineConfig({ plugins: [react()] }))
|
|
|
123
123
|
`;
|
|
124
124
|
await fs.writeFile(viteConfigPath, viteConfig);
|
|
125
125
|
}
|
|
126
|
-
// Plugins the github/gitlab preset activates that semantic-release core does
|
|
127
|
-
// NOT bundle (core bundles only commit-analyzer, release-notes-generator, npm,
|
|
128
|
-
// github). Without these in the consumer's deps, `semantic-release` crashes
|
|
129
|
-
// with "Cannot find module '@semantic-release/changelog'" on first run.
|
|
130
|
-
const RELEASE_PLUGIN_DEPS = {
|
|
131
|
-
'@semantic-release/changelog': '^6.0.0',
|
|
132
|
-
'@semantic-release/git': '^10.0.0',
|
|
133
|
-
};
|
|
134
126
|
export async function generateSemanticReleaseConfig(targetDir) {
|
|
135
127
|
const releaseConfigPath = path.join(targetDir, 'release.config.mjs');
|
|
136
128
|
const releaseConfig = `export { default } from '@rtorcato/repo-tooling/semantic-release/github'
|
|
137
129
|
`;
|
|
130
|
+
// No extra plugin deps to inject: the github preset now uses only what
|
|
131
|
+
// semantic-release core bundles (commit-analyzer, release-notes-generator,
|
|
132
|
+
// npm, github). The changelog and git plugins are gone — see #417.
|
|
138
133
|
await fs.writeFile(releaseConfigPath, releaseConfig);
|
|
139
|
-
|
|
140
|
-
// Ensure the preset's non-bundled plugins are installed; otherwise the
|
|
141
|
-
// scaffolded release.config.mjs references modules the consumer lacks.
|
|
142
|
-
const pkgPath = path.join(targetDir, 'package.json');
|
|
143
|
-
if (await fs.pathExists(pkgPath)) {
|
|
144
|
-
const pkg = (await fs.readJson(pkgPath));
|
|
145
|
-
const devDeps = (pkg.devDependencies ?? {});
|
|
146
|
-
const deps = (pkg.dependencies ?? {});
|
|
147
|
-
let changed = false;
|
|
148
|
-
for (const [name, version] of Object.entries(RELEASE_PLUGIN_DEPS)) {
|
|
149
|
-
if (!devDeps[name] && !deps[name]) {
|
|
150
|
-
devDeps[name] = version;
|
|
151
|
-
changed = true;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (changed) {
|
|
155
|
-
pkg.devDependencies = devDeps;
|
|
156
|
-
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
157
|
-
written.push('package.json');
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return written;
|
|
134
|
+
return ['release.config.mjs'];
|
|
161
135
|
}
|
|
162
136
|
export async function generateChangesetsConfig(targetDir) {
|
|
163
137
|
// Drop the canonical Changesets config into .changeset/config.json. The user
|
|
@@ -333,8 +333,12 @@ on:
|
|
|
333
333
|
branches: [main]
|
|
334
334
|
paths:
|
|
335
335
|
- 'apps/docs/**'
|
|
336
|
-
- 'CHANGELOG.md'
|
|
337
336
|
- '.github/workflows/docs.yml'
|
|
337
|
+
# The changelog page is built from GitHub Releases, and no release commit
|
|
338
|
+
# lands on main any more (see #417) — so a push trigger alone would never
|
|
339
|
+
# rebuild the site after a release.
|
|
340
|
+
release:
|
|
341
|
+
types: [published]
|
|
338
342
|
workflow_dispatch:
|
|
339
343
|
|
|
340
344
|
jobs:
|
|
@@ -377,14 +377,12 @@ function getDependencies(config) {
|
|
|
377
377
|
deps['commitizen'] = '^4.3.1';
|
|
378
378
|
deps['cz-conventional-changelog'] = '^3.3.0';
|
|
379
379
|
}
|
|
380
|
-
// Semantic release. The shipped github preset
|
|
381
|
-
//
|
|
382
|
-
//
|
|
380
|
+
// Semantic release. The shipped github preset uses only plugins that
|
|
381
|
+
// semantic-release core bundles, plus @semantic-release/github — no
|
|
382
|
+
// changelog/git plugins (see tooling/semantic-release/github.mjs, #417).
|
|
383
383
|
if (config.semanticRelease) {
|
|
384
384
|
deps['semantic-release'] = '^25.0.0';
|
|
385
385
|
deps['@semantic-release/github'] = '^12.0.0';
|
|
386
|
-
deps['@semantic-release/changelog'] = '^6.0.0';
|
|
387
|
-
deps['@semantic-release/git'] = '^10.0.0';
|
|
388
386
|
}
|
|
389
387
|
return deps;
|
|
390
388
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtorcato/repo-tooling",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.1",
|
|
4
4
|
"description": "One CLI to scaffold, audit and fix your repo's whole toolchain — linting, tests, commits, releases & CI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -244,11 +244,9 @@
|
|
|
244
244
|
"@ianvs/prettier-plugin-sort-imports": "^4.4.2",
|
|
245
245
|
"@next/eslint-plugin-next": "^16.2.11",
|
|
246
246
|
"@playwright/test": "^1.62.0",
|
|
247
|
-
"@semantic-release/changelog": "^7.0.0",
|
|
248
247
|
"@rollup/plugin-typescript": "^12.0.0",
|
|
249
248
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
250
249
|
"@semantic-release/exec": "^7.1.0",
|
|
251
|
-
"@semantic-release/git": "^11.0.1",
|
|
252
250
|
"@semantic-release/github": "^12.0.9",
|
|
253
251
|
"@semantic-release/npm": "^13.1.5",
|
|
254
252
|
"@semantic-release/release-notes-generator": "^14.1.1",
|
|
@@ -806,10 +806,14 @@ times against one trivial issue and watch the labels advance.
|
|
|
806
806
|
## Repo prerequisites
|
|
807
807
|
|
|
808
808
|
```bash
|
|
809
|
-
gh api repos/$OWNER_REPO --jq '{allow_squash_merge, allow_auto_merge, delete_branch_on_merge}'
|
|
809
|
+
gh api repos/$OWNER_REPO --jq '{allow_squash_merge, allow_merge_commit, allow_rebase_merge, allow_auto_merge, delete_branch_on_merge}'
|
|
810
810
|
gh api repos/$OWNER_REPO/branches/main/protection --jq '{contexts: .required_status_checks.contexts, reviews: .required_pull_request_reviews}'
|
|
811
811
|
```
|
|
812
812
|
|
|
813
|
-
Need:
|
|
814
|
-
status check, and
|
|
815
|
-
`
|
|
813
|
+
Need: auto-merge + delete-on-merge + squash all true, **`allow_merge_commit` and
|
|
814
|
+
`allow_rebase_merge` both false**, at least one required status check, and
|
|
815
|
+
`required_pull_request_reviews: null`. Squash has to be the *only* method, not
|
|
816
|
+
merely an available one: Pass 2 confirms a PR landed by finding its `(#N)` squash
|
|
817
|
+
subject on `main`, and a merge commit leaves nothing to find — the worktree then
|
|
818
|
+
survives every tick and its `ai-wip` slot leaks. See the `github-pr-workflow`
|
|
819
|
+
skill for the one-time bootstrap.
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
// Canonical sync-changelog for @rtorcato/* docs sites (shipped by
|
|
2
2
|
// @rtorcato/repo-tooling — copy via `repo-tooling copy docusaurus-sync-changelog`).
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
// Builds the docs site's changelog page from **GitHub Releases**, not from
|
|
5
|
+
// CHANGELOG.md. The output file is gitignored — it is regenerated on every docs
|
|
6
|
+
// build (wire it into the docs app's `build`/`start` scripts; pnpm 8 doesn't run
|
|
7
|
+
// `pre*` hooks reliably, so chain it explicitly).
|
|
8
|
+
//
|
|
9
|
+
// Why Releases and not the file: the shipped semantic-release github preset
|
|
10
|
+
// does not use @semantic-release/git, because the `code-scanning-main` ruleset
|
|
11
|
+
// rejects the release commit with GH013 (see tooling/semantic-release/github.mjs
|
|
12
|
+
// and rtorcato/repo-tooling#417). Nothing commits CHANGELOG.md back, so it is
|
|
13
|
+
// frozen at whatever the last release with that plugin wrote — and it freezes at
|
|
14
|
+
// a *real* release, which is why it reads as current. @semantic-release/github
|
|
15
|
+
// writes Releases from the same notes that used to build CHANGELOG.md, so
|
|
16
|
+
// concatenating their bodies reproduces the old page and cannot go stale.
|
|
9
17
|
//
|
|
10
18
|
// The target defaults to Docusaurus's `apps/docs/docs/changelog.md`. Override
|
|
11
19
|
// with the CHANGELOG_TARGET env var (path relative to the repo root) for other
|
|
@@ -13,6 +21,9 @@
|
|
|
13
21
|
// CHANGELOG_TARGET=apps/web/content/docs/changelog.mdx node scripts/sync-changelog.mjs
|
|
14
22
|
// The title/description frontmatter is framework-neutral (both Docusaurus and
|
|
15
23
|
// Fumadocs read it), so only the path changes between frameworks.
|
|
24
|
+
//
|
|
25
|
+
// The docs workflow must rebuild on `release: types: [published]` — with no
|
|
26
|
+
// release commit landing on the default branch, a `push` trigger never fires.
|
|
16
27
|
|
|
17
28
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
18
29
|
import { dirname, resolve } from 'node:path'
|
|
@@ -20,21 +31,74 @@ import { fileURLToPath } from 'node:url'
|
|
|
20
31
|
|
|
21
32
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
22
33
|
const repoRoot = resolve(here, '..')
|
|
23
|
-
const source = resolve(repoRoot, 'CHANGELOG.md')
|
|
24
34
|
const target = resolve(repoRoot, process.env.CHANGELOG_TARGET ?? 'apps/docs/docs/changelog.md')
|
|
25
35
|
|
|
36
|
+
/** `owner/name` — from CI, else from package.json `repository`. */
|
|
37
|
+
function resolveRepo() {
|
|
38
|
+
if (process.env.GITHUB_REPOSITORY) return process.env.GITHUB_REPOSITORY
|
|
39
|
+
const pkg = JSON.parse(readFileSync(resolve(repoRoot, 'package.json'), 'utf8'))
|
|
40
|
+
const url = typeof pkg.repository === 'string' ? pkg.repository : pkg.repository?.url
|
|
41
|
+
const match = /github\.com[/:]([^/]+\/[^/.]+)/.exec(url ?? '')
|
|
42
|
+
if (!match) throw new Error('no GITHUB_REPOSITORY and no github repository in package.json')
|
|
43
|
+
return match[1]
|
|
44
|
+
}
|
|
45
|
+
|
|
26
46
|
const frontmatter = `---
|
|
27
47
|
title: Changelog
|
|
28
|
-
description: Release notes, generated by semantic-release.
|
|
48
|
+
description: Release notes, generated by semantic-release and published to GitHub Releases.
|
|
29
49
|
---
|
|
30
50
|
|
|
31
51
|
`
|
|
32
52
|
|
|
33
|
-
|
|
53
|
+
const footer = (repo, note) =>
|
|
54
|
+
`\n\n---\n\n${note}Every entry above is generated by semantic-release and published to [GitHub Releases](https://github.com/${repo}/releases). Anything older than the first GitHub Release is recorded in [CHANGELOG.md](https://github.com/${repo}/blob/main/CHANGELOG.md).\n`
|
|
55
|
+
|
|
56
|
+
async function fetchReleases(repo) {
|
|
57
|
+
const headers = { Accept: 'application/vnd.github+json' }
|
|
58
|
+
// Optional: lifts the 60/hr unauthenticated rate limit. CI passes it; local
|
|
59
|
+
// builds work fine without it at a typical repo's release count.
|
|
60
|
+
// ponytail: one page of 100. Paginate when a repo outgrows it.
|
|
61
|
+
const token = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN
|
|
62
|
+
if (token) headers.Authorization = `Bearer ${token}`
|
|
63
|
+
|
|
64
|
+
const res = await fetch(`https://api.github.com/repos/${repo}/releases?per_page=100`, { headers })
|
|
65
|
+
if (!res.ok) throw new Error(`GitHub API ${res.status} ${res.statusText}`)
|
|
66
|
+
return res.json()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function render(releases) {
|
|
70
|
+
// The API does not guarantee date order, and a release body already carries
|
|
71
|
+
// its own version heading and date — so sort here and emit bodies verbatim.
|
|
72
|
+
const sorted = releases
|
|
73
|
+
.filter((r) => !r.draft && r.body?.trim())
|
|
74
|
+
.sort((a, b) => new Date(b.published_at) - new Date(a.published_at))
|
|
75
|
+
|
|
76
|
+
if (sorted.length === 0) throw new Error('no releases with a body')
|
|
77
|
+
return sorted.map((r) => r.body.trim()).join('\n\n')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let repo = ''
|
|
81
|
+
let body
|
|
34
82
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
83
|
+
repo = resolveRepo()
|
|
84
|
+
body = render(await fetchReleases(repo)) + footer(repo, '')
|
|
85
|
+
console.log(`sync-changelog: built from ${repo} GitHub Releases`)
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Never fail the docs build on a network blip — fall back to the frozen file
|
|
88
|
+
// and say so on the page rather than shipping a silently truncated one.
|
|
89
|
+
console.warn(`sync-changelog: ${error.message} — falling back to CHANGELOG.md`)
|
|
90
|
+
try {
|
|
91
|
+
body =
|
|
92
|
+
readFileSync(resolve(repoRoot, 'CHANGELOG.md'), 'utf8').trim() +
|
|
93
|
+
(repo
|
|
94
|
+
? footer(
|
|
95
|
+
repo,
|
|
96
|
+
'**This page was built from the repository CHANGELOG.md because GitHub Releases could not be reached, and may be out of date.** '
|
|
97
|
+
)
|
|
98
|
+
: '')
|
|
99
|
+
} catch {
|
|
100
|
+
body = '# Changelog\n\nNo releases recorded yet.\n'
|
|
101
|
+
}
|
|
38
102
|
}
|
|
39
103
|
|
|
40
104
|
mkdirSync(dirname(target), { recursive: true })
|
|
@@ -41,7 +41,10 @@ export default {
|
|
|
41
41
|
[
|
|
42
42
|
'@semantic-release/github',
|
|
43
43
|
{
|
|
44
|
-
|
|
44
|
+
// README.md only. CHANGELOG.md and package.json are frozen on the
|
|
45
|
+
// default branch (see the note at the bottom of this file), so
|
|
46
|
+
// attaching them to each release would ship stale artifacts.
|
|
47
|
+
assets: ['README.md'],
|
|
45
48
|
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
|
|
46
49
|
// Don't label the "release is failing" issue. The default is
|
|
47
50
|
// ['semantic-release'], and GitHub rejects issue creation outright when
|
|
@@ -52,12 +55,6 @@ export default {
|
|
|
52
55
|
labels: false,
|
|
53
56
|
},
|
|
54
57
|
],
|
|
55
|
-
[
|
|
56
|
-
'@semantic-release/changelog',
|
|
57
|
-
{
|
|
58
|
-
changelogFile: 'CHANGELOG.md',
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
58
|
// npm publishing goes through OIDC trusted publishing — no NPM_TOKEN. The
|
|
62
59
|
// release job runs with `id-token: write` and npm (>=11.5.1) authenticates
|
|
63
60
|
// via the GitHub Actions trusted publisher configured on the package, and
|
|
@@ -65,19 +62,34 @@ export default {
|
|
|
65
62
|
// @semantic-release/npm; a public repo that only wants GitHub releases can
|
|
66
63
|
// opt out with NPM_PUBLISH=false (the version in package.json is still bumped).
|
|
67
64
|
['@semantic-release/npm', { npmPublish: process.env.NPM_PUBLISH !== 'false', pkgRoot: '.' }],
|
|
68
|
-
[
|
|
69
|
-
'@semantic-release/git',
|
|
70
|
-
{
|
|
71
|
-
assets: ['package.json', 'CHANGELOG.md'],
|
|
72
|
-
message: 'chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}',
|
|
73
|
-
// Skip hooks by disabling Husky
|
|
74
|
-
skipCommitHooks: true,
|
|
75
|
-
prepareCmd: 'pnpm exec biome format package.json',
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
65
|
],
|
|
79
66
|
}
|
|
80
67
|
|
|
68
|
+
// Two plugins are deliberately absent, and the second follows from the first.
|
|
69
|
+
//
|
|
70
|
+
// @semantic-release/git — it committed package.json + CHANGELOG.md and pushed
|
|
71
|
+
// them straight to the default branch. `fix github-settings` installs a
|
|
72
|
+
// `code-scanning-main` ruleset requiring CodeQL results on that branch, and a
|
|
73
|
+
// commit created seconds earlier can never have them: the push fails with
|
|
74
|
+
// GH013. Unwinnable rather than flaky — the commit must exist to be scanned,
|
|
75
|
+
// and be scanned to exist. See rtorcato/repo-tooling#417.
|
|
76
|
+
//
|
|
77
|
+
// @semantic-release/changelog — it writes CHANGELOG.md, which only
|
|
78
|
+
// @semantic-release/git ever committed back. Without that push it regenerates
|
|
79
|
+
// the file inside the CI workspace and throws it away every release, implying
|
|
80
|
+
// CHANGELOG.md is still maintained when it is frozen. The two are one feature
|
|
81
|
+
// in two plugins; shipping one without the other is the trap, not the fix.
|
|
82
|
+
//
|
|
83
|
+
// The consequence, stated plainly because it is easy to miss: **CHANGELOG.md
|
|
84
|
+
// and the `version` field in package.json stop moving on the default branch.**
|
|
85
|
+
// The git tag, the npm publish and the GitHub Release are unaffected and are
|
|
86
|
+
// the source of truth for what shipped. Build any user-facing changelog from
|
|
87
|
+
// GitHub Releases — `repo-tooling copy docusaurus-sync-changelog` does exactly
|
|
88
|
+
// that — never from the frozen file.
|
|
89
|
+
//
|
|
90
|
+
// Adding a bypass actor to the ruleset would also make the push succeed. It is
|
|
91
|
+
// not the answer: it exempts the one commit nobody reviews.
|
|
92
|
+
|
|
81
93
|
// [
|
|
82
94
|
// '@semantic-release/exec',
|
|
83
95
|
// {
|