@ggdna/dna-gg 0.1.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/LICENSE +21 -0
- package/README.md +76 -0
- package/dna/_dna.json +10 -0
- package/dna/_generated.json +204 -0
- package/dna/_vars.json +9 -0
- package/dna/doc/guides/develop-guide.md +41 -0
- package/dna/doc/guides/develop-without-ai-guide.md +63 -0
- package/dna/doc/guides/for-ai/ai-dev-guide.md +97 -0
- package/dna/doc/guides/install-guide/install-gg-guide.md +75 -0
- package/dna/doc/guides/install-guide/install-gg-workspace-guide.md +28 -0
- package/dna/doc/guides/install-guide.overrides.md +9 -0
- package/dna/dot-claude/skills/cleanup/SKILL.md +49 -0
- package/dna/dot-claude/skills/commit/SKILL.md +37 -0
- package/dna/dot-claude/skills/publish/SKILL.md +39 -0
- package/dna/dot-claude/skills/push/SKILL.md +21 -0
- package/dna/dot-claude/skills/ticket/SKILL.md +47 -0
- package/dna/dot-github/workflows/quick_check.yaml +20 -0
- package/dna/scripts/delete-feature-branch.js +97 -0
- package/dna/scripts/functions/colors.js +27 -0
- package/dna/scripts/functions/current-branch.js +11 -0
- package/dna/scripts/functions/directories.js +22 -0
- package/dna/scripts/functions/get-repo-urls.js +43 -0
- package/dna/scripts/functions/get-version.js +14 -0
- package/dna/scripts/functions/is-clean-repo.js +51 -0
- package/dna/scripts/functions/is-main-up-to-date.js +19 -0
- package/dna/scripts/functions/pull-request-url.js +17 -0
- package/dna/scripts/functions/run-command.js +22 -0
- package/dna/scripts/functions/sync-folders.js +46 -0
- package/dna/scripts/rename-class.js +147 -0
- package/dna/scripts/setup-github-repo.js +221 -0
- package/dna/scripts/wait-for-pr.js +90 -0
- package/package.json +45 -0
|
@@ -0,0 +1,147 @@
|
|
|
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.'));
|
|
@@ -0,0 +1,221 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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();
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ggdna/dna-gg",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DNA layer: gg CLI workflow guides, repo setup scripts and CI check",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"dna",
|
|
7
|
+
"helix",
|
|
8
|
+
"conventions",
|
|
9
|
+
"scaffolding"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/ggdna/dna_gg",
|
|
12
|
+
"bugs": "https://github.com/ggdna/dna_gg/issues",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/ggdna/dna_gg.git"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"files": [
|
|
20
|
+
"dna",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@ggdna/dna-index": "^0.1.0",
|
|
32
|
+
"@ggdna/dna-install": "^0.1.0"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"test:dart": "dart test",
|
|
36
|
+
"test": "pnpm run test:dart",
|
|
37
|
+
"prepublishOnly": "pnpm run test",
|
|
38
|
+
"format": "prettier --write .",
|
|
39
|
+
"format:check": "prettier --check ."
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^26.2.0",
|
|
43
|
+
"prettier": "^3.9.6"
|
|
44
|
+
}
|
|
45
|
+
}
|