pob 38.11.0 → 38.13.0
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/lib/generators/app/PobAppGenerator.js +25 -5
- package/lib/generators/app/vite/AppViteGenerator.js +50 -3
- package/lib/generators/app/vite/templates/index.html.ejs +11 -0
- package/lib/generators/app/vite/templates/index.ts.ejs +1 -0
- package/lib/generators/app/vite/templates/vite.config.js.ejs +8 -0
- package/lib/generators/common/babel/templates/app.rollup.config.mjs.ejs +1 -0
- package/lib/generators/common/babel/templates/lib.rollup.config.mjs.ejs +1 -0
- package/lib/generators/common/e2e/CommonE2eGenerator.js +6 -1
- package/lib/generators/common/format-lint/CommonLintGenerator.js +141 -85
- package/lib/generators/common/format-lint/templates/oxfmtrc.jsonc.ejs +1 -0
- package/lib/generators/common/husky/templates/lint-staged.config.js.txt +1 -0
- package/lib/generators/common/release/templates/workflow-release.yml.ejs +21 -10
- package/lib/generators/common/testing/templates/vite.config.js.ejs +1 -0
- package/lib/generators/common/transpiler/templates/app.rollup.config.mjs.ejs +1 -0
- package/lib/generators/common/transpiler/templates/lib.rollup.config.mjs.ejs +1 -0
- package/lib/generators/common/typescript/CommonTypescriptGenerator.js +13 -1
- package/lib/generators/common/typescript/templates/tsconfig.check-js.json.ejs +1 -0
- package/lib/generators/common/typescript/templates/tsconfig.json.ejs +1 -0
- package/lib/generators/common/typescript/templates/tsconfig.tools.json.ejs +1 -0
- package/lib/generators/core/bun/templates/bunfig.toml.ejs +1 -0
- package/lib/generators/core/ci/templates/github-action-documentation-workflow.yml.ejs +2 -1
- package/lib/generators/core/ci/templates/github-action-push-workflow-split.yml.ejs +6 -5
- package/lib/generators/core/ci/templates/github-action-push-workflow.yml.ejs +2 -1
- package/lib/generators/core/claude/CoreClaudeGenerator.js +1 -1
- package/lib/generators/core/editorconfig/templates/editorconfig +1 -0
- package/lib/generators/core/git/CoreGitGenerator.js +72 -57
- package/lib/generators/core/git/generators/github/CoreGitGithubGenerator.js +192 -145
- package/lib/generators/core/gitignore/templates/gitignore.ejs +1 -0
- package/lib/generators/core/npm/templates/npmignore.ejs +1 -0
- package/lib/generators/core/package/CorePackageGenerator.js +21 -8
- package/lib/generators/core/pnpm/CorePnpmGenerator.js +30 -9
- package/lib/generators/core/vscode/templates/extensions.json.ejs +1 -0
- package/lib/generators/core/vscode/templates/settings.json.ejs +1 -0
- package/lib/generators/core/yarn/templates/yarn_gitignore.ejs +1 -0
- package/lib/generators/lib/PobLibGenerator.js +1 -0
- package/lib/generators/lib/doc/LibDocGenerator.js +3 -1
- package/lib/generators/lib/doc/templates/tsconfig.doc.json.ejs +1 -0
- package/lib/generators/lib/doc/templates/tsconfig.doc.json.lerna.ejs +1 -0
- package/lib/generators/monorepo/PobMonorepoGenerator.js +26 -5
- package/lib/generators/monorepo/templates/monorepo.rollup.config.mjs.ejs +1 -0
- package/lib/generators/monorepo/typescript/MonorepoTypescriptGenerator.js +21 -10
- package/lib/generators/monorepo/typescript/templates/tsconfig.json.ejs +1 -0
- package/lib/generators/monorepo/typescript/templates/tsconfig.tools.json.ejs +1 -0
- package/lib/generators/monorepo/workspaces/MonorepoWorkspacesGenerator.js +12 -4
- package/lib/generators/pob/PobBaseGenerator.js +1 -1
- package/lib/pob.js +49 -12
- package/lib/utils/git.js +34 -0
- package/lib/utils/git.test.js +71 -0
- package/lib/utils/workspaceUtils.js +10 -0
- package/lib/utils/workspaceUtils.test.js +15 -0
- package/package.json +13 -13
- package/lib/generators/common/format-lint/templates/eslint.config.js.ejs +0 -5
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# Generated by pob: do not edit, it is overwritten on each pob run.
|
|
1
2
|
name: Push CI
|
|
2
3
|
permissions:
|
|
3
4
|
contents: read
|
|
@@ -25,7 +26,7 @@ jobs:
|
|
|
25
26
|
- name: Install pnpm
|
|
26
27
|
uses: pnpm/action-setup@v6
|
|
27
28
|
with:
|
|
28
|
-
version:
|
|
29
|
+
version: 12
|
|
29
30
|
|
|
30
31
|
<% } -%>
|
|
31
32
|
- uses: actions/setup-node@v7
|
|
@@ -70,7 +71,7 @@ jobs:
|
|
|
70
71
|
- name: Install pnpm
|
|
71
72
|
uses: pnpm/action-setup@v6
|
|
72
73
|
with:
|
|
73
|
-
version:
|
|
74
|
+
version: 12
|
|
74
75
|
|
|
75
76
|
<% } -%>
|
|
76
77
|
- uses: actions/setup-node@v7
|
|
@@ -117,7 +118,7 @@ jobs:
|
|
|
117
118
|
- name: Install pnpm
|
|
118
119
|
uses: pnpm/action-setup@v6
|
|
119
120
|
with:
|
|
120
|
-
version:
|
|
121
|
+
version: 12
|
|
121
122
|
|
|
122
123
|
<% } -%>
|
|
123
124
|
- uses: actions/setup-node@v7
|
|
@@ -183,7 +184,7 @@ jobs:
|
|
|
183
184
|
- name: Install pnpm
|
|
184
185
|
uses: pnpm/action-setup@v6
|
|
185
186
|
with:
|
|
186
|
-
version:
|
|
187
|
+
version: 12
|
|
187
188
|
|
|
188
189
|
<% } -%>
|
|
189
190
|
- uses: actions/setup-node@v7
|
|
@@ -259,7 +260,7 @@ jobs:
|
|
|
259
260
|
- name: Install pnpm
|
|
260
261
|
uses: pnpm/action-setup@v6
|
|
261
262
|
with:
|
|
262
|
-
version:
|
|
263
|
+
version: 12
|
|
263
264
|
|
|
264
265
|
<% } -%>
|
|
265
266
|
- uses: actions/setup-node@v7
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# Generated by pob: do not edit, it is overwritten on each pob run.
|
|
1
2
|
name: Push CI
|
|
2
3
|
permissions:
|
|
3
4
|
contents: read
|
|
@@ -28,7 +29,7 @@ jobs:
|
|
|
28
29
|
- name: Install pnpm
|
|
29
30
|
uses: pnpm/action-setup@v6
|
|
30
31
|
with:
|
|
31
|
-
version:
|
|
32
|
+
version: 12
|
|
32
33
|
|
|
33
34
|
<% } -%>
|
|
34
35
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
@@ -26,7 +26,7 @@ export default class CoreClaudeGenerator extends Generator {
|
|
|
26
26
|
|
|
27
27
|
this.fs.write(
|
|
28
28
|
claudeGitignorePath,
|
|
29
|
-
"/settings.local.json\n/skills/local.*\n",
|
|
29
|
+
"# Generated by pob: do not edit, it is overwritten on each pob run.\n/settings.local.json\n/skills/local.*\n",
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
const command =
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import remoteUrl from "git-remote-url";
|
|
2
2
|
import githubUsername from "github-username";
|
|
3
3
|
import Generator from "yeoman-generator";
|
|
4
|
+
import { getRepoName, parseRepositoryUrl } from "../../../utils/git.js";
|
|
4
5
|
import * as packageUtils from "../../../utils/package.js";
|
|
5
6
|
|
|
7
|
+
const gitHostDomains = {
|
|
8
|
+
github: "github.com",
|
|
9
|
+
gitlab: "gitlab.com",
|
|
10
|
+
bitbucket: "bitbucket.org",
|
|
11
|
+
};
|
|
12
|
+
|
|
6
13
|
export default class CoreGitGenerator extends Generator {
|
|
7
14
|
constructor(args, opts) {
|
|
8
15
|
super(args, opts);
|
|
@@ -34,29 +41,29 @@ export default class CoreGitGenerator extends Generator {
|
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
async initializing() {
|
|
37
|
-
|
|
44
|
+
// the real remote, kept apart from the package.json fallback: only a real
|
|
45
|
+
// remote means the repository already exists
|
|
46
|
+
this.originUrl = await remoteUrl(this.destinationPath(), "origin").catch(
|
|
38
47
|
() => "",
|
|
39
48
|
);
|
|
40
49
|
|
|
41
|
-
|
|
50
|
+
let repository = parseRepositoryUrl(this.originUrl);
|
|
51
|
+
|
|
52
|
+
if (!this.originUrl) {
|
|
42
53
|
const pkg = this.fs.readJSON(this.destinationPath("package.json"), {});
|
|
43
|
-
|
|
54
|
+
repository = parseRepositoryUrl(pkg.repository);
|
|
55
|
+
if (pkg.repository && !repository) {
|
|
56
|
+
console.warn(
|
|
57
|
+
`git: ignoring invalid repository in package.json: ${JSON.stringify(pkg.repository)}`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
|
|
46
|
-
this.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
/^(?:git@|https?:\/\/)(?:([^./:]+)(?:\.com)?[/:])?([^/:]+)\/([^./:]+)(?:.git)?/,
|
|
52
|
-
);
|
|
53
|
-
if (!match) return;
|
|
54
|
-
const [, gitHost, gitAccount, repoName] = match;
|
|
55
|
-
this.gitHost = gitHost || "github";
|
|
56
|
-
this.gitHostAccount = gitAccount;
|
|
57
|
-
if (repoName !== "undefined") {
|
|
58
|
-
this.repoName = repoName;
|
|
59
|
-
}
|
|
62
|
+
this.hasRepositoryUrl = !!(this.originUrl || repository);
|
|
63
|
+
if (!repository) return;
|
|
64
|
+
this.gitHost = repository.gitHost;
|
|
65
|
+
this.gitHostAccount = repository.gitAccount;
|
|
66
|
+
this.repoName = repository.repoName;
|
|
60
67
|
}
|
|
61
68
|
|
|
62
69
|
async prompting() {
|
|
@@ -72,9 +79,12 @@ export default class CoreGitGenerator extends Generator {
|
|
|
72
79
|
type: "list",
|
|
73
80
|
name: "gitHost",
|
|
74
81
|
message: "Which git host service would you like ?",
|
|
75
|
-
default: this.gitHost || (this.
|
|
82
|
+
default: this.gitHost || (this.hasRepositoryUrl ? "none" : "github"),
|
|
76
83
|
choices: [
|
|
77
|
-
{
|
|
84
|
+
{
|
|
85
|
+
value: "none",
|
|
86
|
+
name: !this.hasRepositoryUrl ? "none" : "don't change",
|
|
87
|
+
},
|
|
78
88
|
"github",
|
|
79
89
|
"bitbucket",
|
|
80
90
|
"gitlab",
|
|
@@ -121,23 +131,35 @@ export default class CoreGitGenerator extends Generator {
|
|
|
121
131
|
writing() {
|
|
122
132
|
console.log("git: writing");
|
|
123
133
|
|
|
124
|
-
if (this.gitHost === "none") {
|
|
134
|
+
if (this.gitHost === "none" || !this.gitHostAccount) {
|
|
125
135
|
return;
|
|
126
136
|
}
|
|
127
137
|
|
|
128
138
|
const pkg = this.fs.readJSON(this.destinationPath("package.json"), {});
|
|
129
|
-
const repoName = this.repoName ||
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
const repoName = this.repoName || getRepoName(pkg.name);
|
|
140
|
+
const repositoryUrl = `https://${this.gitHost}.com/${this.gitHostAccount}/${repoName}`;
|
|
141
|
+
|
|
142
|
+
// keep a custom homepage, but fix one pointing to another repository on the
|
|
143
|
+
// same host
|
|
144
|
+
const isHomepageInRepository = (homepage) => {
|
|
145
|
+
const lowerHomepage = homepage.toLowerCase();
|
|
146
|
+
const lowerRepositoryUrl = repositoryUrl.toLowerCase();
|
|
147
|
+
return (
|
|
148
|
+
lowerHomepage === lowerRepositoryUrl ||
|
|
149
|
+
lowerHomepage.startsWith(`${lowerRepositoryUrl}/`) ||
|
|
150
|
+
lowerHomepage.startsWith(`${lowerRepositoryUrl}#`)
|
|
151
|
+
);
|
|
152
|
+
};
|
|
153
|
+
if (
|
|
154
|
+
!pkg.homepage ||
|
|
155
|
+
(pkg.homepage.startsWith(`https://${this.gitHost}.com/`) &&
|
|
156
|
+
!isHomepageInRepository(pkg.homepage))
|
|
157
|
+
) {
|
|
158
|
+
pkg.homepage = repositoryUrl;
|
|
138
159
|
}
|
|
160
|
+
pkg.bugs = { url: `${repositoryUrl}/issues` };
|
|
139
161
|
|
|
140
|
-
const repository =
|
|
162
|
+
const repository = `${repositoryUrl}.git`;
|
|
141
163
|
|
|
142
164
|
if (pkg.repository !== repository) {
|
|
143
165
|
pkg.repository = repository;
|
|
@@ -147,37 +169,30 @@ export default class CoreGitGenerator extends Generator {
|
|
|
147
169
|
|
|
148
170
|
const cwd = this.destinationPath();
|
|
149
171
|
|
|
150
|
-
|
|
151
|
-
this.
|
|
172
|
+
const isGitRepository =
|
|
173
|
+
this.spawnSync("git", ["rev-parse", "--git-dir"], {
|
|
152
174
|
cwd,
|
|
153
175
|
stdio: "ignore",
|
|
154
176
|
reject: false,
|
|
155
|
-
}).
|
|
156
|
-
if (
|
|
157
|
-
this.
|
|
158
|
-
|
|
159
|
-
if (!this.originUrl) {
|
|
160
|
-
let repoSSH = pkg.repository;
|
|
161
|
-
if (pkg.repository && !pkg.repository.includes(".git")) {
|
|
162
|
-
/* this.spawnCommandSync('curl', [
|
|
163
|
-
'--silent',
|
|
164
|
-
'--write-out',
|
|
165
|
-
'"%{http_code}"',
|
|
166
|
-
'--output',
|
|
167
|
-
'/dev/null',
|
|
168
|
-
'-i',
|
|
169
|
-
'-u',
|
|
170
|
-
this.options.githubAccount,
|
|
171
|
-
`-d "{"name": "${this.options.name}", "auto_init": true}`,
|
|
172
|
-
'https://api.github.com/user/repos',
|
|
173
|
-
], { cwd }); */
|
|
174
|
-
|
|
175
|
-
repoSSH = pkg.repository;
|
|
176
|
-
}
|
|
177
|
+
}).exitCode === 0;
|
|
178
|
+
if (!isGitRepository) {
|
|
179
|
+
this.spawnSync("git", ["init"], { cwd });
|
|
180
|
+
}
|
|
177
181
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
if (!this.originUrl) {
|
|
183
|
+
const remoteHost = gitHostDomains[this.gitHost];
|
|
184
|
+
if (remoteHost) {
|
|
185
|
+
const originSSH = `git@${remoteHost}:${this.gitHostAccount}/${repoName}.git`;
|
|
186
|
+
const { exitCode } = this.spawnSync(
|
|
187
|
+
"git",
|
|
188
|
+
["remote", "add", "origin", originSSH],
|
|
189
|
+
{ cwd, reject: false },
|
|
190
|
+
);
|
|
191
|
+
if (exitCode !== 0) {
|
|
192
|
+
console.warn(
|
|
193
|
+
`git: failed to add origin, run by hand: git remote add origin ${originSSH}`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
181
196
|
}
|
|
182
197
|
}
|
|
183
198
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* oxlint-disable eslint-js/camelcase */
|
|
2
2
|
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import Generator from "yeoman-generator";
|
|
6
|
+
import { getRepoName } from "../../../../../utils/git.js";
|
|
6
7
|
import { ciContexts } from "../../../ci/CoreCIGenerator.js";
|
|
7
|
-
// const packageUtils = require('../../../../../utils/package');
|
|
8
8
|
|
|
9
9
|
const readTokenFromKeychain = () => {
|
|
10
10
|
if (process.platform !== "darwin") return undefined;
|
|
@@ -26,71 +26,36 @@ const readTokenFromKeychain = () => {
|
|
|
26
26
|
|
|
27
27
|
const GITHUB_TOKEN = process.env.POB_GITHUB_TOKEN || readTokenFromKeychain();
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
const MISSING_TOKEN_HELP =
|
|
30
|
+
'Create a token with https://github.com/settings/tokens/new?scopes=repo&description=POB%20Generator and set it in the POB_GITHUB_TOKEN env variable, or store it in the macOS keychain with: security add-generic-password -s pob-github-token -a "$USER" -w';
|
|
31
|
+
|
|
32
|
+
class GithubRequestError extends Error {
|
|
33
|
+
constructor(method, url, status, body) {
|
|
34
|
+
super(`${method} ${url} failed with status ${status}: ${body}`);
|
|
35
|
+
this.name = "GithubRequestError";
|
|
36
|
+
this.status = status;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
37
39
|
|
|
38
|
-
const
|
|
39
|
-
fetch(`https://api.github.com/${url}`, {
|
|
40
|
-
method
|
|
41
|
-
body: JSON.stringify(jsonBody),
|
|
40
|
+
const githubRequest = async (method, url, jsonBody) => {
|
|
41
|
+
const res = await fetch(`https://api.github.com/${url}`, {
|
|
42
|
+
method,
|
|
43
|
+
body: jsonBody === undefined ? undefined : JSON.stringify(jsonBody),
|
|
42
44
|
headers: {
|
|
45
|
+
accept: "application/vnd.github+json",
|
|
43
46
|
authorization: `token ${GITHUB_TOKEN}`,
|
|
44
47
|
},
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
repo,
|
|
50
|
-
onlyLatestLTS,
|
|
51
|
-
spawnCommandSync,
|
|
52
|
-
) => {
|
|
53
|
-
if (!ciContexts || ciContexts.length === 0) {
|
|
54
|
-
throw new Error("Invalid ciContexts: []");
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const body = await res.text().catch(() => "");
|
|
51
|
+
throw new GithubRequestError(method, url, res.status, body);
|
|
55
52
|
}
|
|
53
|
+
return res.json();
|
|
54
|
+
};
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"git",
|
|
61
|
-
["ls-remote", "--heads", `git@github.com:${owner}/${repo}.git`, branch],
|
|
62
|
-
{ stdio: "pipe" },
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const isBranchExists =
|
|
66
|
-
result.exitCode === 0 && result.stdout.toString().trim() !== "";
|
|
67
|
-
|
|
68
|
-
if (isBranchExists) {
|
|
69
|
-
await putJson(`repos/${owner}/${repo}/branches/${branch}/protection`, {
|
|
70
|
-
required_status_checks: {
|
|
71
|
-
strict: false,
|
|
72
|
-
contexts: ciContexts,
|
|
73
|
-
},
|
|
74
|
-
enforce_admins: false, // true,
|
|
75
|
-
required_pull_request_reviews: null,
|
|
76
|
-
restrictions: null,
|
|
77
|
-
required_linear_history: true,
|
|
78
|
-
allow_force_pushes: true, // false
|
|
79
|
-
allow_deletions: false,
|
|
80
|
-
});
|
|
81
|
-
if (branch === "master") {
|
|
82
|
-
console.warn('You should rename your "master" branch to "main"');
|
|
83
|
-
}
|
|
84
|
-
} else if (branch === "main") {
|
|
85
|
-
throw new Error(`Branch ${branch} does not exist`);
|
|
86
|
-
}
|
|
87
|
-
} catch (error) {
|
|
88
|
-
if (branch === "main") {
|
|
89
|
-
console.error(`Failed to configure ${branch} branch protection`);
|
|
90
|
-
console.error(error.stack || error.message || error);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
56
|
+
const printError = (message, error) => {
|
|
57
|
+
console.error(message);
|
|
58
|
+
console.error(error.stack || error.message || error);
|
|
94
59
|
};
|
|
95
60
|
|
|
96
61
|
const githubRepoConfig = {
|
|
@@ -123,8 +88,8 @@ export default class CoreGitGithubGenerator extends Generator {
|
|
|
123
88
|
|
|
124
89
|
this.option("repoName", {
|
|
125
90
|
type: String,
|
|
126
|
-
required:
|
|
127
|
-
description: "repo name",
|
|
91
|
+
required: false,
|
|
92
|
+
description: "repo name, computed from the package name when missing",
|
|
128
93
|
});
|
|
129
94
|
|
|
130
95
|
this.option("onlyLatestLTS", {
|
|
@@ -139,107 +104,189 @@ export default class CoreGitGithubGenerator extends Generator {
|
|
|
139
104
|
description: "split CI jobs for faster result",
|
|
140
105
|
});
|
|
141
106
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
107
|
+
this.option("ciEnabled", {
|
|
108
|
+
type: Boolean,
|
|
109
|
+
required: true,
|
|
110
|
+
description: "ci enabled",
|
|
111
|
+
});
|
|
147
112
|
}
|
|
148
113
|
|
|
149
|
-
async
|
|
150
|
-
if (!
|
|
151
|
-
|
|
152
|
-
|
|
114
|
+
async configureProtectionRule(owner, repo) {
|
|
115
|
+
if (!this.options.ciEnabled) return;
|
|
116
|
+
if (ciContexts.length === 0) {
|
|
117
|
+
console.warn("No ci contexts: branch protection not configured");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
153
120
|
|
|
154
|
-
const
|
|
155
|
-
const name = pkg.name.endsWith("-monorepo")
|
|
156
|
-
? pkg.name.slice(0, -"-monorepo".length)
|
|
157
|
-
: pkg.name;
|
|
121
|
+
const cwd = this.destinationPath();
|
|
158
122
|
|
|
159
|
-
|
|
123
|
+
for (const branch of ["main", "master"]) {
|
|
160
124
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
125
|
+
const result = this.spawnSync(
|
|
126
|
+
"git",
|
|
127
|
+
["ls-remote", "--heads", "origin", branch],
|
|
128
|
+
{ cwd, stdio: "pipe", reject: false },
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const isBranchExists =
|
|
132
|
+
result.exitCode === 0 && result.stdout.toString().trim() !== "";
|
|
133
|
+
|
|
134
|
+
if (isBranchExists) {
|
|
135
|
+
await githubRequest(
|
|
136
|
+
"PUT",
|
|
137
|
+
`repos/${owner}/${repo}/branches/${branch}/protection`,
|
|
138
|
+
{
|
|
139
|
+
required_status_checks: {
|
|
140
|
+
strict: false,
|
|
141
|
+
contexts: ciContexts,
|
|
142
|
+
},
|
|
143
|
+
enforce_admins: false, // true,
|
|
144
|
+
required_pull_request_reviews: null,
|
|
145
|
+
restrictions: null,
|
|
146
|
+
required_linear_history: true,
|
|
147
|
+
allow_force_pushes: true, // false
|
|
148
|
+
allow_deletions: false,
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
if (branch === "master") {
|
|
152
|
+
console.warn('You should rename your "master" branch to "main"');
|
|
174
153
|
}
|
|
154
|
+
} else if (branch === "main") {
|
|
155
|
+
throw new Error(`Branch ${branch} does not exist`);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (branch === "main") {
|
|
159
|
+
printError(`Failed to configure ${branch} branch protection`, error);
|
|
175
160
|
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Runs a step of the repository setup, printing which step failed and what
|
|
167
|
+
* to run by hand instead of stopping the run.
|
|
168
|
+
*/
|
|
169
|
+
runStep(description, manualCommand, fn) {
|
|
170
|
+
try {
|
|
171
|
+
fn();
|
|
172
|
+
return true;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
printError(
|
|
175
|
+
`Failed to ${description}. Run by hand: ${manualCommand}`,
|
|
176
|
+
error,
|
|
177
|
+
);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async createRepository(owner, repo, pkg) {
|
|
183
|
+
const cwd = this.destinationPath();
|
|
176
184
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
185
|
+
try {
|
|
186
|
+
const user = await githubRequest("GET", "user");
|
|
187
|
+
const isOrganization = user.login.toLowerCase() !== owner.toLowerCase();
|
|
188
|
+
await githubRequest(
|
|
189
|
+
"POST",
|
|
190
|
+
isOrganization ? `orgs/${owner}/repos` : "user/repos",
|
|
191
|
+
{
|
|
192
|
+
name: repo,
|
|
193
|
+
description: pkg.description,
|
|
194
|
+
homepage: null,
|
|
195
|
+
// only projects with an open source license are public: making a
|
|
196
|
+
// pushed public repository private does not undo its exposure
|
|
197
|
+
private: !pkg.license || pkg.license === "UNLICENSED",
|
|
198
|
+
auto_init: false,
|
|
199
|
+
...githubRepoConfig,
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
} catch (error) {
|
|
203
|
+
if (error.status !== 422) {
|
|
204
|
+
printError(
|
|
205
|
+
`Failed to create github repository ${owner}/${repo}: nothing was committed or pushed`,
|
|
206
|
+
error,
|
|
207
|
+
);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
console.warn(
|
|
211
|
+
`github repository ${owner}/${repo} already exists, syncing its settings`,
|
|
212
|
+
);
|
|
213
|
+
await this.syncRepository(owner, repo, pkg);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const hasCommit =
|
|
217
|
+
this.spawnSync("git", ["rev-parse", "--verify", "HEAD"], {
|
|
218
|
+
cwd,
|
|
219
|
+
stdio: "ignore",
|
|
220
|
+
reject: false,
|
|
221
|
+
}).exitCode === 0;
|
|
222
|
+
|
|
223
|
+
if (!hasCommit) {
|
|
224
|
+
const committed = this.runStep(
|
|
225
|
+
"create the initial commit",
|
|
226
|
+
'git add --all . && git commit -m "chore: initial commit [skip ci]"',
|
|
227
|
+
() => {
|
|
228
|
+
this.spawnSync("git", ["add", "--all", "."], { cwd });
|
|
229
|
+
// generated files are already formatted by pob, and hooks may need
|
|
230
|
+
// tools not installed yet
|
|
231
|
+
this.spawnSync(
|
|
184
232
|
"git",
|
|
185
|
-
[
|
|
186
|
-
|
|
187
|
-
"add",
|
|
188
|
-
"origin",
|
|
189
|
-
`git@github.com:christophehurpeau/${name}.git`,
|
|
190
|
-
],
|
|
191
|
-
{ cwd },
|
|
233
|
+
["commit", "-m", "chore: initial commit [skip ci]"],
|
|
234
|
+
{ cwd, env: { POB_GIT_HOOKS: "0" } },
|
|
192
235
|
);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
"git",
|
|
198
|
-
["commit", "-m", "chore: initial commit [skip ci]"],
|
|
199
|
-
{ cwd },
|
|
200
|
-
);
|
|
201
|
-
this.spawnCommandSync("git", ["branch", "-M", "main"], {
|
|
202
|
-
cwd,
|
|
203
|
-
});
|
|
204
|
-
this.spawnCommandSync("git", ["push", "-u", "origin", "main"], {
|
|
205
|
-
cwd,
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
await configureProtectionRule(
|
|
209
|
-
owner,
|
|
210
|
-
repo,
|
|
211
|
-
this.options.onlyLatestLTS,
|
|
212
|
-
this.spawnCommandSync.bind(this),
|
|
213
|
-
);
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
if (!committed) return;
|
|
239
|
+
}
|
|
214
240
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
241
|
+
const pushed = this.runStep(
|
|
242
|
+
"push to github",
|
|
243
|
+
"git branch -M main && git push -u origin main",
|
|
244
|
+
() => {
|
|
245
|
+
this.spawnSync("git", ["branch", "-M", "main"], { cwd });
|
|
246
|
+
this.spawnSync("git", ["push", "-u", "origin", "main"], { cwd });
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
if (!pushed) return;
|
|
250
|
+
|
|
251
|
+
await this.configureProtectionRule(owner, repo);
|
|
252
|
+
}
|
|
224
253
|
|
|
225
|
-
|
|
254
|
+
async syncRepository(owner, repo, pkg) {
|
|
255
|
+
try {
|
|
256
|
+
await githubRequest("PATCH", `repos/${owner}/${repo}`, {
|
|
226
257
|
name: repo,
|
|
227
|
-
/* pkg.name
|
|
228
|
-
.replace(/-(lerna|monorepo)$/, '')
|
|
229
|
-
.replace(/^@([^-]*)-/, '$1-') */
|
|
230
258
|
description: pkg.description,
|
|
231
|
-
// homepage: null,
|
|
232
259
|
...githubRepoConfig,
|
|
233
260
|
});
|
|
261
|
+
} catch (error) {
|
|
262
|
+
printError(`Failed to sync github repository ${owner}/${repo}`, error);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
234
265
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
266
|
+
async end() {
|
|
267
|
+
const pkg = this.fs.readJSON(this.destinationPath("package.json"), {});
|
|
268
|
+
const owner = this.options.gitHostAccount;
|
|
269
|
+
const repo = this.options.repoName || getRepoName(pkg.name);
|
|
270
|
+
|
|
271
|
+
if (!GITHUB_TOKEN) {
|
|
272
|
+
if (this.options.shouldCreate) {
|
|
273
|
+
console.warn(
|
|
274
|
+
`Missing POB_GITHUB_TOKEN: the github repository ${owner}/${repo} was not created, nothing was committed or pushed. ${MISSING_TOKEN_HELP}`,
|
|
275
|
+
);
|
|
276
|
+
} else if (process.env.CI !== "true") {
|
|
277
|
+
console.warn(
|
|
278
|
+
`Missing POB_GITHUB_TOKEN: github repository settings not synced. ${MISSING_TOKEN_HELP}`,
|
|
241
279
|
);
|
|
242
280
|
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (this.options.shouldCreate) {
|
|
285
|
+
await this.createRepository(owner, repo, pkg);
|
|
286
|
+
} else {
|
|
287
|
+
console.log("sync github info");
|
|
288
|
+
await this.syncRepository(owner, repo, pkg);
|
|
289
|
+
await this.configureProtectionRule(owner, repo);
|
|
243
290
|
}
|
|
244
291
|
}
|
|
245
292
|
}
|