@ankhorage/devtools 1.6.1 → 1.7.1

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 CHANGED
@@ -101,7 +101,7 @@ The synchronized package scripts are:
101
101
  "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
102
102
  "format": "ankhorage-prettier --write .",
103
103
  "format:check": "ankhorage-prettier --check .",
104
- "knip": "ankhorage-knip"
104
+ "knip:check": "ankhorage-knip"
105
105
  }
106
106
  }
107
107
  ```
@@ -258,13 +258,17 @@ export default createKnipConfig();
258
258
 
259
259
  The canonical Bun policy is defined once in devtools and consumed by both package and workflow synchronization. The current policy is:
260
260
 
261
+ <!-- devtools-bun-policy:start -->
262
+
261
263
  ```text
262
264
  Bun runtime 1.3.14
263
265
  packageManager bun@1.3.14
264
266
  @types/bun ^1.3.14
265
267
  ```
266
268
 
267
- Changing the policy in devtools therefore updates the repository-facing Bun version consistently instead of maintaining independent version literals in multiple templates.
269
+ <!-- devtools-bun-policy:end -->
270
+
271
+ Renovate owns the single `BUN_VERSION` literal in `src/policy/bunRuntimePolicy.ts`. Its trusted base-branch workflow invokes `bun scripts/sync-renovate-owner.ts sync repository` to regenerate `packageManager`, `@types/bun`, the Bun workflow setup versions, this documentation block, and `bun.lock`, then runs `bun scripts/sync-renovate-owner.ts status repository` to reject stale generated artifacts. Do not synchronize those values manually in a Renovate branch.
268
272
 
269
273
  ## Managed package contract
270
274
 
@@ -279,7 +283,7 @@ It owns:
279
283
  - `lint:fix`
280
284
  - `format`
281
285
  - `format:check`
282
- - `knip`
286
+ - `knip:check`
283
287
 
284
288
  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.
285
289
 
@@ -291,10 +295,13 @@ When this managed package contract changes, synchronization runs `bun install`.
291
295
 
292
296
  ```text
293
297
  .github/workflows/ci.yml
298
+ .github/workflows/renovate.yml
294
299
  .github/workflows/release.yml
