@ggdna/dna-gg 0.1.0 → 0.1.2
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/README.md +5 -13
- package/dna/_dna.json +3 -2
- package/dna/_generated.json +22 -6
- package/dna/dot-github/workflows/quick_check.yaml +2 -2
- package/package.json +3 -2
- package/dna/scripts/delete-feature-branch.js +0 -97
- package/dna/scripts/functions/colors.js +0 -27
- package/dna/scripts/functions/current-branch.js +0 -11
- package/dna/scripts/functions/directories.js +0 -22
- package/dna/scripts/functions/get-repo-urls.js +0 -43
- package/dna/scripts/functions/get-version.js +0 -14
- package/dna/scripts/functions/is-clean-repo.js +0 -51
- package/dna/scripts/functions/is-main-up-to-date.js +0 -19
- package/dna/scripts/functions/pull-request-url.js +0 -17
- package/dna/scripts/functions/run-command.js +0 -22
- package/dna/scripts/functions/sync-folders.js +0 -46
- package/dna/scripts/rename-class.js +0 -147
- package/dna/scripts/setup-github-repo.js +0 -221
- package/dna/scripts/wait-for-pr.js +0 -90
package/README.md
CHANGED
|
@@ -22,17 +22,6 @@ publish.
|
|
|
22
22
|
- `/publish` — releases the ticket
|
|
23
23
|
- `/cleanup` — removes what the ticket left behind
|
|
24
24
|
|
|
25
|
-
## Scripts
|
|
26
|
-
|
|
27
|
-
- `dna/scripts/setup-github-repo.js` — applies the branch rules a new repo
|
|
28
|
-
needs, called by the create repo guide
|
|
29
|
-
- `dna/scripts/rename-class.js` — renames a template project to its real
|
|
30
|
-
name
|
|
31
|
-
- `dna/scripts/wait-for-pr.js` — waits for the pull request to merge
|
|
32
|
-
- `dna/scripts/delete-feature-branch.js` — removes the branch after the
|
|
33
|
-
merge
|
|
34
|
-
- `dna/scripts/functions/` — the helpers those scripts share
|
|
35
|
-
|
|
36
25
|
## Configuration
|
|
37
26
|
|
|
38
27
|
- `dna/dot-github/workflows/quick_check.yaml` — the quick check pipeline
|
|
@@ -41,8 +30,11 @@ publish.
|
|
|
41
30
|
## Layers
|
|
42
31
|
|
|
43
32
|
Builds on [dna_install](https://github.com/ggdna/dna_install) for the
|
|
44
|
-
install overview
|
|
45
|
-
|
|
33
|
+
install overview, [dna_index](https://github.com/ggdna/dna_index) for the
|
|
34
|
+
repo index every gg repo keeps, and
|
|
35
|
+
[dna_scripts](https://github.com/ggdna/dna_scripts) for
|
|
36
|
+
`setup-github-repo.js`, `rename-class.js`, `wait-for-pr.js` and
|
|
37
|
+
`delete-feature-branch.js`, which the guides above call.
|
|
46
38
|
|
|
47
39
|
It extends the `@tooling` section of the install overview through
|
|
48
40
|
`dna/doc/guides/install-guide.overrides.md`: it adds `Install gg` and
|
package/dna/_dna.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
// The parent DNAs. dna_install brings the install overview this layer
|
|
7
7
|
// extends with the gg install guides, dna_index the index conventions
|
|
8
|
-
// every gg repo follows
|
|
9
|
-
|
|
8
|
+
// every gg repo follows, dna_scripts the node scripts this layer's
|
|
9
|
+
// guides call. Parents first, this layer wins.
|
|
10
|
+
"layers": ["dna_install", "dna_index", "dna_scripts"]
|
|
10
11
|
}
|
package/dna/_generated.json
CHANGED
|
@@ -11,27 +11,35 @@
|
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "dna-install",
|
|
14
|
-
"package": "
|
|
15
|
-
"ecosystem": "
|
|
14
|
+
"package": "dna_install",
|
|
15
|
+
"ecosystem": "pub",
|
|
16
16
|
"resolvedVersion": "0.1.0",
|
|
17
17
|
"via": null,
|
|
18
18
|
"hash": "0xbe88eb4256745ee1"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "dna-index",
|
|
22
|
-
"package": "
|
|
23
|
-
"ecosystem": "
|
|
22
|
+
"package": "dna_index",
|
|
23
|
+
"ecosystem": "pub",
|
|
24
24
|
"resolvedVersion": "0.1.0",
|
|
25
25
|
"via": null,
|
|
26
26
|
"hash": "0x1d92b0add37a1ac7"
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
"name": "dna-scripts",
|
|
30
|
+
"package": "@ggdna/dna-scripts",
|
|
31
|
+
"ecosystem": "node",
|
|
32
|
+
"resolvedVersion": "0.1.0",
|
|
33
|
+
"via": null,
|
|
34
|
+
"hash": "0x9ee08ef13ab4c8d3"
|
|
35
|
+
},
|
|
28
36
|
{
|
|
29
37
|
"name": "self",
|
|
30
38
|
"package": null,
|
|
31
39
|
"ecosystem": null,
|
|
32
40
|
"resolvedVersion": null,
|
|
33
41
|
"via": null,
|
|
34
|
-
"hash": "
|
|
42
|
+
"hash": "0xd5744033dd70c6a3"
|
|
35
43
|
}
|
|
36
44
|
],
|
|
37
45
|
"claude": {
|
|
@@ -64,13 +72,17 @@
|
|
|
64
72
|
"path": ".claude/skills/push/SKILL.md",
|
|
65
73
|
"hash": "0x76c357ca9933317d"
|
|
66
74
|
},
|
|
75
|
+
{
|
|
76
|
+
"path": ".claude/skills/scripts/SKILL.md",
|
|
77
|
+
"hash": "0xa12593aa0af31ed2"
|
|
78
|
+
},
|
|
67
79
|
{
|
|
68
80
|
"path": ".claude/skills/ticket/SKILL.md",
|
|
69
81
|
"hash": "0xbc13efe6fa9af962"
|
|
70
82
|
},
|
|
71
83
|
{
|
|
72
84
|
"path": ".github/workflows/quick_check.yaml",
|
|
73
|
-
"hash": "
|
|
85
|
+
"hash": "0xf3320fb8f530a565"
|
|
74
86
|
},
|
|
75
87
|
{
|
|
76
88
|
"path": "doc/guides/develop-guide.md",
|
|
@@ -136,6 +148,10 @@
|
|
|
136
148
|
"path": "doc/guides/install-guide/install-vscode-guide.md",
|
|
137
149
|
"hash": "0xa0da6a6877bb94c2"
|
|
138
150
|
},
|
|
151
|
+
{
|
|
152
|
+
"path": "doc/guides/scripts-guide.md",
|
|
153
|
+
"hash": "0x426857c389049083"
|
|
154
|
+
},
|
|
139
155
|
{
|
|
140
156
|
"path": "doc/hello_world.md",
|
|
141
157
|
"hash": "0x0903f7fc5224f6f1"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Runs the gg
|
|
1
|
+
# Runs the gg quick check on every push and pull request against main.
|
|
2
2
|
|
|
3
3
|
name: Tests
|
|
4
4
|
|
|
@@ -17,4 +17,4 @@ jobs:
|
|
|
17
17
|
- uses: actions/checkout@v5
|
|
18
18
|
|
|
19
19
|
# Uses the Node.js preinstalled on the runner image.
|
|
20
|
-
- run: npx @tssuite/
|
|
20
|
+
- run: npx @tssuite/ggwsm one did commit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ggdna/dna-gg",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "DNA layer: gg CLI workflow guides, repo setup scripts and CI check",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dna",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@ggdna/dna-index": "^0.1.0",
|
|
32
|
-
"@ggdna/dna-install": "^0.1.0"
|
|
32
|
+
"@ggdna/dna-install": "^0.1.0",
|
|
33
|
+
"@ggdna/dna-scripts": "^0.1.0"
|
|
33
34
|
},
|
|
34
35
|
"scripts": {
|
|
35
36
|
"test:dart": "dart test",
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { gray, green, red, yellow } from './functions/colors.js';
|
|
10
|
-
import { runCommand } from './functions/run-command.js';
|
|
11
|
-
|
|
12
|
-
// Check for uncommitted changes
|
|
13
|
-
function hasUncommittedChanges() {
|
|
14
|
-
console.log(gray('Check for uncommitted changes'));
|
|
15
|
-
const status = runCommand('git status --porcelain');
|
|
16
|
-
return status.length > 0;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Check for unpushed commits
|
|
20
|
-
function hasUnpushedCommits(branch) {
|
|
21
|
-
try {
|
|
22
|
-
console.log(gray('Check for unpushed commits'));
|
|
23
|
-
runCommand(`git rev-parse --abbrev-ref ${branch}@{u}`);
|
|
24
|
-
const ahead = runCommand(`git rev-list --count ${branch}@{u}..${branch}`);
|
|
25
|
-
return parseInt(ahead, 10) > 0;
|
|
26
|
-
} catch {
|
|
27
|
-
return true;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Try a test merge into main and check if it introduces changes
|
|
32
|
-
function isBranchEffectivelyMerged(featureBranch) {
|
|
33
|
-
console.log(gray('Check if feature was fully merged'));
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
runCommand(`git merge --no-commit --no-ff ${featureBranch}`);
|
|
37
|
-
const changed = hasUncommittedChanges();
|
|
38
|
-
runCommand('git merge --abort');
|
|
39
|
-
return !changed;
|
|
40
|
-
} catch {
|
|
41
|
-
try {
|
|
42
|
-
runCommand('git merge --abort');
|
|
43
|
-
} catch {}
|
|
44
|
-
return false;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
console.log(gray('Get current branch name'));
|
|
50
|
-
const currentBranch = runCommand('git rev-parse --abbrev-ref HEAD');
|
|
51
|
-
|
|
52
|
-
if (currentBranch === 'main') {
|
|
53
|
-
console.log(yellow('Please call this script from a feature branch.'));
|
|
54
|
-
process.exit(0);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (hasUncommittedChanges()) {
|
|
58
|
-
console.error(red('❌ You have uncommitted changes.'));
|
|
59
|
-
console.error(
|
|
60
|
-
yellow('Please commit or stash your changes before continuing.'),
|
|
61
|
-
);
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (hasUnpushedCommits(currentBranch)) {
|
|
66
|
-
console.error(red('❌ You have unpushed commits.'));
|
|
67
|
-
console.error(yellow('Please push your branch before continuing.'));
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
console.log(gray(`Fetching and pulling 'main'...`));
|
|
72
|
-
runCommand('git fetch');
|
|
73
|
-
runCommand('git checkout main');
|
|
74
|
-
runCommand('git pull origin main');
|
|
75
|
-
|
|
76
|
-
const isMerged = isBranchEffectivelyMerged(currentBranch);
|
|
77
|
-
|
|
78
|
-
if (isMerged) {
|
|
79
|
-
runCommand(`git branch -d ${currentBranch}`);
|
|
80
|
-
console.log(green(`✅ Branch '${currentBranch}' has been deleted.`));
|
|
81
|
-
} else {
|
|
82
|
-
console.error(
|
|
83
|
-
red(`❌ Branch '${currentBranch}' is not merged into 'main'.`),
|
|
84
|
-
);
|
|
85
|
-
console.log(
|
|
86
|
-
yellow(`Please (create and) merge a pull request and try again.`),
|
|
87
|
-
);
|
|
88
|
-
runCommand(`git checkout ${currentBranch}`);
|
|
89
|
-
console.log(gray(`Switched back to '${currentBranch}'.`));
|
|
90
|
-
}
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error(red(`Error: ${error.message}`));
|
|
93
|
-
try {
|
|
94
|
-
runCommand('git merge --abort');
|
|
95
|
-
} catch {}
|
|
96
|
-
process.exit(1);
|
|
97
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export const red = (str) => `\x1b[31m${str}\x1b[0m`;
|
|
10
|
-
export const green = (str) => `\x1b[32m${str}\x1b[0m`;
|
|
11
|
-
export const yellow = (str) => `\x1b[33m${str}\x1b[0m`;
|
|
12
|
-
export const blue = (str) => `\x1b[34m${str}\x1b[0m`;
|
|
13
|
-
export const magenta = (str) => `\x1b[35m${str}\x1b[0m`;
|
|
14
|
-
export const cyan = (str) => `\x1b[36m${str}\x1b[0m`;
|
|
15
|
-
export const white = (str) => `\x1b[37m${str}\x1b[0m`;
|
|
16
|
-
export const black = (str) => `\x1b[30m${str}\x1b[0m`;
|
|
17
|
-
export const bgRed = (str) => `\x1b[41m${str}\x1b[0m`;
|
|
18
|
-
export const bgGreen = (str) => `\x1b[42m${str}\x1b[0m`;
|
|
19
|
-
export const bgYellow = (str) => `\x1b[43m${str}\x1b[0m`;
|
|
20
|
-
export const bgBlue = (str) => `\x1b[44m${str}\x1b[0m`;
|
|
21
|
-
export const bgMagenta = (str) => `\x1b[45m${str}\x1b[0m`;
|
|
22
|
-
export const bgCyan = (str) => `\x1b[46m${str}\x1b[0m`;
|
|
23
|
-
export const bgWhite = (str) => `\x1b[47m${str}\x1b[0m`;
|
|
24
|
-
export const bgBlack = (str) => `\x1b[40m${str}\x1b[0m`;
|
|
25
|
-
export const gray = (str) => `\x1b[90m${str}\x1b[0m`;
|
|
26
|
-
export const lightGray = (str) => `\x1b[37m${str}\x1b[0m`;
|
|
27
|
-
export const reset = (str) => `\x1b[0m${str}\x1b[0m`;
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export const currentBranch = () => {
|
|
10
|
-
return execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
|
|
11
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// Get the name of this file
|
|
10
|
-
import { dirname, join } from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = dirname(__filename);
|
|
15
|
-
|
|
16
|
-
export const workSpaceDir = join(__dirname, '..', '..');
|
|
17
|
-
export const distDir = join(workSpaceDir, 'dist');
|
|
18
|
-
export const srcDir = join(workSpaceDir, 'src');
|
|
19
|
-
export const testDir = join(workSpaceDir, 'test');
|
|
20
|
-
export const scriptsDir = join(workSpaceDir, 'scripts');
|
|
21
|
-
export const goldensDir = join(testDir, 'goldens');
|
|
22
|
-
export const nodeModulesDir = join(workSpaceDir, 'node_modules');
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// Import Node.js built-in module for working with child processes
|
|
10
|
-
import { exec } from 'child_process';
|
|
11
|
-
import { promisify } from 'util';
|
|
12
|
-
import { green, yellow } from './colors.js';
|
|
13
|
-
|
|
14
|
-
// Convert exec to return a Promise for async/await usage
|
|
15
|
-
const execAsync = promisify(exec);
|
|
16
|
-
|
|
17
|
-
// GitHub organization or user
|
|
18
|
-
const org = 'rljson';
|
|
19
|
-
|
|
20
|
-
export async function getRepoUrls() {
|
|
21
|
-
try {
|
|
22
|
-
// Use the GitHub CLI to list repositories in JSON format
|
|
23
|
-
const { stdout } = await execAsync(
|
|
24
|
-
`gh repo list ${org} --limit 1000 --json url`,
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
// Parse the JSON output
|
|
28
|
-
const repos = JSON.parse(stdout);
|
|
29
|
-
|
|
30
|
-
// Map to name + URL and print to console
|
|
31
|
-
return repos.map((repo) => repo.url);
|
|
32
|
-
} catch (error) {
|
|
33
|
-
if (error.message.match('gh auth login').length == 1) {
|
|
34
|
-
throw Error(
|
|
35
|
-
[yellow('Not yet logged in. Please run:'), green('gh auth login')].join(
|
|
36
|
-
'\n',
|
|
37
|
-
),
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { readFile } from 'fs/promises';
|
|
10
|
-
|
|
11
|
-
export const getVersion = async () => {
|
|
12
|
-
const packageJson = JSON.parse(await readFile('package.json', 'utf8'));
|
|
13
|
-
return packageJson.version;
|
|
14
|
-
};
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// checkRepo.js
|
|
10
|
-
import { execSync } from 'child_process';
|
|
11
|
-
|
|
12
|
-
export function isCleanRepo(path) {
|
|
13
|
-
const cwd = path ?? '.';
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
// Aktuellen Branch ermitteln
|
|
17
|
-
const branch = execSync('git rev-parse --abbrev-ref HEAD', {
|
|
18
|
-
cwd,
|
|
19
|
-
})
|
|
20
|
-
.toString()
|
|
21
|
-
.trim();
|
|
22
|
-
if (branch !== 'main') {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Git-Fetch ausführen (still)
|
|
27
|
-
execSync('git fetch', { stdio: 'ignore', cwd });
|
|
28
|
-
|
|
29
|
-
// Prüfen, ob Arbeitsverzeichnis sauber ist
|
|
30
|
-
const status = execSync('git status --porcelain', { cwd })
|
|
31
|
-
.toString()
|
|
32
|
-
.trim();
|
|
33
|
-
if (status.length > 0) {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Prüfen, ob main auf dem neuesten Stand mit origin/main ist
|
|
38
|
-
const revList = execSync(
|
|
39
|
-
'git rev-list --left-right --count origin/main...main',
|
|
40
|
-
{ cwd },
|
|
41
|
-
)
|
|
42
|
-
.toString()
|
|
43
|
-
.trim();
|
|
44
|
-
|
|
45
|
-
const [behind, ahead] = revList.split('\t').map(Number);
|
|
46
|
-
return behind === 0 && ahead === 0;
|
|
47
|
-
} catch (err) {
|
|
48
|
-
console.error('Git error:', err.message);
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export const isMainUpToDate = () => {
|
|
10
|
-
try {
|
|
11
|
-
execSync('git fetch origin main');
|
|
12
|
-
const localHash = execSync('git rev-parse main').toString().trim();
|
|
13
|
-
const remoteHash = execSync('git rev-parse origin/main').toString().trim();
|
|
14
|
-
return localHash === remoteHash;
|
|
15
|
-
} catch (error) {
|
|
16
|
-
console.error(red('Error checking branch status:'), error);
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export function pullRequestUrl() {
|
|
10
|
-
const json = execSync('gh pr view --json url', {
|
|
11
|
-
encoding: 'utf-8',
|
|
12
|
-
}).trim();
|
|
13
|
-
|
|
14
|
-
const parsed = JSON.parse(json);
|
|
15
|
-
const url = parsed.url;
|
|
16
|
-
return url;
|
|
17
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
|
-
import { gray } from './colors.js';
|
|
11
|
-
|
|
12
|
-
// Execute a shell command and return trimmed output
|
|
13
|
-
// Execute a shell command and return trimmed output
|
|
14
|
-
export function runCommand(command, silent = true, logCommand = true) {
|
|
15
|
-
if (logCommand) {
|
|
16
|
-
console.log(gray(`${command}`));
|
|
17
|
-
}
|
|
18
|
-
return execSync(command, {
|
|
19
|
-
encoding: 'utf-8',
|
|
20
|
-
stdio: silent ? ['pipe', 'pipe', 'pipe'] : undefined,
|
|
21
|
-
}).trim();
|
|
22
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import * as fs from 'fs';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
|
|
12
|
-
// Create a node.js script that recursively copies all files from one folder
|
|
13
|
-
// to another
|
|
14
|
-
|
|
15
|
-
export async function syncFolders(
|
|
16
|
-
source,
|
|
17
|
-
target,
|
|
18
|
-
options = { excludeHidden: true },
|
|
19
|
-
) {
|
|
20
|
-
if (!fs.existsSync(source)) {
|
|
21
|
-
console.error(`Source folder "${source}" does not exist.`);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (!fs.existsSync(target)) {
|
|
26
|
-
await fs.promises.mkdir(target, { recursive: true });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const items = await fs.promises.readdir(source);
|
|
30
|
-
|
|
31
|
-
for (const item of items) {
|
|
32
|
-
if (options.excludeHidden && item.startsWith('.')) {
|
|
33
|
-
continue; // Skip hidden files and folders
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const sourcePath = path.join(source, item);
|
|
37
|
-
const targetPath = path.join(target, item);
|
|
38
|
-
|
|
39
|
-
const stat = await fs.promises.lstat(sourcePath);
|
|
40
|
-
if (stat.isDirectory()) {
|
|
41
|
-
await syncFolders(sourcePath, targetPath, options.excludeHidden);
|
|
42
|
-
} else {
|
|
43
|
-
await fs.promises.copyFile(sourcePath, targetPath);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
#!/bin/node
|
|
2
|
-
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { blue, gray, green, red, yellow } from './functions/colors.js';
|
|
6
|
-
|
|
7
|
-
// .............................................................................
|
|
8
|
-
const excludedFiles = ['create-new-repo.md', 'rename-class.js'];
|
|
9
|
-
|
|
10
|
-
// .............................................................................
|
|
11
|
-
|
|
12
|
-
const isExcluded = (fileName) => {
|
|
13
|
-
return excludedFiles.includes(fileName);
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const shouldProcessFile = (file) => !isExcluded(file);
|
|
17
|
-
|
|
18
|
-
// Binary files must not be rewritten as UTF-8, that would corrupt them.
|
|
19
|
-
const readTextFile = (fullPath) => {
|
|
20
|
-
const buffer = fs.readFileSync(fullPath);
|
|
21
|
-
if (buffer.includes(0)) return null;
|
|
22
|
-
return buffer.toString('utf8');
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const writeTextFile = (fullPath, oldContent, newContent) => {
|
|
26
|
-
if (newContent === oldContent) return;
|
|
27
|
-
fs.writeFileSync(fullPath, newContent, 'utf8');
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
if (process.argv.length < 4) {
|
|
31
|
-
const usage = red('Usage:');
|
|
32
|
-
const script = yellow(path.basename(process.argv[1]));
|
|
33
|
-
const classA = green('ClassA');
|
|
34
|
-
const classB = blue('ClassB');
|
|
35
|
-
console.error([usage, script, classA, classB].join(' '));
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const classA = process.argv[2];
|
|
40
|
-
const classB = process.argv[3];
|
|
41
|
-
|
|
42
|
-
const toSnakeCase = (str) => {
|
|
43
|
-
return str.replace(/(?<=[a-z0-9])([A-Z])/g, '-$1').toLowerCase();
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const toLowerCamelCase = (str) => {
|
|
47
|
-
return str.replace(/([-_]\w)/g, (matches) => matches[1].toUpperCase());
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const toUpperCamelCase = (str) => {
|
|
51
|
-
return str.replace(/(?:^|[-_])(\w)/g, (_, c) => (c ? c.toUpperCase() : ''));
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const classALower = toLowerCamelCase(classA);
|
|
55
|
-
const classBLower = toLowerCamelCase(classB);
|
|
56
|
-
const classAUpper = toUpperCamelCase(classA);
|
|
57
|
-
const classBUpper = toUpperCamelCase(classB);
|
|
58
|
-
const classASnake = toSnakeCase(classA);
|
|
59
|
-
const classBSnake = toSnakeCase(classB);
|
|
60
|
-
const classASnakeUnderscore = classASnake.replace(/-/g, '_');
|
|
61
|
-
const classBSnakeUnderscore = classBSnake.replace(/-/g, '_');
|
|
62
|
-
|
|
63
|
-
const replaceIncludesFirst = (directory) => {
|
|
64
|
-
const files = fs.readdirSync(directory, { withFileTypes: true });
|
|
65
|
-
|
|
66
|
-
for (const file of files) {
|
|
67
|
-
const fullPath = path.join(directory, file.name);
|
|
68
|
-
|
|
69
|
-
if (file.isDirectory()) {
|
|
70
|
-
if (file.name.startsWith('.') || file.name === 'node_modules') continue;
|
|
71
|
-
replaceIncludesFirst(fullPath);
|
|
72
|
-
} else if (shouldProcessFile(file.name)) {
|
|
73
|
-
const original = readTextFile(fullPath);
|
|
74
|
-
if (original === null) continue;
|
|
75
|
-
const content = original
|
|
76
|
-
.replace(
|
|
77
|
-
new RegExp(`(\\/)${classASnake}(\\.ts)?([\\.\\/\\'\\\"\\\\])`, 'g'),
|
|
78
|
-
`$1${classBSnake}$2$3`,
|
|
79
|
-
)
|
|
80
|
-
.replace(
|
|
81
|
-
new RegExp(
|
|
82
|
-
`(\\/)${classASnakeUnderscore}(\\.ts)?([\\.\\/\\'\\\"\\\\])`,
|
|
83
|
-
'g',
|
|
84
|
-
),
|
|
85
|
-
`$1${classBSnakeUnderscore}$2$3`,
|
|
86
|
-
);
|
|
87
|
-
writeTextFile(fullPath, original, content);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const replaceInFiles = (directory) => {
|
|
93
|
-
const files = fs.readdirSync(directory, { withFileTypes: true });
|
|
94
|
-
|
|
95
|
-
for (const file of files) {
|
|
96
|
-
const fullPath = path.join(directory, file.name);
|
|
97
|
-
|
|
98
|
-
if (file.isDirectory()) {
|
|
99
|
-
if (file.name.startsWith('.') || file.name === 'node_modules') continue;
|
|
100
|
-
replaceInFiles(fullPath);
|
|
101
|
-
} else if (shouldProcessFile(file.name)) {
|
|
102
|
-
const original = readTextFile(fullPath);
|
|
103
|
-
if (original === null) continue;
|
|
104
|
-
const content = original
|
|
105
|
-
.replace(new RegExp(classAUpper, 'g'), classBUpper)
|
|
106
|
-
.replace(new RegExp(classALower, 'g'), classBLower)
|
|
107
|
-
.replace(new RegExp(classASnake, 'g'), classBSnake)
|
|
108
|
-
.replace(
|
|
109
|
-
new RegExp(classASnakeUnderscore, 'g'),
|
|
110
|
-
classBSnakeUnderscore,
|
|
111
|
-
);
|
|
112
|
-
writeTextFile(fullPath, original, content);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const renameFiles = (directory) => {
|
|
118
|
-
const files = fs.readdirSync(directory, { withFileTypes: true });
|
|
119
|
-
|
|
120
|
-
for (const file of files) {
|
|
121
|
-
const fullPath = path.join(directory, file.name);
|
|
122
|
-
|
|
123
|
-
if (file.isDirectory()) {
|
|
124
|
-
if (file.name.startsWith('.') || file.name === 'node_modules') continue;
|
|
125
|
-
renameFiles(fullPath);
|
|
126
|
-
} else if (
|
|
127
|
-
file.name.includes(classASnake) ||
|
|
128
|
-
file.name.includes(classASnakeUnderscore)
|
|
129
|
-
) {
|
|
130
|
-
const newFileName = file.name
|
|
131
|
-
.replace(classASnake, classBSnake)
|
|
132
|
-
.replace(classASnakeUnderscore, classBSnakeUnderscore);
|
|
133
|
-
fs.renameSync(fullPath, path.join(directory, newFileName));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
console.log(gray('Replacing includes first...'));
|
|
139
|
-
replaceIncludesFirst('.');
|
|
140
|
-
|
|
141
|
-
console.log(gray('Renaming files...'));
|
|
142
|
-
renameFiles('.');
|
|
143
|
-
|
|
144
|
-
console.log(gray('Replacing occurrences in files...'));
|
|
145
|
-
replaceInFiles('.');
|
|
146
|
-
|
|
147
|
-
console.log(green('Done.'));
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
|
-
import { writeFileSync } from 'fs';
|
|
11
|
-
import { tmpdir } from 'os';
|
|
12
|
-
import { join } from 'path';
|
|
13
|
-
import {
|
|
14
|
-
blue,
|
|
15
|
-
cyan,
|
|
16
|
-
gray,
|
|
17
|
-
green,
|
|
18
|
-
red,
|
|
19
|
-
white,
|
|
20
|
-
yellow,
|
|
21
|
-
} from './functions/colors.js';
|
|
22
|
-
import { runCommand } from './functions/run-command.js';
|
|
23
|
-
|
|
24
|
-
// The name of the branch ruleset managed by this script
|
|
25
|
-
const rulesetName = 'Default';
|
|
26
|
-
|
|
27
|
-
// The status check that must pass before merging. It is provided by the
|
|
28
|
-
// GitHub Actions app, i.e. not by "Any source".
|
|
29
|
-
const requiredStatusCheck = 'dnaGitQuickCheckPipelineName';
|
|
30
|
-
const gitHubActionsAppId = 15368;
|
|
31
|
-
|
|
32
|
-
// ...........................................................................
|
|
33
|
-
// Make sure the GitHub CLI is available and authenticated
|
|
34
|
-
function checkGhCli() {
|
|
35
|
-
console.log(gray('Check GitHub CLI'));
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
runCommand('gh --version');
|
|
39
|
-
} catch {
|
|
40
|
-
throw new Error(
|
|
41
|
-
[
|
|
42
|
-
yellow('GitHub CLI is not installed. Please install it:'),
|
|
43
|
-
green('brew install gh'),
|
|
44
|
-
].join('\n'),
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
runCommand('gh auth status');
|
|
50
|
-
} catch {
|
|
51
|
-
throw new Error(
|
|
52
|
-
[yellow('Not yet logged in. Please run:'), green('gh auth login')].join(
|
|
53
|
-
'\n',
|
|
54
|
-
),
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ...........................................................................
|
|
60
|
-
// Get "org/repo" either from the first argument or from the current directory
|
|
61
|
-
function repoSlug() {
|
|
62
|
-
const fromArgs = process.argv.slice(2).find((a) => !a.startsWith('--'));
|
|
63
|
-
if (fromArgs) {
|
|
64
|
-
if (!/^[^/]+\/[^/]+$/.test(fromArgs)) {
|
|
65
|
-
throw new Error(`Invalid repository '${fromArgs}'. Expected 'org/repo'.`);
|
|
66
|
-
}
|
|
67
|
-
return fromArgs;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
console.log(gray('Get repository from current directory'));
|
|
71
|
-
try {
|
|
72
|
-
const json = runCommand('gh repo view --json nameWithOwner');
|
|
73
|
-
return JSON.parse(json).nameWithOwner;
|
|
74
|
-
} catch {
|
|
75
|
-
throw new Error(
|
|
76
|
-
[
|
|
77
|
-
yellow('Could not determine the repository.'),
|
|
78
|
-
yellow('Please call this script from a GitHub repository or run:'),
|
|
79
|
-
green('node dna/scripts/setup-github-repo.js org/repo'),
|
|
80
|
-
].join('\n'),
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ...........................................................................
|
|
86
|
-
// Call the GitHub API with a JSON body
|
|
87
|
-
function ghApi(method, path, body) {
|
|
88
|
-
const file = join(tmpdir(), `setup-github-repo-${process.pid}.json`);
|
|
89
|
-
writeFileSync(file, JSON.stringify(body));
|
|
90
|
-
console.log(gray(`gh api --method ${method} ${path}`));
|
|
91
|
-
return execSync(`gh api --method ${method} ${path} --input ${file}`, {
|
|
92
|
-
encoding: 'utf-8',
|
|
93
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
94
|
-
}).trim();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// ...........................................................................
|
|
98
|
-
// Only allow squash merges, auto merge and delete branches after merge
|
|
99
|
-
function pullRequestSettings() {
|
|
100
|
-
return {
|
|
101
|
-
allow_merge_commit: false,
|
|
102
|
-
allow_squash_merge: true,
|
|
103
|
-
// Default commit message: "Pull request title"
|
|
104
|
-
squash_merge_commit_title: 'PR_TITLE',
|
|
105
|
-
squash_merge_commit_message: 'BLANK',
|
|
106
|
-
allow_rebase_merge: false,
|
|
107
|
-
allow_auto_merge: true,
|
|
108
|
-
allow_update_branch: true,
|
|
109
|
-
delete_branch_on_merge: true,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ...........................................................................
|
|
114
|
-
function setupPullRequestSettings(slug) {
|
|
115
|
-
console.log(gray('Setup pull request settings'));
|
|
116
|
-
|
|
117
|
-
ghApi('PATCH', `repos/${slug}`, pullRequestSettings());
|
|
118
|
-
|
|
119
|
-
console.log(green('✅ Pull request settings applied.'));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ...........................................................................
|
|
123
|
-
// The ruleset protecting the default branch
|
|
124
|
-
function ruleset(requireReview) {
|
|
125
|
-
return {
|
|
126
|
-
name: rulesetName,
|
|
127
|
-
target: 'branch',
|
|
128
|
-
enforcement: 'active',
|
|
129
|
-
conditions: {
|
|
130
|
-
ref_name: { include: ['~DEFAULT_BRANCH'], exclude: [] },
|
|
131
|
-
},
|
|
132
|
-
rules: [
|
|
133
|
-
{ type: 'deletion' },
|
|
134
|
-
{ type: 'non_fast_forward' },
|
|
135
|
-
{ type: 'required_linear_history' },
|
|
136
|
-
{
|
|
137
|
-
type: 'pull_request',
|
|
138
|
-
parameters: {
|
|
139
|
-
required_approving_review_count: requireReview ? 1 : 0,
|
|
140
|
-
dismiss_stale_reviews_on_push: false,
|
|
141
|
-
require_code_owner_review: false,
|
|
142
|
-
require_last_push_approval: requireReview,
|
|
143
|
-
required_review_thread_resolution: requireReview,
|
|
144
|
-
allowed_merge_methods: ['squash'],
|
|
145
|
-
},
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
type: 'required_status_checks',
|
|
149
|
-
parameters: {
|
|
150
|
-
strict_required_status_checks_policy: true,
|
|
151
|
-
required_status_checks: [
|
|
152
|
-
{
|
|
153
|
-
context: requiredStatusCheck,
|
|
154
|
-
integration_id: gitHubActionsAppId,
|
|
155
|
-
},
|
|
156
|
-
],
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
],
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// ...........................................................................
|
|
164
|
-
// Find the id of an already existing ruleset with the same name
|
|
165
|
-
function existingRulesetId(slug) {
|
|
166
|
-
const json = runCommand(`gh api repos/${slug}/rulesets`);
|
|
167
|
-
const found = JSON.parse(json).find((r) => r.name === rulesetName);
|
|
168
|
-
return found ? found.id : undefined;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// ...........................................................................
|
|
172
|
-
// Create the ruleset or update it when it already exists
|
|
173
|
-
function setupBranchRules(slug, requireReview) {
|
|
174
|
-
console.log(gray('Setup branch rules'));
|
|
175
|
-
|
|
176
|
-
const id = existingRulesetId(slug);
|
|
177
|
-
|
|
178
|
-
if (id) {
|
|
179
|
-
ghApi('PUT', `repos/${slug}/rulesets/${id}`, ruleset(requireReview));
|
|
180
|
-
console.log(green(`✅ Ruleset '${rulesetName}' has been updated.`));
|
|
181
|
-
} else {
|
|
182
|
-
ghApi('POST', `repos/${slug}/rulesets`, ruleset(requireReview));
|
|
183
|
-
console.log(green(`✅ Ruleset '${rulesetName}' has been created.`));
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// ...........................................................................
|
|
188
|
-
// Print the repo and the settings without changing anything
|
|
189
|
-
function dryRun(slug, requireReview) {
|
|
190
|
-
console.log(cyan(slug));
|
|
191
|
-
console.log(white(JSON.stringify(pullRequestSettings(), null, 2)));
|
|
192
|
-
console.log(white(JSON.stringify(ruleset(requireReview), null, 2)));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// ...........................................................................
|
|
196
|
-
function main() {
|
|
197
|
-
const requireReview = process.argv.includes('--require-review');
|
|
198
|
-
const isDryRun = !process.argv.includes('--apply');
|
|
199
|
-
|
|
200
|
-
checkGhCli();
|
|
201
|
-
const slug = repoSlug();
|
|
202
|
-
|
|
203
|
-
if (isDryRun) {
|
|
204
|
-
dryRun(slug, requireReview);
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
console.log(blue(`https://github.com/${slug}`));
|
|
209
|
-
|
|
210
|
-
setupPullRequestSettings(slug);
|
|
211
|
-
setupBranchRules(slug, requireReview);
|
|
212
|
-
|
|
213
|
-
console.log(green(`✅ Repository '${slug}' has been set up.`));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
main();
|
|
218
|
-
} catch (error) {
|
|
219
|
-
console.error(red(`Error: ${error.message}`));
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright (c) ggsuite
|
|
4
|
-
*
|
|
5
|
-
* Use of this source code is governed by terms that can be
|
|
6
|
-
* found in the LICENSE file in the root of this package.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { blue, green, red, yellow } from './functions/colors.js';
|
|
10
|
-
import { runCommand } from './functions/run-command.js';
|
|
11
|
-
|
|
12
|
-
function getPRUrl() {
|
|
13
|
-
try {
|
|
14
|
-
const json = runCommand('gh pr view --json url').trim();
|
|
15
|
-
|
|
16
|
-
const parsed = JSON.parse(json);
|
|
17
|
-
const url = parsed.url;
|
|
18
|
-
console.log(blue(url));
|
|
19
|
-
} catch (error) {
|
|
20
|
-
console.error(yellow('No PR available'));
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getPRStatus() {
|
|
26
|
-
try {
|
|
27
|
-
const jsonString = runCommand(
|
|
28
|
-
'gh pr view --json state',
|
|
29
|
-
true,
|
|
30
|
-
false,
|
|
31
|
-
).trim();
|
|
32
|
-
|
|
33
|
-
const jsonParsed = JSON.parse(jsonString);
|
|
34
|
-
return jsonParsed.state;
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.error(red('Error fetching PR status'));
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function checkIfPipelineHasFailed() {
|
|
42
|
-
try {
|
|
43
|
-
const json = runCommand(
|
|
44
|
-
'gh run list --repo rljson/template-project --limit 1 --json status,conclusion',
|
|
45
|
-
true,
|
|
46
|
-
false,
|
|
47
|
-
).trim();
|
|
48
|
-
|
|
49
|
-
const jsonParsed = JSON.parse(json);
|
|
50
|
-
if (jsonParsed.length) {
|
|
51
|
-
if (jsonParsed[0].conclusion === 'failure') {
|
|
52
|
-
console.error(red('Pipeline has failed. Please fix.'));
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
} catch (e) {
|
|
57
|
-
console.error(red('Error fetching pipeline status'));
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async function waitForPRClosure() {
|
|
63
|
-
console.log(yellow('Wait for pipelines, code review and merge ...'));
|
|
64
|
-
|
|
65
|
-
while (true) {
|
|
66
|
-
const status = getPRStatus();
|
|
67
|
-
|
|
68
|
-
if (status === 'MERGED') {
|
|
69
|
-
console.log(green('PR has been merged.'));
|
|
70
|
-
break;
|
|
71
|
-
} else if (status === 'CLOSED') {
|
|
72
|
-
console.log(green('PR has been closed.'));
|
|
73
|
-
break;
|
|
74
|
-
} else if (status === 'FAILED') {
|
|
75
|
-
console.log(red('Error: PR has failed.'));
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
await checkIfPipelineHasFailed();
|
|
80
|
-
|
|
81
|
-
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function main() {
|
|
86
|
-
getPRUrl();
|
|
87
|
-
await waitForPRClosure();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
main();
|