faf-cli 4.3.0 ā 4.3.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +40 -22
- package/dist/cli.js.map +1 -1
- package/dist/commands/readme.d.ts +11 -6
- package/dist/commands/readme.d.ts.map +1 -1
- package/dist/commands/readme.js +167 -120
- package/dist/commands/readme.js.map +1 -1
- package/dist/commands/show.d.ts.map +1 -1
- package/dist/commands/show.js +22 -7
- package/dist/commands/show.js.map +1 -1
- package/dist/commands/sixws.d.ts +6 -0
- package/dist/commands/sixws.d.ts.map +1 -0
- package/dist/commands/sixws.js +154 -0
- package/dist/commands/sixws.js.map +1 -0
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +1 -4
- package/dist/utils/file-utils.js.map +1 -1
- package/package.json +6 -2
- package/project.faf +4 -4
- package/scripts/ANTHROPIC-DEMO.sh +203 -0
- package/scripts/boris-ready.sh +169 -0
- package/scripts/bundle-yaml.js +87 -0
- package/scripts/check-version.js +88 -0
- package/scripts/clean-build.js +34 -0
- package/scripts/cleanup-unused.sh +54 -0
- package/scripts/debug-django.txt +9 -0
- package/scripts/debug-mongo.txt +9 -0
- package/scripts/debug-react.txt +9 -0
- package/scripts/debug-rust.txt +9 -0
- package/scripts/debug-whisper.cpp.txt +9 -0
- package/scripts/evaluate-family-member.ts +300 -0
- package/scripts/generate-docs.ts +358 -0
- package/scripts/generate-drift-reports.sh +111 -0
- package/scripts/industry-showcase.json +122 -0
- package/scripts/mcp-ecosystem-research.sh +58 -0
- package/scripts/migrate-yaml-imports.sh +55 -0
- package/scripts/migrate-yaml.ts +132 -0
- package/scripts/performance-validation.ts +460 -0
- package/scripts/postinstall.js +30 -0
- package/scripts/prepare-release.ts +421 -0
- package/scripts/run-industry-showcase.ts +237 -0
- package/scripts/run-test-showcase.ts +244 -0
- package/scripts/setup-github-watch.sh +43 -0
- package/scripts/sync-version.js +35 -0
- package/scripts/test-integration-detection.ts +93 -0
- package/scripts/test-integration-simple.js +93 -0
- package/scripts/test-medal-progression.sh +143 -0
- package/scripts/test-showcase-results.json +109 -0
- package/scripts/test-showcase.json +32 -0
- package/scripts/update-version.js +148 -0
- package/scripts/verify-build.js +343 -0
- package/scripts/version-check.js +78 -0
- package/scripts/watch-discussions.sh +86 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
#!/usr/bin/env ts-node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* š Release Preparation Script - F1-Inspired Championship Releases
|
|
5
|
+
* Automates version bumping, changelog generation, and release validation
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import { FAF_COLORS, FAF_ICONS } from '../src/utils/championship-style';
|
|
12
|
+
|
|
13
|
+
interface ReleaseOptions {
|
|
14
|
+
type: 'patch' | 'minor' | 'major' | 'prerelease';
|
|
15
|
+
dryRun: boolean;
|
|
16
|
+
skipTests: boolean;
|
|
17
|
+
prereleaseId?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface VersionInfo {
|
|
21
|
+
current: string;
|
|
22
|
+
next: string;
|
|
23
|
+
tag: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface ChangelogEntry {
|
|
27
|
+
type: 'feat' | 'fix' | 'docs' | 'style' | 'refactor' | 'test' | 'chore';
|
|
28
|
+
scope?: string;
|
|
29
|
+
description: string;
|
|
30
|
+
breaking?: boolean;
|
|
31
|
+
hash: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Main release preparation function
|
|
36
|
+
*/
|
|
37
|
+
async function prepareRelease(options: ReleaseOptions): Promise<void> {
|
|
38
|
+
console.log(`${FAF_COLORS.fafCyan('š FAF CLI Release Preparation')}`);
|
|
39
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} F1-inspired championship engineering`);
|
|
40
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Release type: ${FAF_COLORS.fafGreen(options.type)}`);
|
|
41
|
+
console.log();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// Pre-flight checks
|
|
45
|
+
await preflightChecks(options);
|
|
46
|
+
|
|
47
|
+
// Calculate version info
|
|
48
|
+
const versionInfo = await calculateVersions(options);
|
|
49
|
+
console.log(`${FAF_COLORS.fafCyan('š Version Info:')}`);
|
|
50
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Current: ${versionInfo.current}`);
|
|
51
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Next: ${FAF_COLORS.fafGreen(versionInfo.next)}`);
|
|
52
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Tag: ${versionInfo.tag}`);
|
|
53
|
+
console.log();
|
|
54
|
+
|
|
55
|
+
// Generate changelog
|
|
56
|
+
const changelog = await generateChangelog(versionInfo);
|
|
57
|
+
|
|
58
|
+
// Update package.json
|
|
59
|
+
if (!options.dryRun) {
|
|
60
|
+
await updatePackageVersion(versionInfo.next);
|
|
61
|
+
console.log(`${FAF_COLORS.fafGreen('ā
')} Updated package.json to ${versionInfo.next}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Run tests if not skipped
|
|
65
|
+
if (!options.skipTests) {
|
|
66
|
+
await runTestSuite();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Build project
|
|
70
|
+
await buildProject();
|
|
71
|
+
|
|
72
|
+
// Show release summary
|
|
73
|
+
showReleaseSummary(versionInfo, changelog, options);
|
|
74
|
+
|
|
75
|
+
// Create release commands
|
|
76
|
+
generateReleaseCommands(versionInfo, options);
|
|
77
|
+
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(`${FAF_COLORS.fafOrange('ā Release preparation failed:')} ${error}`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Pre-flight checks
|
|
86
|
+
*/
|
|
87
|
+
async function preflightChecks(options: ReleaseOptions): Promise<void> {
|
|
88
|
+
console.log(`${FAF_COLORS.fafCyan('š Pre-flight Checks:')}`);
|
|
89
|
+
|
|
90
|
+
// Check git status
|
|
91
|
+
try {
|
|
92
|
+
const gitStatus = execSync('git status --porcelain', { encoding: 'utf8' });
|
|
93
|
+
if (gitStatus.trim() && !options.dryRun) {
|
|
94
|
+
throw new Error('Working directory not clean. Commit or stash changes first.');
|
|
95
|
+
}
|
|
96
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} Git working directory clean`);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.log(`${FAF_COLORS.fafOrange('āā ā ļø')} Git status check failed`);
|
|
99
|
+
if (!options.dryRun) throw error;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check we're on main branch
|
|
103
|
+
try {
|
|
104
|
+
const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
105
|
+
if (currentBranch !== 'main' && !options.dryRun) {
|
|
106
|
+
console.log(`${FAF_COLORS.fafOrange('āā ā ļø')} Currently on branch: ${currentBranch} (expected: main)`);
|
|
107
|
+
} else {
|
|
108
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} On main branch`);
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.log(`${FAF_COLORS.fafOrange('āā ā ļø')} Branch check failed`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Check package.json exists
|
|
115
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
116
|
+
try {
|
|
117
|
+
await fs.access(packagePath);
|
|
118
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} package.json found`);
|
|
119
|
+
} catch {
|
|
120
|
+
throw new Error('package.json not found in current directory');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check npm credentials (for actual releases)
|
|
124
|
+
if (!options.dryRun) {
|
|
125
|
+
try {
|
|
126
|
+
execSync('npm whoami', { stdio: 'pipe' });
|
|
127
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} NPM authentication ready`);
|
|
128
|
+
} catch {
|
|
129
|
+
console.log(`${FAF_COLORS.fafOrange('āā ā ļø')} NPM authentication not configured`);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} Dry run mode - skipping NPM check`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Calculate version information
|
|
140
|
+
*/
|
|
141
|
+
async function calculateVersions(options: ReleaseOptions): Promise<VersionInfo> {
|
|
142
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
143
|
+
const packageContent = await fs.readFile(packagePath, 'utf8');
|
|
144
|
+
const packageJson = JSON.parse(packageContent);
|
|
145
|
+
const currentVersion = packageJson.version;
|
|
146
|
+
|
|
147
|
+
// Calculate next version using npm version --dry-run
|
|
148
|
+
let versionArgs = options.type;
|
|
149
|
+
if (options.type === 'prerelease' && options.prereleaseId) {
|
|
150
|
+
versionArgs += ` --preid=${options.prereleaseId}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const nextVersion = execSync(`npm version ${versionArgs} --dry-run --no-git-tag-version`, {
|
|
154
|
+
encoding: 'utf8'
|
|
155
|
+
}).trim().replace(/^v/, '');
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
current: currentVersion,
|
|
159
|
+
next: nextVersion,
|
|
160
|
+
tag: `v${nextVersion}`
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Generate changelog from git commits
|
|
166
|
+
*/
|
|
167
|
+
async function generateChangelog(versionInfo: VersionInfo): Promise<ChangelogEntry[]> {
|
|
168
|
+
console.log(`${FAF_COLORS.fafCyan('š Generating Changelog:')}`);
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// Get commits since last tag
|
|
172
|
+
const lastTag = execSync('git describe --tags --abbrev=0 2>/dev/null || echo ""', {
|
|
173
|
+
encoding: 'utf8'
|
|
174
|
+
}).trim();
|
|
175
|
+
|
|
176
|
+
const gitRange = lastTag ? `${lastTag}..HEAD` : 'HEAD';
|
|
177
|
+
const commits = execSync(`git log --pretty=format:"%H|%s" ${gitRange}`, {
|
|
178
|
+
encoding: 'utf8'
|
|
179
|
+
}).trim().split('\n').filter(line => line);
|
|
180
|
+
|
|
181
|
+
const changelog: ChangelogEntry[] = commits.map(commit => {
|
|
182
|
+
const [hash, message] = commit.split('|');
|
|
183
|
+
return parseCommitMessage(message, hash);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Group by type
|
|
187
|
+
const grouped = changelog.reduce((acc, entry) => {
|
|
188
|
+
if (!acc[entry.type]) acc[entry.type] = [];
|
|
189
|
+
acc[entry.type].push(entry);
|
|
190
|
+
return acc;
|
|
191
|
+
}, {} as Record<string, ChangelogEntry[]>);
|
|
192
|
+
|
|
193
|
+
// Display changelog preview
|
|
194
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Changes since ${lastTag || 'initial commit'}:`);
|
|
195
|
+
|
|
196
|
+
const typeLabels = {
|
|
197
|
+
feat: 'š Features',
|
|
198
|
+
fix: 'š Bug Fixes',
|
|
199
|
+
docs: 'š Documentation',
|
|
200
|
+
style: 'šØ Style',
|
|
201
|
+
refactor: 'ā»ļø Refactoring',
|
|
202
|
+
test: 'š§Ŗ Tests',
|
|
203
|
+
chore: 'š§ Maintenance'
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
Object.entries(grouped).forEach(([type, entries]) => {
|
|
207
|
+
if (entries.length > 0) {
|
|
208
|
+
console.log(`${FAF_COLORS.fafCyan('ā ')}${typeLabels[type as keyof typeof typeLabels] || type}: ${entries.length} changes`);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
console.log(`${FAF_COLORS.fafGreen('āā')} Total commits: ${changelog.length}`);
|
|
213
|
+
console.log();
|
|
214
|
+
|
|
215
|
+
return changelog;
|
|
216
|
+
|
|
217
|
+
} catch (error) {
|
|
218
|
+
console.log(`${FAF_COLORS.fafOrange('āā ā ļø')} Could not generate changelog: ${error}`);
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Parse conventional commit message
|
|
225
|
+
*/
|
|
226
|
+
function parseCommitMessage(message: string, hash: string): ChangelogEntry {
|
|
227
|
+
// Parse conventional commit format: type(scope): description
|
|
228
|
+
const conventionalMatch = message.match(/^(feat|fix|docs|style|refactor|test|chore)(?:\\(([^)]+)\\))?: (.+)/);
|
|
229
|
+
|
|
230
|
+
if (conventionalMatch) {
|
|
231
|
+
const [, type, scope, description] = conventionalMatch;
|
|
232
|
+
return {
|
|
233
|
+
type: type as ChangelogEntry['type'],
|
|
234
|
+
scope,
|
|
235
|
+
description,
|
|
236
|
+
breaking: message.includes('BREAKING CHANGE'),
|
|
237
|
+
hash: hash.substring(0, 7)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Fallback for non-conventional commits
|
|
242
|
+
return {
|
|
243
|
+
type: 'chore',
|
|
244
|
+
description: message,
|
|
245
|
+
hash: hash.substring(0, 7)
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Update package.json version
|
|
251
|
+
*/
|
|
252
|
+
async function updatePackageVersion(newVersion: string): Promise<void> {
|
|
253
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
254
|
+
const packageContent = await fs.readFile(packagePath, 'utf8');
|
|
255
|
+
const packageJson = JSON.parse(packageContent);
|
|
256
|
+
|
|
257
|
+
packageJson.version = newVersion;
|
|
258
|
+
|
|
259
|
+
await fs.writeFile(packagePath, JSON.stringify(packageJson, null, 2) + '\n');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Run test suite
|
|
264
|
+
*/
|
|
265
|
+
async function runTestSuite(): Promise<void> {
|
|
266
|
+
console.log(`${FAF_COLORS.fafCyan('š§Ŗ Running Test Suite:')}`);
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
// Run main tests
|
|
270
|
+
execSync('npm test', { stdio: 'inherit' });
|
|
271
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} Unit tests passed`);
|
|
272
|
+
|
|
273
|
+
// Run audit tests
|
|
274
|
+
execSync('npm run test:audit', { stdio: 'inherit' });
|
|
275
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} Audit tests passed`);
|
|
276
|
+
|
|
277
|
+
// Run linting
|
|
278
|
+
execSync('npm run lint', { stdio: 'inherit' });
|
|
279
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} Linting passed`);
|
|
280
|
+
|
|
281
|
+
} catch (error) {
|
|
282
|
+
throw new Error(`Test suite failed: ${error}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
console.log();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Build project
|
|
290
|
+
*/
|
|
291
|
+
async function buildProject(): Promise<void> {
|
|
292
|
+
console.log(`${FAF_COLORS.fafCyan('šļø Building Project:')}`);
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
execSync('npm run build', { stdio: 'inherit' });
|
|
296
|
+
console.log(`${FAF_COLORS.fafGreen('āā ā
')} Build completed successfully`);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
throw new Error(`Build failed: ${error}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Show release summary
|
|
306
|
+
*/
|
|
307
|
+
function showReleaseSummary(
|
|
308
|
+
versionInfo: VersionInfo,
|
|
309
|
+
changelog: ChangelogEntry[],
|
|
310
|
+
options: ReleaseOptions
|
|
311
|
+
): void {
|
|
312
|
+
console.log(`${FAF_COLORS.fafCyan('š Release Summary:')}`);
|
|
313
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Version: ${versionInfo.current} ā ${FAF_COLORS.fafGreen(versionInfo.next)}`);
|
|
314
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Type: ${options.type}`);
|
|
315
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Changes: ${changelog.length} commits`);
|
|
316
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Mode: ${options.dryRun ? 'DRY RUN' : 'LIVE RELEASE'}`);
|
|
317
|
+
console.log(`${FAF_COLORS.fafCyan('āā')} Ready for championship deployment! šļø`);
|
|
318
|
+
console.log();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Generate release commands
|
|
323
|
+
*/
|
|
324
|
+
function generateReleaseCommands(versionInfo: VersionInfo, options: ReleaseOptions): void {
|
|
325
|
+
console.log(`${FAF_COLORS.fafCyan('š Release Commands:')}`);
|
|
326
|
+
console.log();
|
|
327
|
+
|
|
328
|
+
if (options.dryRun) {
|
|
329
|
+
console.log(`${FAF_COLORS.fafOrange('š To complete the release, run:')}`);
|
|
330
|
+
console.log();
|
|
331
|
+
console.log(`${FAF_COLORS.fafCyan('# 1. Commit version change')}`);
|
|
332
|
+
console.log(`git add package.json`);
|
|
333
|
+
console.log(`git commit -m "chore: bump version to ${versionInfo.next}"`);
|
|
334
|
+
console.log();
|
|
335
|
+
console.log(`${FAF_COLORS.fafCyan('# 2. Create and push tag')}`);
|
|
336
|
+
console.log(`git tag ${versionInfo.tag}`);
|
|
337
|
+
console.log(`git push origin main --tags`);
|
|
338
|
+
console.log();
|
|
339
|
+
console.log(`${FAF_COLORS.fafCyan('# 3. GitHub Actions will handle the rest! š')}`);
|
|
340
|
+
} else {
|
|
341
|
+
console.log(`${FAF_COLORS.fafGreen('ā
Release prepared! Push to trigger automation:')}`);
|
|
342
|
+
console.log();
|
|
343
|
+
console.log(`git push origin main --tags`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
console.log();
|
|
347
|
+
console.log(`${FAF_COLORS.fafGreen('š Championship release engineering complete!')}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* CLI argument parsing
|
|
352
|
+
*/
|
|
353
|
+
function parseArgs(): ReleaseOptions {
|
|
354
|
+
const args = process.argv.slice(2);
|
|
355
|
+
|
|
356
|
+
const options: ReleaseOptions = {
|
|
357
|
+
type: 'patch',
|
|
358
|
+
dryRun: false,
|
|
359
|
+
skipTests: false
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
for (let i = 0; i < args.length; i++) {
|
|
363
|
+
const arg = args[i];
|
|
364
|
+
|
|
365
|
+
switch (arg) {
|
|
366
|
+
case '--type':
|
|
367
|
+
options.type = args[++i] as ReleaseOptions['type'];
|
|
368
|
+
break;
|
|
369
|
+
case '--dry-run':
|
|
370
|
+
options.dryRun = true;
|
|
371
|
+
break;
|
|
372
|
+
case '--skip-tests':
|
|
373
|
+
options.skipTests = true;
|
|
374
|
+
break;
|
|
375
|
+
case '--preid':
|
|
376
|
+
options.prereleaseId = args[++i];
|
|
377
|
+
break;
|
|
378
|
+
case '--help':
|
|
379
|
+
showHelp();
|
|
380
|
+
process.exit(0);
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
return options;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Show help message
|
|
390
|
+
*/
|
|
391
|
+
function showHelp(): void {
|
|
392
|
+
console.log(`
|
|
393
|
+
š FAF CLI Release Preparation Script
|
|
394
|
+
|
|
395
|
+
Usage: npm run release [options]
|
|
396
|
+
|
|
397
|
+
Options:
|
|
398
|
+
--type <type> Release type: patch|minor|major|prerelease (default: patch)
|
|
399
|
+
--dry-run Show what would be done without making changes
|
|
400
|
+
--skip-tests Skip running test suite
|
|
401
|
+
--preid <id> Pre-release identifier (alpha, beta, rc)
|
|
402
|
+
--help Show this help
|
|
403
|
+
|
|
404
|
+
Examples:
|
|
405
|
+
npm run release # Patch release (1.0.0 ā 1.0.1)
|
|
406
|
+
npm run release -- --type minor # Minor release (1.0.0 ā 1.1.0)
|
|
407
|
+
npm run release -- --type major # Major release (1.0.0 ā 2.0.0)
|
|
408
|
+
npm run release -- --dry-run # Preview changes only
|
|
409
|
+
npm run release -- --type prerelease --preid beta # Beta release
|
|
410
|
+
|
|
411
|
+
šļø F1-inspired engineering - Championship releases every time!
|
|
412
|
+
`);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Run if called directly
|
|
416
|
+
if (require.main === module) {
|
|
417
|
+
const options = parseArgs();
|
|
418
|
+
prepareRelease(options).catch(console.error);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export { prepareRelease };
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Industry Showcase Runner
|
|
4
|
+
*
|
|
5
|
+
* Runs faf git on all famous repos and collects results
|
|
6
|
+
* Output: industry-showcase-results.json
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import * as yaml from 'yaml';
|
|
13
|
+
|
|
14
|
+
interface ShowcaseData {
|
|
15
|
+
title: string;
|
|
16
|
+
description: string;
|
|
17
|
+
updated: string;
|
|
18
|
+
categories: Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
repos: string[];
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface RepoResult {
|
|
25
|
+
repo: string;
|
|
26
|
+
owner: string;
|
|
27
|
+
name: string;
|
|
28
|
+
category: string;
|
|
29
|
+
currentScore: number;
|
|
30
|
+
newScore: number;
|
|
31
|
+
improvement: number;
|
|
32
|
+
tier: string;
|
|
33
|
+
stack: {
|
|
34
|
+
frontend?: string;
|
|
35
|
+
backend?: string;
|
|
36
|
+
runtime?: string;
|
|
37
|
+
build?: string;
|
|
38
|
+
database?: string;
|
|
39
|
+
hosting?: string;
|
|
40
|
+
};
|
|
41
|
+
projectType: string;
|
|
42
|
+
mainLanguage: string;
|
|
43
|
+
description: string;
|
|
44
|
+
status: 'success' | 'failed' | 'rate-limited';
|
|
45
|
+
error?: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface ShowcaseResults {
|
|
49
|
+
generated: string;
|
|
50
|
+
totalRepos: number;
|
|
51
|
+
successCount: number;
|
|
52
|
+
failedCount: number;
|
|
53
|
+
avgImprovement: number;
|
|
54
|
+
categories: Array<{
|
|
55
|
+
name: string;
|
|
56
|
+
repos: RepoResult[];
|
|
57
|
+
}>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function parseFafGitOutput(output: string): { currentScore: number; newScore: number; improvement: number; tier: string } | null {
|
|
61
|
+
// v4.3.0 format:
|
|
62
|
+
// Current: No .faf file ā ļø
|
|
63
|
+
// With FAF: 100% š Trophy
|
|
64
|
+
|
|
65
|
+
const currentMatch = output.match(/Current:\s+No \.faf file/);
|
|
66
|
+
const withFafMatch = output.match(/With FAF:\s+(\d+)%\s+([šš„š„š„š¢š”š“š¤ā ļø]+)\s*(\w+)?/);
|
|
67
|
+
|
|
68
|
+
if (!withFafMatch) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const currentScore = currentMatch ? 0 : 0; // Always 0 for "No .faf file"
|
|
73
|
+
const newScore = parseInt(withFafMatch[1]);
|
|
74
|
+
const tier = withFafMatch[3] || 'Unknown';
|
|
75
|
+
const improvement = newScore - currentScore;
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
currentScore,
|
|
79
|
+
newScore,
|
|
80
|
+
improvement,
|
|
81
|
+
tier
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function extractStackFromFaf(fafPath: string): any {
|
|
86
|
+
try {
|
|
87
|
+
const content = readFileSync(fafPath, 'utf-8');
|
|
88
|
+
const parsed = yaml.parse(content);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
stack: {
|
|
92
|
+
frontend: parsed.stack?.frontend !== 'slotignored' ? parsed.stack?.frontend : undefined,
|
|
93
|
+
backend: parsed.stack?.backend !== 'slotignored' ? parsed.stack?.backend : undefined,
|
|
94
|
+
runtime: parsed.stack?.runtime !== 'slotignored' ? parsed.stack?.runtime : undefined,
|
|
95
|
+
build: parsed.stack?.build !== 'slotignored' ? parsed.stack?.build : undefined,
|
|
96
|
+
database: parsed.stack?.database !== 'slotignored' ? parsed.stack?.database : undefined,
|
|
97
|
+
hosting: parsed.stack?.hosting !== 'slotignored' ? parsed.stack?.hosting : undefined,
|
|
98
|
+
},
|
|
99
|
+
projectType: parsed.project?.type || 'unknown',
|
|
100
|
+
mainLanguage: parsed.project?.main_language || 'unknown',
|
|
101
|
+
description: parsed.project?.goal || parsed.metadata?.description || ''
|
|
102
|
+
};
|
|
103
|
+
} catch (e) {
|
|
104
|
+
return {
|
|
105
|
+
stack: {},
|
|
106
|
+
projectType: 'unknown',
|
|
107
|
+
mainLanguage: 'unknown',
|
|
108
|
+
description: ''
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function runShowcase() {
|
|
114
|
+
console.log('šļø FAF Industry Showcase Runner\n');
|
|
115
|
+
|
|
116
|
+
// Read showcase data
|
|
117
|
+
const showcaseData: ShowcaseData = JSON.parse(
|
|
118
|
+
readFileSync(join(__dirname, 'industry-showcase.json'), 'utf-8')
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const results: ShowcaseResults = {
|
|
122
|
+
generated: new Date().toISOString(),
|
|
123
|
+
totalRepos: 0,
|
|
124
|
+
successCount: 0,
|
|
125
|
+
failedCount: 0,
|
|
126
|
+
avgImprovement: 0,
|
|
127
|
+
categories: []
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
let totalImprovement = 0;
|
|
131
|
+
|
|
132
|
+
for (const category of showcaseData.categories) {
|
|
133
|
+
console.log(`\nš Category: ${category.name}`);
|
|
134
|
+
console.log(` Testing ${category.repos.length} repos...\n`);
|
|
135
|
+
|
|
136
|
+
const categoryResults: RepoResult[] = [];
|
|
137
|
+
|
|
138
|
+
for (const repo of category.repos) {
|
|
139
|
+
results.totalRepos++;
|
|
140
|
+
const [owner, name] = repo.split('/');
|
|
141
|
+
|
|
142
|
+
process.stdout.write(` ā” ${repo}... `);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Run faf git
|
|
146
|
+
const output = execSync(
|
|
147
|
+
`node ${join(__dirname, '../dist/cli.js')} git ${repo}`,
|
|
148
|
+
{
|
|
149
|
+
encoding: 'utf-8',
|
|
150
|
+
timeout: 30000,
|
|
151
|
+
stdio: 'pipe'
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Parse output
|
|
156
|
+
const scores = parseFafGitOutput(output);
|
|
157
|
+
|
|
158
|
+
if (!scores) {
|
|
159
|
+
throw new Error('Failed to parse output');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Extract stack from generated .faf file
|
|
163
|
+
const fafPath = join(process.cwd(), `${name}.faf`);
|
|
164
|
+
const fafData = extractStackFromFaf(fafPath);
|
|
165
|
+
|
|
166
|
+
const result: RepoResult = {
|
|
167
|
+
repo,
|
|
168
|
+
owner,
|
|
169
|
+
name,
|
|
170
|
+
category: category.name,
|
|
171
|
+
currentScore: scores.currentScore,
|
|
172
|
+
newScore: scores.newScore,
|
|
173
|
+
improvement: scores.improvement,
|
|
174
|
+
tier: scores.tier,
|
|
175
|
+
stack: fafData.stack,
|
|
176
|
+
projectType: fafData.projectType,
|
|
177
|
+
mainLanguage: fafData.mainLanguage,
|
|
178
|
+
description: fafData.description,
|
|
179
|
+
status: 'success'
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
categoryResults.push(result);
|
|
183
|
+
results.successCount++;
|
|
184
|
+
totalImprovement += scores.improvement;
|
|
185
|
+
|
|
186
|
+
console.log(`ā
${scores.currentScore}% ā ${scores.newScore}% (+${scores.improvement})`);
|
|
187
|
+
|
|
188
|
+
} catch (error: any) {
|
|
189
|
+
results.failedCount++;
|
|
190
|
+
|
|
191
|
+
const result: RepoResult = {
|
|
192
|
+
repo,
|
|
193
|
+
owner,
|
|
194
|
+
name,
|
|
195
|
+
category: category.name,
|
|
196
|
+
currentScore: 0,
|
|
197
|
+
newScore: 0,
|
|
198
|
+
improvement: 0,
|
|
199
|
+
tier: 'Failed',
|
|
200
|
+
stack: {},
|
|
201
|
+
projectType: 'unknown',
|
|
202
|
+
mainLanguage: 'unknown',
|
|
203
|
+
description: '',
|
|
204
|
+
status: error.message?.includes('rate limit') ? 'rate-limited' : 'failed',
|
|
205
|
+
error: error.message
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
categoryResults.push(result);
|
|
209
|
+
console.log(`ā ${error.message.split('\n')[0]}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Rate limit protection
|
|
213
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
results.categories.push({
|
|
217
|
+
name: category.name,
|
|
218
|
+
repos: categoryResults
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Calculate average improvement
|
|
223
|
+
results.avgImprovement = Math.round(totalImprovement / results.successCount);
|
|
224
|
+
|
|
225
|
+
// Save results
|
|
226
|
+
const outputPath = join(__dirname, 'industry-showcase-results.json');
|
|
227
|
+
writeFileSync(outputPath, JSON.stringify(results, null, 2));
|
|
228
|
+
|
|
229
|
+
console.log(`\n\nš Showcase Complete!\n`);
|
|
230
|
+
console.log(` Total Repos: ${results.totalRepos}`);
|
|
231
|
+
console.log(` ā
Success: ${results.successCount}`);
|
|
232
|
+
console.log(` ā Failed: ${results.failedCount}`);
|
|
233
|
+
console.log(` š Avg Improvement: +${results.avgImprovement} points\n`);
|
|
234
|
+
console.log(`š Results saved to: ${outputPath}\n`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
runShowcase().catch(console.error);
|