@regardio/dev 1.24.0 → 2.0.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 +2 -2
- package/dist/bin/ship/hotfix.bin.mjs +140 -0
- package/dist/bin/ship/production.bin.mjs +120 -0
- package/dist/bin/ship/staging.bin.mjs +70 -0
- package/dist/bin/ship/utils-BQ-JZ2D5.mjs +45 -0
- package/dist/playwright/index.d.mts +24 -0
- package/dist/playwright/index.mjs +61 -0
- package/dist/vitest/node.d.mts +22 -0
- package/dist/vitest/node.mjs +28 -0
- package/dist/vitest/react.d.mts +22 -0
- package/dist/vitest/react.mjs +28 -0
- package/docs/en/README.md +95 -0
- package/docs/en/agents.md +57 -0
- package/docs/en/standards/api.md +324 -0
- package/docs/en/standards/coding.md +144 -0
- package/docs/en/standards/commits.md +111 -0
- package/docs/en/standards/documentation.md +173 -0
- package/docs/en/standards/naming.md +180 -0
- package/docs/en/standards/principles.md +84 -0
- package/docs/en/standards/react.md +246 -0
- package/docs/en/standards/sql.md +258 -0
- package/docs/en/standards/testing.md +139 -0
- package/docs/en/standards/writing.md +119 -0
- package/docs/en/tools/biome.md +89 -0
- package/docs/en/tools/commitlint.md +92 -0
- package/docs/en/tools/dependencies.md +116 -0
- package/docs/en/tools/husky.md +90 -0
- package/docs/en/tools/markdownlint.md +84 -0
- package/docs/en/tools/playwright.md +117 -0
- package/docs/en/tools/releases.md +242 -0
- package/docs/en/tools/typescript.md +89 -0
- package/docs/en/tools/vitest.md +146 -0
- package/package.json +57 -70
- package/src/biome/preset.json +3 -0
- package/templates/changeset/README.md +14 -0
- package/templates/changeset/config.json +11 -0
- package/templates/github/release.yml +77 -0
- package/dist/bin/exec/clean.d.ts +0 -3
- package/dist/bin/exec/clean.d.ts.map +0 -1
- package/dist/bin/exec/clean.js +0 -25
- package/dist/bin/exec/clean.test.d.ts +0 -2
- package/dist/bin/exec/clean.test.d.ts.map +0 -1
- package/dist/bin/exec/clean.test.js +0 -45
- package/dist/bin/exec/husky.d.ts +0 -3
- package/dist/bin/exec/husky.d.ts.map +0 -1
- package/dist/bin/exec/husky.js +0 -9
- package/dist/bin/exec/p.d.ts +0 -3
- package/dist/bin/exec/p.d.ts.map +0 -1
- package/dist/bin/exec/p.js +0 -8
- package/dist/bin/exec/s.d.ts +0 -3
- package/dist/bin/exec/s.d.ts.map +0 -1
- package/dist/bin/exec/s.js +0 -8
- package/dist/bin/exec/tsc.d.ts +0 -3
- package/dist/bin/exec/tsc.d.ts.map +0 -1
- package/dist/bin/exec/tsc.js +0 -8
- package/dist/bin/lint/biome.d.ts +0 -3
- package/dist/bin/lint/biome.d.ts.map +0 -1
- package/dist/bin/lint/biome.js +0 -8
- package/dist/bin/lint/commit.d.ts +0 -3
- package/dist/bin/lint/commit.d.ts.map +0 -1
- package/dist/bin/lint/commit.js +0 -8
- package/dist/bin/lint/md.d.ts +0 -3
- package/dist/bin/lint/md.d.ts.map +0 -1
- package/dist/bin/lint/md.js +0 -16
- package/dist/bin/lint/package.d.ts +0 -4
- package/dist/bin/lint/package.d.ts.map +0 -1
- package/dist/bin/lint/package.js +0 -81
- package/dist/bin/lint/package.test.d.ts +0 -2
- package/dist/bin/lint/package.test.d.ts.map +0 -1
- package/dist/bin/lint/package.test.js +0 -65
- package/dist/bin/ship/hotfix.d.ts +0 -3
- package/dist/bin/ship/hotfix.d.ts.map +0 -1
- package/dist/bin/ship/hotfix.js +0 -141
- package/dist/bin/ship/production.d.ts +0 -3
- package/dist/bin/ship/production.d.ts.map +0 -1
- package/dist/bin/ship/production.js +0 -124
- package/dist/bin/ship/staging.d.ts +0 -3
- package/dist/bin/ship/staging.d.ts.map +0 -1
- package/dist/bin/ship/staging.js +0 -51
- package/dist/bin/ship/utils.d.ts +0 -9
- package/dist/bin/ship/utils.d.ts.map +0 -1
- package/dist/bin/ship/utils.js +0 -63
- package/dist/bin/ship/utils.test.d.ts +0 -2
- package/dist/bin/ship/utils.test.d.ts.map +0 -1
- package/dist/bin/ship/utils.test.js +0 -127
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -101
- package/dist/playwright/index.d.ts +0 -10
- package/dist/playwright/index.d.ts.map +0 -1
- package/dist/playwright/index.js +0 -42
- package/dist/playwright/index.test.d.ts +0 -2
- package/dist/playwright/index.test.d.ts.map +0 -1
- package/dist/playwright/index.test.js +0 -55
- package/dist/testing/setup-react.d.ts +0 -2
- package/dist/testing/setup-react.d.ts.map +0 -1
- package/dist/testing/setup-react.js +0 -1
- package/dist/vitest/node.d.ts +0 -22
- package/dist/vitest/node.d.ts.map +0 -1
- package/dist/vitest/node.js +0 -16
- package/dist/vitest/react.d.ts +0 -17
- package/dist/vitest/react.d.ts.map +0 -1
- package/dist/vitest/react.js +0 -12
- package/src/bin/exec/clean.test.ts +0 -63
- package/src/bin/exec/clean.ts +0 -36
- package/src/bin/exec/husky.ts +0 -14
- package/src/bin/exec/p.ts +0 -13
- package/src/bin/exec/s.ts +0 -13
- package/src/bin/exec/tsc.ts +0 -13
- package/src/bin/lint/biome.ts +0 -13
- package/src/bin/lint/commit.ts +0 -13
- package/src/bin/lint/md.ts +0 -28
- package/src/bin/lint/package.test.ts +0 -83
- package/src/bin/lint/package.ts +0 -108
- package/src/bin/ship/hotfix.ts +0 -241
- package/src/bin/ship/production.ts +0 -240
- package/src/bin/ship/staging.ts +0 -108
- package/src/bin/ship/utils.test.ts +0 -178
- package/src/bin/ship/utils.ts +0 -109
- package/src/config.test.ts +0 -129
- package/src/markdownlint/markdownlint-cli2.jsonc +0 -9
- package/src/playwright/index.test.ts +0 -73
- package/src/playwright/index.ts +0 -63
- package/src/templates/release.yml +0 -128
- package/src/testing/setup-react.ts +0 -8
- package/src/vitest/node.ts +0 -25
- package/src/vitest/react.ts +0 -19
- /package/{src → templates}/sqlfluff/setup.cfg +0 -0
package/src/bin/exec/s.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* exec-s: Run npm scripts sequentially via npm-run-all's run-s.
|
|
4
|
-
* Usage: exec-s <script-patterns>
|
|
5
|
-
*/
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
7
|
-
import { createRequire } from 'node:module';
|
|
8
|
-
|
|
9
|
-
const require = createRequire(import.meta.url);
|
|
10
|
-
const bin = require.resolve('npm-run-all/bin/run-s/index.js');
|
|
11
|
-
const args = process.argv.slice(2);
|
|
12
|
-
const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
|
|
13
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
package/src/bin/exec/tsc.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* exec-tsc: Run the TypeScript compiler (tsc) via local dependency.
|
|
4
|
-
* Usage: exec-tsc [tsc args...]
|
|
5
|
-
*/
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
7
|
-
import { createRequire } from 'node:module';
|
|
8
|
-
|
|
9
|
-
const require = createRequire(import.meta.url);
|
|
10
|
-
const bin = require.resolve('typescript/bin/tsc');
|
|
11
|
-
const args = process.argv.slice(2);
|
|
12
|
-
const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
|
|
13
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
package/src/bin/lint/biome.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* lint-biome: Run Biome (formatter/linter) against the codebase.
|
|
4
|
-
* Usage: lint-biome [biome args...]
|
|
5
|
-
*/
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
7
|
-
import { createRequire } from 'node:module';
|
|
8
|
-
|
|
9
|
-
const require = createRequire(import.meta.url);
|
|
10
|
-
const bin = require.resolve('@biomejs/biome/bin/biome');
|
|
11
|
-
const args = process.argv.slice(2);
|
|
12
|
-
const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
|
|
13
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
package/src/bin/lint/commit.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* lint-commit: Run commitlint against commit messages.
|
|
4
|
-
* Usage: lint-commit [commitlint args...]
|
|
5
|
-
*/
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
7
|
-
import { createRequire } from 'node:module';
|
|
8
|
-
|
|
9
|
-
const require = createRequire(import.meta.url);
|
|
10
|
-
const bin = require.resolve('@commitlint/cli/cli.js');
|
|
11
|
-
const args = process.argv.slice(2);
|
|
12
|
-
const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
|
|
13
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
package/src/bin/lint/md.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* lint-md: Run markdownlint-cli2 to lint and fix Markdown files.
|
|
4
|
-
* Usage: lint-md [--fix] [globs...]
|
|
5
|
-
*
|
|
6
|
-
* Default globs (if none provided): "**\/*.md" "**\/*.mdx"
|
|
7
|
-
* Default exclusions (always added): "!**\/dist/**" "!**\/node_modules/**"
|
|
8
|
-
*/
|
|
9
|
-
import { spawn } from 'node:child_process';
|
|
10
|
-
import { createRequire } from 'node:module';
|
|
11
|
-
import { dirname, join } from 'node:path';
|
|
12
|
-
|
|
13
|
-
const require = createRequire(import.meta.url);
|
|
14
|
-
const packageDir = dirname(require.resolve('markdownlint-cli2'));
|
|
15
|
-
const bin = join(packageDir, 'markdownlint-cli2-bin.mjs');
|
|
16
|
-
|
|
17
|
-
const DEFAULT_GLOBS = ['**/*.md', '**/*.mdx'];
|
|
18
|
-
const DEFAULT_EXCLUSIONS = ['!**/dist/**', '!**/node_modules/**'];
|
|
19
|
-
|
|
20
|
-
const rawArgs = process.argv.slice(2);
|
|
21
|
-
const fixFlag = rawArgs.includes('--fix');
|
|
22
|
-
const userGlobs = rawArgs.filter((arg) => arg !== '--fix');
|
|
23
|
-
|
|
24
|
-
const globs = userGlobs.length > 0 ? userGlobs : DEFAULT_GLOBS;
|
|
25
|
-
const args = [...(fixFlag ? ['--fix'] : []), ...globs, ...DEFAULT_EXCLUSIONS];
|
|
26
|
-
|
|
27
|
-
const child = spawn(process.execPath, [bin, ...args], { stdio: 'inherit' });
|
|
28
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { reorderConditions } from './package.js';
|
|
4
|
-
|
|
5
|
-
describe('lint-package', () => {
|
|
6
|
-
describe('reorderConditions', () => {
|
|
7
|
-
it('reorders types before default when default comes first', () => {
|
|
8
|
-
const input = {
|
|
9
|
-
'./foo': {
|
|
10
|
-
default: './dist/foo.js',
|
|
11
|
-
types: './dist/foo.d.ts',
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const result = reorderConditions(input);
|
|
16
|
-
|
|
17
|
-
expect(Object.keys(result['./foo'] as Record<string, unknown>)).toEqual(['types', 'default']);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('does not modify when types already comes before default', () => {
|
|
21
|
-
const input = {
|
|
22
|
-
'./foo': {
|
|
23
|
-
default: './dist/foo.js',
|
|
24
|
-
types: './dist/foo.d.ts',
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const result = reorderConditions(input);
|
|
29
|
-
|
|
30
|
-
expect(Object.keys(result['./foo'] as Record<string, unknown>)).toEqual(['types', 'default']);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('handles multiple exports with mixed order', () => {
|
|
34
|
-
const input = {
|
|
35
|
-
'./a': { default: './dist/a.js', types: './dist/a.d.ts' },
|
|
36
|
-
'./b': { default: './dist/b.js', types: './dist/b.d.ts' },
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const result = reorderConditions(input);
|
|
40
|
-
|
|
41
|
-
expect(Object.keys(result['./a'] as Record<string, unknown>)[0]).toBe('types');
|
|
42
|
-
expect(Object.keys(result['./b'] as Record<string, unknown>)[0]).toBe('types');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('preserves other keys after types', () => {
|
|
46
|
-
const input = {
|
|
47
|
-
'./foo': {
|
|
48
|
-
default: './dist/foo.js',
|
|
49
|
-
import: './dist/foo.mjs',
|
|
50
|
-
require: './dist/foo.cjs',
|
|
51
|
-
types: './dist/foo.d.ts',
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
const result = reorderConditions(input);
|
|
56
|
-
const keys = Object.keys(result['./foo'] as Record<string, unknown>);
|
|
57
|
-
|
|
58
|
-
expect(keys[0]).toBe('types');
|
|
59
|
-
expect(keys.slice(1)).toEqual(['default', 'import', 'require']);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('handles exports without types or default', () => {
|
|
63
|
-
const input = { './styles.css': './dist/styles.css' };
|
|
64
|
-
|
|
65
|
-
expect(reorderConditions(input)).toEqual(input);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('handles deeply nested condition objects', () => {
|
|
69
|
-
const input = {
|
|
70
|
-
'./foo': {
|
|
71
|
-
browser: { default: './dist/foo.browser.js', types: './dist/foo.browser.d.ts' },
|
|
72
|
-
node: { default: './dist/foo.node.js', types: './dist/foo.node.d.ts' },
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const result = reorderConditions(input);
|
|
77
|
-
const foo = result['./foo'] as Record<string, Record<string, unknown>>;
|
|
78
|
-
|
|
79
|
-
expect(Object.keys(foo.node as object)[0]).toBe('types');
|
|
80
|
-
expect(Object.keys(foo.browser as object)[0]).toBe('types');
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
});
|
package/src/bin/lint/package.ts
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Sorts package.json files using sort-package-json and fixes
|
|
4
|
-
* exports condition order (types must come before default for TypeScript).
|
|
5
|
-
*/
|
|
6
|
-
import { execSync } from 'node:child_process';
|
|
7
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
|
-
import { dirname, join, resolve } from 'node:path';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Recursively reorder exports condition objects so that `types` always
|
|
13
|
-
* appears before `default`. Returns a new object; does not mutate input.
|
|
14
|
-
*/
|
|
15
|
-
export function reorderConditions(obj: Record<string, unknown>): Record<string, unknown> {
|
|
16
|
-
if (typeof obj !== 'object' || obj === null) return obj;
|
|
17
|
-
|
|
18
|
-
const processed: Record<string, unknown> = {};
|
|
19
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
20
|
-
processed[key] =
|
|
21
|
-
typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
22
|
-
? reorderConditions(value as Record<string, unknown>)
|
|
23
|
-
: value;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if ('types' in processed && 'default' in processed) {
|
|
27
|
-
const keys = Object.keys(processed);
|
|
28
|
-
if (keys.indexOf('default') < keys.indexOf('types')) {
|
|
29
|
-
const reordered: Record<string, unknown> = { types: processed.types };
|
|
30
|
-
for (const key of keys) {
|
|
31
|
-
if (key !== 'types') reordered[key] = processed[key];
|
|
32
|
-
}
|
|
33
|
-
return reordered;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return processed;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Fix exports condition order in a package.json file.
|
|
42
|
-
* Returns true if the file needs changes (or was changed when fix=true).
|
|
43
|
-
*/
|
|
44
|
-
export function fixExportsOrder(filePath: string, fix: boolean): boolean {
|
|
45
|
-
const fullPath = resolve(process.cwd(), filePath);
|
|
46
|
-
if (!existsSync(fullPath)) return false;
|
|
47
|
-
|
|
48
|
-
const content = readFileSync(fullPath, 'utf-8');
|
|
49
|
-
const pkg = JSON.parse(content) as Record<string, unknown>;
|
|
50
|
-
|
|
51
|
-
if (!pkg.exports || typeof pkg.exports !== 'object') return false;
|
|
52
|
-
|
|
53
|
-
const fixed = reorderConditions(pkg.exports as Record<string, unknown>);
|
|
54
|
-
const changed = JSON.stringify(fixed) !== JSON.stringify(pkg.exports);
|
|
55
|
-
|
|
56
|
-
if (changed && fix) {
|
|
57
|
-
writeFileSync(fullPath, `${JSON.stringify({ ...pkg, exports: fixed }, null, 2)}\n`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return changed;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
// CLI entry point — only runs when executed directly
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
if (fileURLToPath(import.meta.url) === resolve(process.argv[1] ?? '')) {
|
|
67
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
68
|
-
const devRoot = resolve(__dirname, '../../..');
|
|
69
|
-
|
|
70
|
-
const sortPkgBin = join(devRoot, 'node_modules/.bin/sort-package-json');
|
|
71
|
-
const sortPkgBinAlt = join(devRoot, 'node_modules/sort-package-json/cli.js');
|
|
72
|
-
|
|
73
|
-
let bin = '';
|
|
74
|
-
if (existsSync(sortPkgBin)) {
|
|
75
|
-
bin = sortPkgBin;
|
|
76
|
-
} else if (existsSync(sortPkgBinAlt)) {
|
|
77
|
-
bin = `node ${sortPkgBinAlt}`;
|
|
78
|
-
} else {
|
|
79
|
-
bin = 'npx sort-package-json';
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const args = process.argv.slice(2);
|
|
83
|
-
const fixMode = args.includes('--fix');
|
|
84
|
-
const files = args.filter((arg) => arg !== '--fix');
|
|
85
|
-
const targets = files.length > 0 ? files : ['package.json'];
|
|
86
|
-
|
|
87
|
-
try {
|
|
88
|
-
const checkFlag = fixMode ? '' : '--check';
|
|
89
|
-
execSync(`${bin} ${checkFlag} ${targets.join(' ')}`.trim(), { stdio: 'inherit' });
|
|
90
|
-
} catch {
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
let hasExportsIssues = false;
|
|
95
|
-
for (const file of targets) {
|
|
96
|
-
const needsFix = fixExportsOrder(file, fixMode);
|
|
97
|
-
if (needsFix && !fixMode) {
|
|
98
|
-
console.error(
|
|
99
|
-
`${file}: exports condition order is incorrect (types must come before default)`,
|
|
100
|
-
);
|
|
101
|
-
hasExportsIssues = true;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (hasExportsIssues) {
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
}
|
package/src/bin/ship/hotfix.ts
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* ship-hotfix: Manage hotfix branches based on production code.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* ship-hotfix start <name> - Create hotfix/<name> from production
|
|
7
|
-
* ship-hotfix finish <patch|minor> "description" - Finish and propagate the hotfix
|
|
8
|
-
*
|
|
9
|
-
* GitLab workflow:
|
|
10
|
-
* production → hotfix/<name> → production → staging → main
|
|
11
|
-
*
|
|
12
|
-
* start:
|
|
13
|
-
* 1. Fetches origin, checks out production, creates hotfix/<name>
|
|
14
|
-
*
|
|
15
|
-
* finish:
|
|
16
|
-
* 1. Ensures you are on a hotfix/* branch with a clean working tree
|
|
17
|
-
* 2. Runs quality checks
|
|
18
|
-
* 3. Bumps version (patch or minor) and updates CHANGELOG.md
|
|
19
|
-
* 4. Commits, then merges into production
|
|
20
|
-
* 5. Merges production into staging
|
|
21
|
-
* 6. Merges staging into main
|
|
22
|
-
* 7. Pushes all three branches, deletes the hotfix branch
|
|
23
|
-
* 8. Checks out main
|
|
24
|
-
*/
|
|
25
|
-
import { execSync } from 'node:child_process';
|
|
26
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
27
|
-
import { join } from 'node:path';
|
|
28
|
-
|
|
29
|
-
import {
|
|
30
|
-
branchExists,
|
|
31
|
-
bumpVersion,
|
|
32
|
-
git,
|
|
33
|
-
gitRead,
|
|
34
|
-
insertChangelog,
|
|
35
|
-
runQualityChecks,
|
|
36
|
-
runScript,
|
|
37
|
-
} from './utils.js';
|
|
38
|
-
|
|
39
|
-
const subcommand = process.argv[2];
|
|
40
|
-
const subArgs = process.argv.slice(3);
|
|
41
|
-
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
// flow-hotfix start <name>
|
|
44
|
-
// ---------------------------------------------------------------------------
|
|
45
|
-
if (subcommand === 'start') {
|
|
46
|
-
const name = subArgs[0];
|
|
47
|
-
if (!name) {
|
|
48
|
-
console.error('Usage: ship-hotfix start <name>');
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const hotfixBranch = `hotfix/${name}`;
|
|
53
|
-
|
|
54
|
-
const status = gitRead('status', '--porcelain');
|
|
55
|
-
if (status) {
|
|
56
|
-
console.error('Working directory has uncommitted changes. Commit or stash them first.');
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
console.log('\nFetching latest state from origin...');
|
|
61
|
-
git('fetch', 'origin');
|
|
62
|
-
|
|
63
|
-
if (!branchExists('production')) {
|
|
64
|
-
console.error(
|
|
65
|
-
'Branch "production" does not exist. Create it first:\n'
|
|
66
|
-
+ ' git checkout -b production && git push -u origin production',
|
|
67
|
-
);
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
git('checkout', 'production');
|
|
72
|
-
git('pull', '--ff-only', 'origin', 'production');
|
|
73
|
-
git('checkout', '-b', hotfixBranch);
|
|
74
|
-
|
|
75
|
-
console.log(`\n✅ Hotfix branch "${hotfixBranch}" created from production.`);
|
|
76
|
-
console.log('Apply your fix, then run: ship-hotfix finish <patch|minor> "description"');
|
|
77
|
-
process.exit(0);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
// flow-hotfix finish <patch|minor> "description"
|
|
82
|
-
// ---------------------------------------------------------------------------
|
|
83
|
-
if (subcommand === 'finish') {
|
|
84
|
-
const bumpType = subArgs[0];
|
|
85
|
-
const message = subArgs.slice(1).join(' ');
|
|
86
|
-
|
|
87
|
-
if (!bumpType || !['patch', 'minor'].includes(bumpType)) {
|
|
88
|
-
console.error('Usage: ship-hotfix finish <patch|minor> "description"');
|
|
89
|
-
console.error('Hotfixes use patch or minor bumps only.');
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!message) {
|
|
94
|
-
console.error('A description is required.');
|
|
95
|
-
console.error('Example: ship-hotfix finish patch "Fix critical auth bug"');
|
|
96
|
-
process.exit(1);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Guard: must be on a hotfix/* branch
|
|
100
|
-
const currentBranch = gitRead('branch', '--show-current');
|
|
101
|
-
if (!currentBranch.startsWith('hotfix/')) {
|
|
102
|
-
console.error(`Must be on a hotfix/* branch. Currently on: ${currentBranch}`);
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Guard: working tree must be clean
|
|
107
|
-
const status = gitRead('status', '--porcelain');
|
|
108
|
-
if (status) {
|
|
109
|
-
console.error('Working directory has uncommitted changes. Commit or stash them first.');
|
|
110
|
-
process.exit(1);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Quality checks
|
|
114
|
-
console.log('\nRunning quality checks...');
|
|
115
|
-
try {
|
|
116
|
-
runQualityChecks();
|
|
117
|
-
} catch {
|
|
118
|
-
console.error('\nQuality checks failed. Fix all issues before finishing the hotfix.');
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
console.log('✅ Quality checks passed');
|
|
122
|
-
|
|
123
|
-
// Read package.json
|
|
124
|
-
const packageJsonPath = join(process.cwd(), 'package.json');
|
|
125
|
-
if (!existsSync(packageJsonPath)) {
|
|
126
|
-
console.error('No package.json found in current directory.');
|
|
127
|
-
process.exit(1);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {
|
|
131
|
-
name: string;
|
|
132
|
-
version: string;
|
|
133
|
-
};
|
|
134
|
-
const packageName = packageJson.name;
|
|
135
|
-
const oldVersion = packageJson.version;
|
|
136
|
-
const newVersion = bumpVersion(oldVersion, bumpType);
|
|
137
|
-
|
|
138
|
-
console.log(`\nBumping ${packageName}: ${oldVersion} → ${newVersion}`);
|
|
139
|
-
|
|
140
|
-
writeFileSync(
|
|
141
|
-
packageJsonPath,
|
|
142
|
-
`${JSON.stringify({ ...packageJson, version: newVersion }, null, 2)}\n`,
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
// Update CHANGELOG.md
|
|
146
|
-
const changelogPath = join(process.cwd(), 'CHANGELOG.md');
|
|
147
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
148
|
-
insertChangelog(changelogPath, `## [${newVersion}] - ${today} (hotfix)\n\n${message}\n`);
|
|
149
|
-
|
|
150
|
-
// Fix formatting of modified files (package.json, CHANGELOG.md)
|
|
151
|
-
try {
|
|
152
|
-
runScript('fix:pkg');
|
|
153
|
-
} catch {
|
|
154
|
-
// fix:pkg may not exist
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Format modified files
|
|
158
|
-
try {
|
|
159
|
-
git('add', '-A');
|
|
160
|
-
const changedFiles = gitRead('diff', '--cached', '--name-only').split('\n').filter(Boolean);
|
|
161
|
-
|
|
162
|
-
// Format JSON files with biome
|
|
163
|
-
for (const file of changedFiles) {
|
|
164
|
-
if (file.endsWith('.json')) {
|
|
165
|
-
try {
|
|
166
|
-
execSync(`npx biome check --write ${file}`, { cwd: process.cwd(), stdio: 'inherit' });
|
|
167
|
-
} catch {
|
|
168
|
-
// File might not need formatting or biome not available
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Format markdown files with markdownlint
|
|
174
|
-
for (const file of changedFiles) {
|
|
175
|
-
if (file.endsWith('.md')) {
|
|
176
|
-
try {
|
|
177
|
-
execSync(`npx markdownlint-cli2 --fix ${file}`, { cwd: process.cwd(), stdio: 'inherit' });
|
|
178
|
-
} catch {
|
|
179
|
-
// File might not need formatting or markdownlint not available
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
} catch {
|
|
184
|
-
// Formatters not available
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Commit
|
|
188
|
-
git('add', '-A');
|
|
189
|
-
git('commit', '-m', `chore(hotfix): ${packageName}@${newVersion}`, '-m', message);
|
|
190
|
-
|
|
191
|
-
// Fetch before merging
|
|
192
|
-
console.log('\nFetching latest state from origin...');
|
|
193
|
-
git('fetch', 'origin');
|
|
194
|
-
|
|
195
|
-
// Merge hotfix → production
|
|
196
|
-
console.log('\nMerging hotfix into production...');
|
|
197
|
-
git('checkout', 'production');
|
|
198
|
-
git('pull', '--ff-only', 'origin', 'production');
|
|
199
|
-
git(
|
|
200
|
-
'merge',
|
|
201
|
-
'--no-ff',
|
|
202
|
-
currentBranch,
|
|
203
|
-
'-m',
|
|
204
|
-
`chore(hotfix): merge ${currentBranch} into production`,
|
|
205
|
-
);
|
|
206
|
-
git('push', 'origin', 'production');
|
|
207
|
-
|
|
208
|
-
// Merge production → staging
|
|
209
|
-
console.log('\nPropagating hotfix to staging...');
|
|
210
|
-
git('checkout', 'staging');
|
|
211
|
-
git('pull', '--ff-only', 'origin', 'staging');
|
|
212
|
-
git('merge', '--no-ff', 'production', '-m', 'chore(hotfix): merge production into staging');
|
|
213
|
-
git('push', 'origin', 'staging');
|
|
214
|
-
|
|
215
|
-
// Merge staging → main
|
|
216
|
-
console.log('\nPropagating hotfix to main...');
|
|
217
|
-
git('checkout', 'main');
|
|
218
|
-
git('pull', '--ff-only', 'origin', 'main');
|
|
219
|
-
git('merge', '--no-ff', 'staging', '-m', 'chore(hotfix): merge staging into main');
|
|
220
|
-
git('push', 'origin', 'main');
|
|
221
|
-
|
|
222
|
-
// Delete hotfix branch
|
|
223
|
-
git('branch', '-d', currentBranch);
|
|
224
|
-
try {
|
|
225
|
-
git('push', 'origin', '--delete', currentBranch);
|
|
226
|
-
} catch {
|
|
227
|
-
// Remote branch may not exist if it was never pushed
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
console.log(`\n✅ Hotfix ${packageName}@${newVersion} shipped to production → staging → main`);
|
|
231
|
-
console.log('You are on main and ready to keep working.');
|
|
232
|
-
process.exit(0);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// ---------------------------------------------------------------------------
|
|
236
|
-
// Unknown subcommand
|
|
237
|
-
// ---------------------------------------------------------------------------
|
|
238
|
-
console.error('Usage:');
|
|
239
|
-
console.error(' ship-hotfix start <name>');
|
|
240
|
-
console.error(' ship-hotfix finish <patch|minor> "description"');
|
|
241
|
-
process.exit(1);
|