295
300
  ```
296
301
 
297
- 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.
302
+ CI and Release 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.
303
+
304
+ The Renovate workflow accepts only same-repository branches created by `renovate[bot]`. It calls the SHA-pinned `ankhorage/renovate` workflow to add a release Changeset for runtime Ankhorage dependency updates or an empty Changeset for dev-only updates, then dispatches CI for that bot-authored commit. It never checks out or executes pull-request code in the privileged `pull_request_target` context.
298
305
 
299
306
  ## Managed VS Code configuration
300
307
 
@@ -4,12 +4,12 @@ declare const provider: {
4
4
  version: string;
5
5
  capabilities: ("devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync")[];
6
6
  commands: {
7
- path: [string, ...string[]];
7
+ path: string[];
8
8
  capability: "devtools.format" | "devtools.knip" | "devtools.lint" | "devtools.status" | "devtools.sync" | "devtools.eslint.status" | "devtools.eslint.sync" | "devtools.knip.status" | "devtools.knip.sync" | "devtools.package.status" | "devtools.package.sync" | "devtools.prettier.status" | "devtools.prettier.sync" | "devtools.vscode.status" | "devtools.vscode.sync" | "devtools.workflows.status" | "devtools.workflows.sync";
9
9
  summary: string;
10
10
  }[];
11
11
  handlers: {
12
- path: [string, ...string[]];
12
+ path: string[];
13
13
  handler: (request: import("@ankhorage/ankh").AnkhCommandExecutionRequest) => Promise<{
14
14
  readonly exitCode: number;
15
15
  }>;
@@ -1,3 +1,4 @@
1
+ import { bunRuntimePolicy } from '../policy/bunRuntimePolicy.js';
1
2
  const REQUIRED_README_SNIPPETS = [
2
3
  'ankh devtools lint',
3
4
  'ankh devtools format',
@@ -19,6 +20,13 @@ const REQUIRED_README_SNIPPETS = [
19
20
  '--dry-run',
20
21
  "profile: 'auto'",
21
22
  '@ankhorage/utility/project',
23
+ '<!-- devtools-bun-policy:start -->',
24
+ '<!-- devtools-bun-policy:end -->',
25
+ 'bun scripts/sync-renovate-owner.ts sync repository',
26
+ 'bun scripts/sync-renovate-owner.ts status',
27
+ bunRuntimePolicy.version,
28
+ bunRuntimePolicy.packageManager,
29
+ bunRuntimePolicy.typesRange,
22
30
  ];
23
31
  export function getReadmeDocumentationErrors(readmeContents) {
24
32
  return REQUIRED_README_SNIPPETS.flatMap((snippet) => readmeContents.includes(snippet)
@@ -0,0 +1,6 @@
1
+ export declare function synchronizeRenovateOwnerAsync(operation: OwnerSyncOperation, targetDirectory: string, options?: OwnerSyncOptions): Promise<void>;
2
+ type OwnerSyncOperation = 'status' | 'sync';
3
+ interface OwnerSyncOptions {
4
+ readonly runLockfileAsync?: (operation: OwnerSyncOperation, targetDirectory: string) => Promise<void>;
5
+ }
6
+ export {};
@@ -0,0 +1,153 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { applyBunRuntimePolicy } from '../policy/applyBunRuntimePolicy.js';
5
+ import { nodeRuntimePolicy } from '../policy/bunRuntimePolicy.js';
6
+ import { renderWorkflowAsync } from '../tools/workflows/renderWorkflowAsync.js';
7
+ export async function synchronizeRenovateOwnerAsync(operation, targetDirectory, options = {}) {
8
+ const target = resolve(targetDirectory);
9
+ const policy = await readTargetBunPolicyAsync(target);
10
+ const definitions = await createManagedDefinitionsAsync(target, policy);
11
+ await assertDevtoolsTargetAsync(target);
12
+ if (operation === 'sync') {
13
+ await syncDefinitionsAsync(target, definitions);
14
+ await (options.runLockfileAsync ?? runBunLockfileAsync)(operation, target);
15
+ }
16
+ const outdatedPaths = await getOutdatedPathsAsync(target, definitions);
17
+ if (outdatedPaths.length > 0) {
18
+ throw new Error(`Stale Devtools owner policy artifacts: ${outdatedPaths.join(', ')}`);
19
+ }
20
+ if (operation === 'status') {
21
+ await (options.runLockfileAsync ?? runBunLockfileAsync)(operation, target);
22
+ }
23
+ }
24
+ async function assertDevtoolsTargetAsync(targetDirectory) {
25
+ const manifest = JSON.parse(await readFile(resolve(targetDirectory, 'package.json'), 'utf8'));
26
+ if (!isRecord(manifest) || manifest.name !== '@ankhorage/devtools') {
27
+ throw new Error('The Renovate owner sync target must be @ankhorage/devtools.');
28
+ }
29
+ }
30
+ async function createManagedDefinitionsAsync(targetDirectory, policy) {
31
+ const manifest = JSON.parse(await readFile(resolve(targetDirectory, 'package.json'), 'utf8'));
32
+ if (!isRecord(manifest)) {
33
+ throw new Error('Devtools package.json must contain a JSON object.');
34
+ }
35
+ const readme = await readFile(resolve(targetDirectory, 'README.md'), 'utf8');
36
+ const workflowPolicy = {
37
+ bunVersion: policy.version,
38
+ nodeVersion: nodeRuntimePolicy.setupVersion,
39
+ };
40
+ return [
41
+ {
42
+ relativePath: 'package.json',
43
+ contents: serializePackageManifest(applyBunRuntimePolicy(manifest, policy)),
44
+ },
45
+ {
46
+ relativePath: '.github/workflows/ci.yml',
47
+ contents: await renderWorkflowAsync(new URL('../tools/workflows/files/ci.yml', import.meta.url), workflowPolicy),
48
+ },
49
+ {
50
+ relativePath: '.github/workflows/release.yml',
51
+ contents: await renderWorkflowAsync(new URL('../tools/workflows/files/release.yml', import.meta.url), workflowPolicy),
52
+ },
53
+ {
54
+ relativePath: '.github/workflows/renovate.yml',
55
+ contents: await renderWorkflowAsync(new URL('../tools/workflows/files/renovate.yml', import.meta.url), workflowPolicy),
56
+ },
57
+ {
58
+ relativePath: 'README.md',
59
+ contents: replaceReadmePolicy(readme, policy),
60
+ },
61
+ ];
62
+ }
63
+ async function getOutdatedPathsAsync(targetDirectory, definitions) {
64
+ const results = await Promise.all(definitions.map(async ({ contents, relativePath }) => {
65
+ const current = await readFile(resolve(targetDirectory, relativePath), 'utf8').catch((error) => {
66
+ if (isNodeError(error) && error.code === 'ENOENT')
67
+ return null;
68
+ throw error;
69
+ });
70
+ return current === contents ? null : relativePath;
71
+ }));
72
+ return results.filter((relativePath) => relativePath !== null);
73
+ }
74
+ function isNodeError(error) {
75
+ return error instanceof Error && 'code' in error;
76
+ }
77
+ function isRecord(value) {
78
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
79
+ }
80
+ async function readTargetBunPolicyAsync(targetDirectory) {
81
+ const contents = await readFile(resolve(targetDirectory, 'src/policy/bunRuntimePolicy.ts'), 'utf8');
82
+ const matches = [...contents.matchAll(BUN_VERSION_PATTERN)];
83
+ const version = matches.length === 1 ? matches[0]?.[1] : undefined;
84
+ if (version === undefined) {
85
+ throw new Error('Expected exactly one canonical BUN_VERSION literal in the target policy.');
86
+ }
87
+ return {
88
+ packageManager: `bun@${version}`,
89
+ typesRange: `^${version}`,
90
+ version,
91
+ };
92
+ }
93
+ function replaceReadmePolicy(readme, policy) {
94
+ const startIndex = readme.indexOf(README_POLICY_START);
95
+ const endIndex = readme.indexOf(README_POLICY_END);
96
+ if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
97
+ throw new Error('README.md must contain one ordered Devtools Bun policy marker pair.');
98
+ }
99
+ if (readme.includes(README_POLICY_START, startIndex + README_POLICY_START.length) ||
100
+ readme.includes(README_POLICY_END, endIndex + README_POLICY_END.length)) {
101
+ throw new Error('README.md must contain exactly one Devtools Bun policy marker pair.');
102
+ }
103
+ const replacement = `${README_POLICY_START}\n\n${renderReadmePolicy(policy)}\n\n${README_POLICY_END}`;
104
+ return `${readme.slice(0, startIndex)}${replacement}${readme.slice(endIndex + README_POLICY_END.length)}`;
105
+ }
106
+ function renderReadmePolicy(policy) {
107
+ return `\`\`\`text
108
+ Bun runtime ${policy.version}
109
+ packageManager ${policy.packageManager}
110
+ @types/bun ${policy.typesRange}
111
+ \`\`\``;
112
+ }
113
+ async function runBunLockfileAsync(operation, targetDirectory) {
114
+ const args = [
115
+ 'install',
116
+ '--cwd',
117
+ targetDirectory,
118
+ '--ignore-scripts',
119
+ '--lockfile-only',
120
+ '--registry=https://registry.npmjs.org',
121
+ ];
122
+ if (operation === 'status')
123
+ args.push('--frozen-lockfile');
124
+ await new Promise((resolvePromise, rejectPromise) => {
125
+ const child = spawn('bun', args, { stdio: 'inherit' });
126
+ child.once('error', rejectPromise);
127
+ child.once('exit', (code) => {
128
+ if (code === 0)
129
+ return resolvePromise();
130
+ rejectPromise(new Error(`Bun lockfile ${operation} exited with code ${code ?? 'unknown'}.`));
131
+ });
132
+ });
133
+ }
134
+ function serializePackageManifest(manifest) {
135
+ return `${JSON.stringify(manifest, null, 2)}\n`;
136
+ }
137
+ async function syncDefinitionsAsync(targetDirectory, definitions) {
138
+ for (const { contents, relativePath } of definitions) {
139
+ const targetPath = resolve(targetDirectory, relativePath);
140
+ const current = await readFile(targetPath, 'utf8').catch((error) => {
141
+ if (isNodeError(error) && error.code === 'ENOENT')
142
+ return null;
143
+ throw error;
144
+ });
145
+ if (current === contents)
146
+ continue;
147
+ await mkdir(dirname(targetPath), { recursive: true });
148
+ await writeFile(targetPath, contents, 'utf8');
149
+ }
150
+ }
151
+ const BUN_VERSION_PATTERN = /const BUN_VERSION = '(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)';/gu;
152
+ const README_POLICY_END = '<!-- devtools-bun-policy:end -->';
153
+ const README_POLICY_START = '<!-- devtools-bun-policy:start -->';
@@ -0,0 +1,4 @@
1
+ export declare function applyBunRuntimePolicy(manifest: Record<string, unknown>, policy: {
2
+ readonly packageManager: string;
3
+ readonly typesRange: string;
4
+ }): Record<string, unknown>;
@@ -0,0 +1,15 @@
1
+ export function applyBunRuntimePolicy(manifest, policy) {
2
+ const devDependencies = toRecord(manifest.devDependencies);
3
+ devDependencies['@types/bun'] = policy.typesRange;
4
+ return {
5
+ ...manifest,
6
+ packageManager: policy.packageManager,
7
+ devDependencies,
8
+ };
9
+ }
10
+ function isRecord(value) {
11
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
12
+ }
13
+ function toRecord(value) {
14
+ return isRecord(value) ? { ...value } : {};
15
+ }
@@ -1,9 +1,3 @@
1
- /**
2
- * Canonical runtime/tooling policies for Ankhorage repositories.
3
- *
4
- * Import these from `@ankhorage/devtools/policy` when another package needs to inspect
5
- * the managed Bun or Node baseline without defining an independent version authority.
6
- */
7
1
  export declare const bunRuntimePolicy: {
8
2
  readonly packageManager: "bun@1.3.14";
9
3
  readonly typesRange: "^1.3.14";
@@ -4,10 +4,11 @@
4
4
  * Import these from `@ankhorage/devtools/policy` when another package needs to inspect
5
5
  * the managed Bun or Node baseline without defining an independent version authority.
6
6
  */
7
+ const BUN_VERSION = '1.3.14';
7
8
  export const bunRuntimePolicy = {
8
- packageManager: 'bun@1.3.14',
9
- typesRange: '^1.3.14',
10
- version: '1.3.14',
9
+ packageManager: `bun@${BUN_VERSION}`,
10
+ typesRange: `^${BUN_VERSION}`,
11
+ version: BUN_VERSION,
11
12
  };
12
13
  /**
13
14
  * Canonical Node LTS baseline for Node-based Ankhorage tooling and CI execution.
@@ -5,7 +5,7 @@
5
5
  * as a development dependency for normal consumers, while `@ankhorage/ankh` keeps devtools as a
6
6
  * runtime dependency because the CLI loads it as a bundled core provider. Individually installed
7
7
  * toolchain packages that devtools now owns are removed, and the canonical `lint`, `lint:fix`,
8
- * `format`, `format:check`, and `knip` scripts are written.
8
+ * `format`, `format:check`, and `knip:check` scripts are written.
9
9
  * Unrelated manifest fields, scripts, dependencies, and metadata are preserved.
10
10
  *
11
11
  * The Bun runtime policy is shared by every repository, including devtools itself. Devtools skips
@@ -20,6 +20,7 @@
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 { applyBunRuntimePolicy } from '../../policy/applyBunRuntimePolicy.js';
23
24
  import { bunRuntimePolicy } from '../../policy/bunRuntimePolicy.js';
24
25
  const PACKAGE_PATH = 'package.json';
25
26
  const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
@@ -30,7 +31,7 @@ const STANDARD_SCRIPTS = {
30
31
  'lint:fix': 'ankhorage-eslint . --fix --max-warnings=0',
31
32
  format: 'ankhorage-prettier --write .',
32
33
  'format:check': 'ankhorage-prettier --check .',
33
- knip: 'ankhorage-knip',
34
+ 'knip:check': 'ankhorage-knip',
34
35
  };
35
36
  const DEVTOOLS_OWNED_DEV_DEPENDENCIES = [
36
37
  '@eslint/js',
@@ -80,9 +81,11 @@ export async function syncPackageManifest(targetDirectory, devtoolsVersion, opti
80
81
  }
81
82
  export function applyManagedPackageContract(manifest, devtoolsVersion) {
82
83
  if (manifest.name === DEVTOOLS_PACKAGE_NAME) {
83
- return applyBunRuntimePolicy(manifest);
84
+ return applyBunRuntimePolicy(manifest, bunRuntimePolicy);
84
85
  }
85
- const scripts = { ...toRecord(manifest.scripts), ...STANDARD_SCRIPTS };
86
+ const scripts = { ...toRecord(manifest.scripts) };
87
+ delete scripts.knip;
88
+ Object.assign(scripts, STANDARD_SCRIPTS);
86
89
  const devDependencies = removeOwnedDependencies(toRecord(manifest.devDependencies));
87
90
  const dependencies = toRecord(manifest.dependencies);
88
91
  applyDevtoolsDependencyPlacement(manifest, dependencies, devDependencies, devtoolsVersion);
@@ -91,7 +94,7 @@ export function applyManagedPackageContract(manifest, devtoolsVersion) {
91
94
  ...normalizedDependencies(manifest, dependencies),
92
95
  scripts,
93
96
  devDependencies,
94
- });
97
+ }, bunRuntimePolicy);
95
98
  }
96
99
  export function isManagedPackageContractCurrent(manifest, devtoolsVersion) {
97
100
  if (!hasCurrentBunRuntimePolicy(manifest)) {
@@ -130,15 +133,6 @@ async function readPackageManifest(targetDirectory) {
130
133
  throw error;
131
134
  }
132
135
  }
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
136
  function hasCurrentBunRuntimePolicy(manifest) {
143
137
  const devDependencies = toRecord(manifest.devDependencies);
144
138
  return (manifest.packageManager === bunRuntimePolicy.packageManager &&
@@ -175,7 +169,8 @@ function normalizedDependencies(manifest, dependencies) {
175
169
  : { dependencies };
176
170
  }
177
171
  function hasStandardScripts(scripts) {
178
- return Object.entries(STANDARD_SCRIPTS).every(([name, command]) => scripts[name] === command);
172
+ return (scripts.knip === undefined &&
173
+ Object.entries(STANDARD_SCRIPTS).every(([name, command]) => scripts[name] === command));
179
174
  }
180
175
  function serializePackageManifest(manifest) {
181
176
  return `${JSON.stringify(manifest, null, 2)}\n`;
@@ -5,6 +5,7 @@ on:
5
5
  push:
6
6
  branches:
7
7
  - main
8
+ workflow_dispatch:
8
9
 
9
10
  permissions:
10
11
  contents: read
@@ -61,10 +62,10 @@ jobs:
61
62
 
62
63
  - name: Run Knip
63
64
  run: |
64
- if node -e "const p=require('./package.json'); process.exit(p.scripts?.knip ? 0 : 1)"; then
65
- bun run knip
65
+ if node -e "const p=require('./package.json'); process.exit(p.scripts?.['knip:check'] ? 0 : 1)"; then
66
+ bun run knip:check
66
67
  else
67
- echo "No knip script found; skipping."
68
+ echo "No knip:check script found; skipping."
68
69
  fi
69
70
 
70
71
  - name: Run tests
@@ -0,0 +1,21 @@
1
+ name: Renovate
2
+
3
+ on:
4
+ pull_request_target:
5
+ types:
6
+ - opened
7
+ - reopened
8
+ - synchronize
9
+
10
+ permissions:
11
+ actions: write
12
+ contents: write
13
+ pull-requests: read
14
+
15
+ jobs:
16
+ changeset:
17
+ if: >-
18
+ github.actor == 'renovate[bot]' &&
19
+ github.event.pull_request.head.repo.full_name == github.repository &&
20
+ startsWith(github.event.pull_request.head.ref, 'renovate/')
21
+ uses: ankhorage/renovate/.github/workflows/changeset.yml@7d4a5104b94e763ca5be34919f4fcfbb12efd526
@@ -1,2 +1,2 @@
1
1
  import type { ManagedFileDefinition } from '../shared/managedFiles.js';
2
- export declare const workflowManagedFiles: readonly [ManagedFileDefinition, ManagedFileDefinition];
2
+ export declare const workflowManagedFiles: readonly [ManagedFileDefinition, ManagedFileDefinition, ManagedFileDefinition];
@@ -1,20 +1,17 @@
1
- import { readFile } from 'node:fs/promises';
2
1
  import { bunRuntimePolicy, nodeRuntimePolicy } from '../../policy/bunRuntimePolicy.js';
3
- const BUN_VERSION_TOKEN = '__ANKH_BUN_VERSION__';
4
- const NODE_VERSION_TOKEN = '__ANKH_NODE_VERSION__';
2
+ import { renderWorkflowAsync } from './renderWorkflowAsync.js';
5
3
  export const workflowManagedFiles = [
6
4
  createWorkflowDefinition('.github/workflows/ci.yml', './files/ci.yml'),
7
5
  createWorkflowDefinition('.github/workflows/release.yml', './files/release.yml'),
6
+ createWorkflowDefinition('.github/workflows/renovate.yml', './files/renovate.yml'),
8
7
  ];
9
8
  function createWorkflowDefinition(relativePath, sourcePath) {
10
9
  const sourceUrl = new URL(sourcePath, import.meta.url);
11
10
  return {
12
11
  relativePath,
13
- render: async () => {
14
- const template = await readFile(sourceUrl, 'utf8');
15
- return template
16
- .replaceAll(BUN_VERSION_TOKEN, bunRuntimePolicy.version)
17
- .replaceAll(NODE_VERSION_TOKEN, nodeRuntimePolicy.setupVersion);
18
- },
12
+ render: async () => await renderWorkflowAsync(sourceUrl, {
13
+ bunVersion: bunRuntimePolicy.version,
14
+ nodeVersion: nodeRuntimePolicy.setupVersion,
15
+ }),
19
16
  };
20
17
  }
@@ -0,0 +1,4 @@
1
+ export declare function renderWorkflowAsync(sourceUrl: URL, policy: {
2
+ readonly bunVersion: string;
3
+ readonly nodeVersion: string;
4
+ }): Promise<string>;
@@ -0,0 +1,9 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ export async function renderWorkflowAsync(sourceUrl, policy) {
3
+ const template = await readFile(sourceUrl, 'utf8');
4
+ return template
5
+ .replaceAll(BUN_VERSION_TOKEN, policy.bunVersion)
6
+ .replaceAll(NODE_VERSION_TOKEN, policy.nodeVersion);
7
+ }
8
+ const BUN_VERSION_TOKEN = '__ANKH_BUN_VERSION__';
9
+ const NODE_VERSION_TOKEN = '__ANKH_NODE_VERSION__';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/devtools",
3
- "version": "1.6.1",
3
+ "version": "1.7.1",
4
4
  "description": "Shared development tools and repository standards for Ankhorage",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/ankhorage/devtools#readme",
@@ -86,7 +86,7 @@
86
86
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc && mkdir -p dist/tools/prettier dist/tools/workflows dist/tools/vscode && cp src/tools/prettier/index.cjs dist/tools/prettier/index.cjs && cp -R src/tools/workflows/files dist/tools/workflows/files && cp -R src/tools/vscode/files dist/tools/vscode/files",
87
87
  "typecheck": "bun x tsc --noEmit -p tsconfig.test.json",
88
88
  "doctor": "ankhorage-doctor validate .",
89
- "knip": "knip",
89
+ "knip:check": "knip",
90
90
  "lint": "eslint .",
91
91
  "lint:fix": "eslint . --fix --max-warnings=0",
92
92
  "format": "prettier --write .",
@@ -99,7 +99,7 @@
99
99
  "version-packages": "changeset version"
100
100
  },
101
101
  "dependencies": {
102
- "@ankhorage/utility": "^0.1.1",
102
+ "@ankhorage/utility": "^0.2.0",
103
103
  "@eslint/compat": "^2.1.0",
104
104
  "@eslint/js": "^10.0.1",
105
105
  "eslint": "^10.7.0",
@@ -117,8 +117,8 @@
117
117
  "typescript-eslint": "^8.24.0"
118
118
  },
119
119
  "devDependencies": {
120
- "@ankhorage/ankh": "^0.4.0",
121
- "@ankhorage/doctor": "0.3.0",
120
+ "@ankhorage/ankh": "^0.8.2",
121
+ "@ankhorage/doctor": "0.10.1",
122
122
  "@changesets/cli": "^2.30.0",
123
123
  "@types/bun": "^1.3.14",
124
124
  "@types/node": "^25.2.3",