@ankhorage/devtools 1.3.4 → 1.4.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/README.md +28 -7
- package/dist/cli/runRepositoryCommand.d.ts +2 -0
- package/dist/cli/runRepositoryCommand.js +12 -1
- package/dist/policy/bunRuntimePolicy.d.ts +5 -0
- package/dist/policy/bunRuntimePolicy.js +5 -0
- package/dist/tools/package/index.js +25 -6
- package/dist/tools/package/syncBunDependencies.d.ts +3 -0
- package/dist/tools/package/syncBunDependencies.js +30 -0
- package/dist/tools/workflows/files/ci.yml +1 -1
- package/dist/tools/workflows/files/release.yml +1 -1
- package/dist/tools/workflows/index.d.ts +2 -7
- package/dist/tools/workflows/index.js +15 -8
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ Shared development tools and repository standards for Ankhorage TypeScript proje
|
|
|
9
9
|
```text
|
|
10
10
|
src/
|
|
11
11
|
├── cli/
|
|
12
|
+
├── policy/
|
|
12
13
|
└── tools/
|
|
13
14
|
├── eslint/
|
|
14
15
|
├── prettier/
|
|
@@ -18,14 +19,15 @@ src/
|
|
|
18
19
|
└── vscode/
|
|
19
20
|
```
|
|
20
21
|
|
|
22
|
+
- `policy`: shared repository runtime policy, including the canonical Bun version
|
|
21
23
|
- `eslint`: shared flat ESLint configuration, automatic project profiles, and the bundled ESLint runner
|
|
22
24
|
- `prettier`: shared Prettier configuration and the bundled Prettier runner
|
|
23
25
|
- `knip`: shared Knip configuration helpers and the bundled Knip runner
|
|
24
|
-
- `package`: merge-aware synchronization of the shared `package.json` tooling contract
|
|
26
|
+
- `package`: merge-aware synchronization of the shared `package.json` tooling and Bun runtime contract
|
|
25
27
|
- `workflows`: canonical `.github/workflows/ci.yml` and `release.yml`
|
|
26
28
|
- `vscode`: canonical `.vscode/settings.json` and `extensions.json`
|
|
27
29
|
|
|
28
|
-
The package owns the supported ESLint, TypeScript ESLint, Prettier, Knip, security, React, React Hooks, React Native, import/sort, unused-import, and formatting-plugin versions used by consuming repositories.
|
|
30
|
+
The package owns the supported ESLint, TypeScript ESLint, Prettier, Knip, security, React, React Hooks, React Native, import/sort, unused-import, and formatting-plugin versions used by consuming repositories. It also owns the Bun runtime version used by Ankhorage repository metadata and managed workflows.
|
|
29
31
|
|
|
30
32
|
## Bootstrap
|
|
31
33
|
|
|
@@ -34,7 +36,6 @@ For a repository that does not yet depend on the shared toolchain:
|
|
|
34
36
|
```bash
|
|
35
37
|
bun add -D @ankhorage/devtools
|
|
36
38
|
bunx @ankhorage/ankh devtools sync .
|
|
37
|
-
bun install
|
|
38
39
|
```
|
|
39
40
|
|
|
40
41
|
After the first install, the normal workflow is:
|
|
@@ -45,7 +46,9 @@ ankh devtools sync
|
|
|
45
46
|
|
|
46
47
|
The target path is optional and defaults to the current working directory.
|
|
47
48
|
|
|
48
|
-
Synchronization ensures `@ankhorage/devtools` is declared using the version of the provider performing the sync, installs the standard package scripts, and removes direct devDependencies for tools/plugins owned by devtools. Unrelated package metadata, dependencies, and scripts are preserved.
|
|
49
|
+
Synchronization ensures `@ankhorage/devtools` is declared using the version of the provider performing the sync, installs the standard package scripts, applies the managed Bun runtime policy, and removes direct devDependencies for tools/plugins owned by devtools. When package metadata changes, sync runs `bun install` so installed dependencies and `bun.lock` match the synchronized manifest. Unrelated package metadata, dependencies, and scripts are preserved.
|
|
50
|
+
|
|
51
|
+
`devtools sync` does not upgrade the globally installed Bun executable. The managed version applies to repository metadata, Bun types, and GitHub workflows.
|
|
49
52
|
|
|
50
53
|
## Ankh provider
|
|
51
54
|
|
|
@@ -150,6 +153,8 @@ Synchronization is deterministic and idempotent:
|
|
|
150
153
|
|
|
151
154
|
- missing managed artifacts are created
|
|
152
155
|
- outdated centrally owned artifacts are updated
|
|
156
|
+
- the managed Bun runtime version is applied consistently to package metadata and workflows
|
|
157
|
+
- package changes are followed by `bun install`, keeping installed dependencies and `bun.lock` synchronized
|
|
153
158
|
- current artifacts are left untouched
|
|
154
159
|
- unrelated files and package fields are preserved
|
|
155
160
|
- repeated sync produces only `unchanged` results
|
|
@@ -247,20 +252,36 @@ export default createKnipConfig();
|
|
|
247
252
|
|
|
248
253
|
`knip.config.ts` is create-only after bootstrap so repositories can retain narrow local entries, projects, ignores, binaries, dependencies, or switch to `createKnipMonorepoConfig()` without synchronization overwriting those extensions.
|
|
249
254
|
|
|
255
|
+
## Managed Bun runtime policy
|
|
256
|
+
|
|
257
|
+
The canonical Bun policy is defined once in devtools and consumed by both package and workflow synchronization. The current policy is:
|
|
258
|
+
|
|
259
|
+
```text
|
|
260
|
+
Bun runtime 1.3.14
|
|
261
|
+
packageManager bun@1.3.14
|
|
262
|
+
@types/bun ^1.3.14
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Changing the policy in devtools therefore updates the repository-facing Bun version consistently instead of maintaining independent version literals in multiple templates.
|
|
266
|
+
|
|
250
267
|
## Managed package contract
|
|
251
268
|
|
|
252
269
|
`ankh devtools package sync` merge-updates `package.json` rather than replacing it.
|
|
253
270
|
|
|
254
271
|
It owns:
|
|
255
272
|
|
|
256
|
-
- the `@ankhorage/devtools`
|
|
273
|
+
- the `@ankhorage/devtools` dependency version range
|
|
274
|
+
- `packageManager` according to the managed Bun runtime policy
|
|
275
|
+
- the `@types/bun` development dependency according to the managed Bun runtime policy
|
|
257
276
|
- `lint`
|
|
258
277
|
- `lint:fix`
|
|
259
278
|
- `format`
|
|
260
279
|
- `format:check`
|
|
261
280
|
- `knip`
|
|
262
281
|
|
|
263
|
-
|
|
282
|
+
For normal consumers, `@ankhorage/devtools` is a devDependency. `@ankhorage/ankh` keeps devtools as a runtime dependency because it loads the provider. Devtools itself participates in the Bun runtime policy without attempting to install itself as a consumer dependency.
|
|
283
|
+
|
|
284
|
+
When this managed package contract changes, synchronization runs `bun install`. This updates installed dependencies and `bun.lock` before sync completes. It also removes direct devDependencies for tools and ESLint plugins already provided by `@ankhorage/devtools`. Unrelated scripts, dependencies, metadata, and repository-specific configuration remain unchanged.
|
|
264
285
|
|
|
265
286
|
## Managed GitHub Actions workflows
|
|
266
287
|
|
|
@@ -271,7 +292,7 @@ It also removes direct devDependencies for tools and ESLint plugins already prov
|
|
|
271
292
|
.github/workflows/release.yml
|
|
272
293
|
```
|
|
273
294
|
|
|
274
|
-
The CI workflow installs
|
|
295
|
+
Both workflows render their `bun-version` from the same managed Bun runtime policy used for `package.json`. The CI workflow installs that Bun version with the frozen lockfile, builds before repository-provider validation, runs `bunx @ankhorage/ankh doctor validate .`, and conditionally runs lint, formatting, Knip, tests, typecheck, and Changesets checks.
|
|
275
296
|
|
|
276
297
|
## Managed VS Code configuration
|
|
277
298
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { type ManagedFileSyncResult } from '../tools/shared/managedFiles.js';
|
|
1
2
|
import type { DevtoolsRepositoryCommandDefinition } from './commands.js';
|
|
2
3
|
export interface DevtoolsRepositoryCommandContext {
|
|
3
4
|
readonly cwd: string;
|
|
5
|
+
readonly syncDependencies?: (targetDirectory: string) => Promise<ManagedFileSyncResult>;
|
|
4
6
|
writeStdout(text: string): void;
|
|
5
7
|
writeStderr(text: string): void;
|
|
6
8
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { eslintManagedFiles } from '../tools/eslint/managed.js';
|
|
2
2
|
import { knipManagedFiles } from '../tools/knip/managed.js';
|
|
3
3
|
import { inspectPackageManifest, readCurrentDevtoolsVersion, syncPackageManifest, } from '../tools/package/index.js';
|
|
4
|
+
import { planBunDependencySync, syncBunDependencies, } from '../tools/package/syncBunDependencies.js';
|
|
4
5
|
import { prettierManagedFiles } from '../tools/prettier/managed.js';
|
|
5
6
|
import { inspectManagedFiles, resolveManagedTargetDirectory, syncManagedFiles, } from '../tools/shared/managedFiles.js';
|
|
6
7
|
import { vscodeManagedFiles } from '../tools/vscode/index.js';
|
|
@@ -52,7 +53,17 @@ async function runStatus(scope, targetDirectory, devtoolsVersion, context) {
|
|
|
52
53
|
async function runSync(scope, targetDirectory, devtoolsVersion, dryRun, context) {
|
|
53
54
|
const results = [];
|
|
54
55
|
if (scope === 'all' || scope === 'package') {
|
|
55
|
-
|
|
56
|
+
const packageResult = await syncPackageManifest(targetDirectory, devtoolsVersion, { dryRun });
|
|
57
|
+
results.push(packageResult);
|
|
58
|
+
if (packageResult.action !== 'unchanged') {
|
|
59
|
+
if (dryRun) {
|
|
60
|
+
results.push(planBunDependencySync(targetDirectory));
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const syncDependencies = context.syncDependencies ?? syncBunDependencies;
|
|
64
|
+
results.push(await syncDependencies(targetDirectory));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
56
67
|
}
|
|
57
68
|
results.push(...(await syncManagedFiles(targetDirectory, getManagedFiles(scope), { dryRun })));
|
|
58
69
|
writeSyncOutput(results, context);
|
|
@@ -8,21 +8,23 @@
|
|
|
8
8
|
* `format`, `format:check`, and `knip` scripts are written.
|
|
9
9
|
* Unrelated manifest fields, scripts, dependencies, and metadata are preserved.
|
|
10
10
|
*
|
|
11
|
+
* The Bun runtime policy is shared by every repository, including devtools itself. Devtools skips
|
|
12
|
+
* only its consumer dependency/script normalization so it never attempts to install itself.
|
|
13
|
+
*
|
|
11
14
|
* Status compares only the fields owned by this contract, so unrelated repository customization
|
|
12
15
|
* does not count as drift. `--dry-run` reports whether `package.json` would be created or updated
|
|
13
16
|
* without writing it, and repeated synchronization is idempotent.
|
|
14
17
|
*
|
|
15
|
-
* The devtools package itself is excluded from the consumer contract to avoid rewriting its own
|
|
16
|
-
* manifest.
|
|
17
|
-
*
|
|
18
18
|
* @readme
|
|
19
19
|
*/
|
|
20
20
|
import { readFileSync } from 'node:fs';
|
|
21
21
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
22
22
|
import { resolve } from 'node:path';
|
|
23
|
+
import { bunRuntimePolicy } from '../../policy/bunRuntimePolicy.js';
|
|
23
24
|
const PACKAGE_PATH = 'package.json';
|
|
24
25
|
const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
|
|
25
26
|
const ANKH_PACKAGE_NAME = '@ankhorage/ankh';
|
|
27
|
+
const BUN_TYPES_PACKAGE_NAME = '@types/bun';
|
|
26
28
|
const STANDARD_SCRIPTS = {
|
|
27
29
|
lint: 'ankhorage-eslint . --max-warnings=0',
|
|
28
30
|
'lint:fix': 'ankhorage-eslint . --fix --max-warnings=0',
|
|
@@ -78,20 +80,23 @@ export async function syncPackageManifest(targetDirectory, devtoolsVersion, opti
|
|
|
78
80
|
}
|
|
79
81
|
export function applyManagedPackageContract(manifest, devtoolsVersion) {
|
|
80
82
|
if (manifest.name === DEVTOOLS_PACKAGE_NAME) {
|
|
81
|
-
return manifest;
|
|
83
|
+
return applyBunRuntimePolicy(manifest);
|
|
82
84
|
}
|
|
83
85
|
const scripts = { ...toRecord(manifest.scripts), ...STANDARD_SCRIPTS };
|
|
84
86
|
const devDependencies = removeOwnedDependencies(toRecord(manifest.devDependencies));
|
|
85
87
|
const dependencies = toRecord(manifest.dependencies);
|
|
86
88
|
applyDevtoolsDependencyPlacement(manifest, dependencies, devDependencies, devtoolsVersion);
|
|
87
|
-
return {
|
|
89
|
+
return applyBunRuntimePolicy({
|
|
88
90
|
...manifest,
|
|
89
91
|
...normalizedDependencies(manifest, dependencies),
|
|
90
92
|
scripts,
|
|
91
93
|
devDependencies,
|
|
92
|
-
};
|
|
94
|
+
});
|
|
93
95
|
}
|
|
94
96
|
export function isManagedPackageContractCurrent(manifest, devtoolsVersion) {
|
|
97
|
+
if (!hasCurrentBunRuntimePolicy(manifest)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
95
100
|
if (manifest.name === DEVTOOLS_PACKAGE_NAME) {
|
|
96
101
|
return true;
|
|
97
102
|
}
|
|
@@ -125,6 +130,20 @@ async function readPackageManifest(targetDirectory) {
|
|
|
125
130
|
throw error;
|
|
126
131
|
}
|
|
127
132
|
}
|
|
133
|
+
function applyBunRuntimePolicy(manifest) {
|
|
134
|
+
const devDependencies = toRecord(manifest.devDependencies);
|
|
135
|
+
devDependencies[BUN_TYPES_PACKAGE_NAME] = bunRuntimePolicy.typesRange;
|
|
136
|
+
return {
|
|
137
|
+
...manifest,
|
|
138
|
+
packageManager: bunRuntimePolicy.packageManager,
|
|
139
|
+
devDependencies,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function hasCurrentBunRuntimePolicy(manifest) {
|
|
143
|
+
const devDependencies = toRecord(manifest.devDependencies);
|
|
144
|
+
return (manifest.packageManager === bunRuntimePolicy.packageManager &&
|
|
145
|
+
devDependencies[BUN_TYPES_PACKAGE_NAME] === bunRuntimePolicy.typesRange);
|
|
146
|
+
}
|
|
128
147
|
function applyDevtoolsDependencyPlacement(manifest, dependencies, devDependencies, devtoolsVersion) {
|
|
129
148
|
const versionRange = `^${devtoolsVersion}`;
|
|
130
149
|
if (manifest.name === ANKH_PACKAGE_NAME) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
export function planBunDependencySync(targetDirectory) {
|
|
5
|
+
return {
|
|
6
|
+
relativePath: 'bun.lock',
|
|
7
|
+
action: existsSync(resolve(targetDirectory, 'bun.lock')) ? 'would-update' : 'would-create',
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export async function syncBunDependencies(targetDirectory) {
|
|
11
|
+
const existed = existsSync(resolve(targetDirectory, 'bun.lock'));
|
|
12
|
+
await runBunInstall(targetDirectory);
|
|
13
|
+
return {
|
|
14
|
+
relativePath: 'bun.lock',
|
|
15
|
+
action: existed ? 'updated' : 'created',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function runBunInstall(targetDirectory) {
|
|
19
|
+
await new Promise((resolvePromise, rejectPromise) => {
|
|
20
|
+
const child = spawn('bun', ['install'], { cwd: targetDirectory, stdio: 'inherit' });
|
|
21
|
+
child.once('error', rejectPromise);
|
|
22
|
+
child.once('exit', (code) => {
|
|
23
|
+
if (code === 0) {
|
|
24
|
+
resolvePromise();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
rejectPromise(new Error(`bun install exited with code ${code ?? 'unknown'}.`));
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
readonly sourceUrl: URL;
|
|
4
|
-
}, {
|
|
5
|
-
readonly relativePath: ".github/workflows/release.yml";
|
|
6
|
-
readonly sourceUrl: URL;
|
|
7
|
-
}];
|
|
1
|
+
import type { ManagedFileDefinition } from '../shared/managedFiles.js';
|
|
2
|
+
export declare const workflowManagedFiles: readonly [ManagedFileDefinition, ManagedFileDefinition];
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { bunRuntimePolicy } from '../../policy/bunRuntimePolicy.js';
|
|
3
|
+
const BUN_VERSION_TOKEN = '__ANKH_BUN_VERSION__';
|
|
1
4
|
export const workflowManagedFiles = [
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
sourceUrl: new URL('./files/ci.yml', import.meta.url),
|
|
5
|
-
},
|
|
6
|
-
{
|
|
7
|
-
relativePath: '.github/workflows/release.yml',
|
|
8
|
-
sourceUrl: new URL('./files/release.yml', import.meta.url),
|
|
9
|
-
},
|
|
5
|
+
createWorkflowDefinition('.github/workflows/ci.yml', './files/ci.yml'),
|
|
6
|
+
createWorkflowDefinition('.github/workflows/release.yml', './files/release.yml'),
|
|
10
7
|
];
|
|
8
|
+
function createWorkflowDefinition(relativePath, sourcePath) {
|
|
9
|
+
const sourceUrl = new URL(sourcePath, import.meta.url);
|
|
10
|
+
return {
|
|
11
|
+
relativePath,
|
|
12
|
+
render: async () => {
|
|
13
|
+
const template = await readFile(sourceUrl, 'utf8');
|
|
14
|
+
return template.replaceAll(BUN_VERSION_TOKEN, bunRuntimePolicy.version);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ankhorage/devtools",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Shared development tools and repository standards for Ankhorage",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/ankhorage/devtools#readme",
|
|
@@ -116,9 +116,9 @@
|
|
|
116
116
|
"@ankhorage/ankh": "^0.4.0",
|
|
117
117
|
"@ankhorage/doctor": "0.3.0",
|
|
118
118
|
"@changesets/cli": "^2.30.0",
|
|
119
|
-
"@types/bun": "^1.3.
|
|
119
|
+
"@types/bun": "^1.3.14",
|
|
120
120
|
"@types/node": "^25.2.3",
|
|
121
121
|
"typescript": "^5.9.3"
|
|
122
122
|
},
|
|
123
|
-
"packageManager": "bun@1.3.
|
|
123
|
+
"packageManager": "bun@1.3.14"
|
|
124
124
|
}
|