@ankhorage/devtools 1.7.2 → 1.8.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 CHANGED
@@ -20,6 +20,7 @@ src/
20
20
  ```
21
21
 
22
22
  - `policy`: shared repository runtime policy, including the canonical Bun version
23
+ - `changesets`: package-resolved Changesets execution and release command policy
23
24
  - `eslint`: shared flat ESLint configuration, automatic project profiles, and the bundled ESLint runner
24
25
  - `prettier`: shared Prettier configuration and the bundled Prettier runner
25
26
  - `knip`: shared Knip configuration helpers and the bundled Knip runner
@@ -27,7 +28,7 @@ src/
27
28
  - `workflows`: canonical `.github/workflows/ci.yml` and `release.yml`
28
29
  - `vscode`: canonical `.vscode/settings.json` and `extensions.json`
29
30
 
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.
31
+ The package owns the supported Changesets CLI, 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.
31
32
 
32
33
  ## Bootstrap
33
34
 
@@ -55,6 +56,7 @@ Synchronization ensures `@ankhorage/devtools` is declared using the version of t
55
56
  The package is discovered under the `devtools` category and exposes these capabilities:
56
57
 
57
58
  - `devtools.lint`
59
+ - `devtools.changeset`
58
60
  - `devtools.format`
59
61
  - `devtools.knip`
60
62
  - `devtools.sync`
@@ -81,6 +83,7 @@ ankh devtools ...
81
83
  ## Tool commands
82
84
 
83
85
  ```bash
86
+ ankh devtools changeset -- status --since=origin/main
84
87
  ankh devtools lint -- --max-warnings=0 .
85
88
  ankh devtools format -- --check .
86
89
  ankh devtools knip -- --production
@@ -88,6 +91,7 @@ ankh devtools knip -- --production
88
91
 
89
92
  These delegate to the same bundled tools as the package binaries:
90
93
 
94
+ - `ankh devtools changeset` → `ankhorage-changeset`
91
95
  - `ankh devtools lint` → `ankhorage-eslint`
92
96
  - `ankh devtools format` → `ankhorage-prettier`
93
97
  - `ankh devtools knip` → `ankhorage-knip`
@@ -97,11 +101,14 @@ The synchronized package scripts are:
97
101
  ```json
98
102
  {
99
103
  "scripts": {
104
+ "changeset": "ankhorage-changeset",
105
+ "changeset:status": "ankhorage-changeset status --since=origin/main",
100
106
  "lint": "ankhorage-eslint . --max-warnings=0",
101
107
  "lint:fix": "ankhorage-eslint . --fix --max-warnings=0",
102
108
  "format": "ankhorage-prettier --write .",
103
109
  "format:check": "ankhorage-prettier --check .",
104
- "knip:check": "ankhorage-knip"
110
+ "knip:check": "ankhorage-knip",
111
+ "version-packages": "ankhorage-changeset version"
105
112
  }
106
113
  }
107
114
  ```
@@ -154,6 +161,7 @@ Synchronization is deterministic and idempotent:
154
161
  - missing managed artifacts are created
155
162
  - outdated centrally owned artifacts are updated
156
163
  - the managed Bun runtime version is applied consistently to package metadata and workflows
164
+ - Changesets-enabled repositories use the Devtools-owned runner without a direct `@changesets/cli` declaration
157
165
  - package changes are followed by `bun install` after all managed files have been written, keeping installed dependencies and `bun.lock` synchronized without invalidating the running sync
158
166
  - current artifacts are left untouched
159
167
  - unrelated files and package fields are preserved
@@ -277,6 +285,7 @@ Renovate owns the single `BUN_VERSION` literal in `src/policy/bunRuntimePolicy.t
277
285
  It owns:
278
286
 
279
287
  - the `@ankhorage/devtools` dependency version range
288
+ - removal of direct consumer `@changesets/cli` dependencies
280
289
  - `packageManager` according to the managed Bun runtime policy
281
290
  - the `@types/bun` development dependency according to the managed Bun runtime policy
282
291
  - `lint`
@@ -284,10 +293,13 @@ It owns:
284
293
  - `format`
285
294
  - `format:check`
286
295
  - `knip:check`
296
+ - `changeset`, `changeset:status`, and `version-packages` for Changesets-enabled repositories
287
297
 
288
298
  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.
289
299
 
290
- 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.
300
+ When this managed package contract changes, synchronization runs `bun install`. This updates installed dependencies and `bun.lock` before sync completes. It also removes direct dependencies for Changesets and direct devDependencies for tools and ESLint plugins already provided by `@ankhorage/devtools`. Unrelated scripts, dependencies, metadata, and repository-specific configuration remain unchanged.
301
+
302
+ A repository participates in Changesets synchronization when `.changeset/config.json` exists or any of the canonical `changeset`, `changeset:status`, or `version-packages` script keys is present. This explicit rule migrates partially configured repositories while ensuring repositories without Changesets do not acquire release tooling. The repository continues to own `.changeset/config.json`, pending `.changeset/*.md` files, and its package release semantics. Direct `@changesets/cli` declarations, ambient `changeset` scripts, and `bunx changeset` workflow commands are not supported consumer forms.
291
303
 
292
304
  ## Managed GitHub Actions workflows
293
305
 
@@ -299,7 +311,7 @@ When this managed package contract changes, synchronization runs `bun install`.
299
311
  .github/workflows/release.yml
300
312
  ```
301
313
 
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.
314
+ CI and Release render their `bun-version` from the same managed Bun runtime policy used for `package.json`. They also render Changesets status, version, and publish commands from the same policy that owns the synchronized package scripts. 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. Release publishing calls the Devtools-owned runner through the synchronized package script; it never resolves an ambient or mutable Changesets executable.
303
315
 
304
316
  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.
305
317
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runStandaloneTool } from '../runStandaloneTool.js';
3
+ const result = await runStandaloneTool('changeset', process.argv.slice(2));
4
+ process.exit(result.exitCode);
@@ -1,7 +1,7 @@
1
- export type DevtoolsToolName = 'format' | 'knip' | 'lint';
1
+ export type DevtoolsToolName = 'changeset' | 'format' | 'knip' | 'lint';
2
2
  type DevtoolsManagedScope = 'all' | 'eslint' | 'knip' | 'package' | 'prettier' | 'vscode' | 'workflows';
3
3
  type DevtoolsManagedOperation = 'status' | 'sync';
4
- type DevtoolsCapability = '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';
4
+ type DevtoolsCapability = 'devtools.changeset' | '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';
5
5
  interface DevtoolsCommandBase {
6
6
  readonly path: readonly [string, ...string[]];
7
7
  readonly capability: DevtoolsCapability;
@@ -10,8 +10,8 @@ interface DevtoolsCommandBase {
10
10
  export interface DevtoolsExternalCommandDefinition extends DevtoolsCommandBase {
11
11
  readonly kind: 'external';
12
12
  readonly toolName: DevtoolsToolName;
13
- readonly packageName: 'eslint' | 'knip' | 'prettier';
14
- readonly binName: 'eslint' | 'knip' | 'prettier';
13
+ readonly packageName: '@changesets/cli' | 'eslint' | 'knip' | 'prettier';
14
+ readonly binName: 'changeset' | 'eslint' | 'knip' | 'prettier';
15
15
  }
16
16
  export interface DevtoolsRepositoryCommandDefinition extends DevtoolsCommandBase {
17
17
  readonly kind: 'repository';
@@ -1,7 +1,8 @@
1
1
  const DEVTOOLS_COMMANDS = [
2
- externalCommand('lint', 'devtools.lint', 'Run the shared ESLint toolchain.', 'eslint'),
3
- externalCommand('format', 'devtools.format', 'Run the shared Prettier toolchain.', 'prettier'),
4
- externalCommand('knip', 'devtools.knip', 'Run the shared Knip toolchain.', 'knip'),
2
+ externalCommand('changeset', 'devtools.changeset', 'Run the shared Changesets toolchain.', '@changesets/cli', 'changeset'),
3
+ externalCommand('lint', 'devtools.lint', 'Run the shared ESLint toolchain.', 'eslint', 'eslint'),
4
+ externalCommand('format', 'devtools.format', 'Run the shared Prettier toolchain.', 'prettier', 'prettier'),
5
+ externalCommand('knip', 'devtools.knip', 'Run the shared Knip toolchain.', 'knip', 'knip'),
5
6
  repositoryCommand(['sync'], 'devtools.sync', 'Synchronize all managed repository setup.', 'all', 'sync'),
6
7
  repositoryCommand(['status'], 'devtools.status', 'Report drift for all managed repository setup.', 'all', 'status'),
7
8
  repositoryCommand(['eslint', 'sync'], 'devtools.eslint.sync', 'Synchronize the shared ESLint setup.', 'eslint', 'sync'),
@@ -32,7 +33,7 @@ export function getDevtoolsToolCommand(toolName) {
32
33
  }
33
34
  throw new Error(`Unknown devtools tool command: ${toolName}`);
34
35
  }
35
- function externalCommand(toolName, capability, summary, packageName) {
36
+ function externalCommand(toolName, capability, summary, packageName, binName) {
36
37
  return {
37
38
  kind: 'external',
38
39
  toolName,
@@ -40,7 +41,7 @@ function externalCommand(toolName, capability, summary, packageName) {
40
41
  capability,
41
42
  summary,
42
43
  packageName,
43
- binName: packageName,
44
+ binName,
44
45
  };
45
46
  }
46
47
  function repositoryCommand(path, capability, summary, scope, operation) {
@@ -2,10 +2,10 @@ declare const provider: {
2
2
  id: string;
3
3
  category: string;
4
4
  version: string;
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")[];
5
+ capabilities: ("devtools.changeset" | "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
7
  path: string[];
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";
8
+ capability: "devtools.changeset" | "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: {
package/dist/cli/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /***
2
2
  * Run and synchronize the shared development toolchain through the Ankh CLI.
3
3
  *
4
- * `ankh devtools lint`, `ankh devtools format`, and `ankh devtools knip` execute the bundled
5
- * ESLint, Prettier, and Knip versions. Repository synchronization is available through
4
+ * `ankh devtools changeset`, `ankh devtools lint`, `ankh devtools format`, and
5
+ * `ankh devtools knip` execute the bundled Changesets, ESLint, Prettier, and Knip versions.
6
+ * Repository synchronization is available through
6
7
  * `ankh devtools sync` and `ankh devtools status`, with focused `eslint`, `prettier`, `knip`,
7
8
  * `package`, `workflows`, and `vscode` sync/status subcommands.
8
9
  *
@@ -1,6 +1,7 @@
1
1
  import { bunRuntimePolicy } from '../policy/bunRuntimePolicy.js';
2
2
  const REQUIRED_README_SNIPPETS = [
3
3
  'ankh devtools lint',
4
+ 'ankh devtools changeset',
4
5
  'ankh devtools format',
5
6
  'ankh devtools knip',
6
7
  'ankh devtools sync',
@@ -20,6 +21,8 @@ const REQUIRED_README_SNIPPETS = [
20
21
  '--dry-run',
21
22
  "profile: 'auto'",
22
23
  '@ankhorage/utility/project',
24
+ 'ankhorage-changeset',
25
+ '.changeset/config.json',
23
26
  '<!-- devtools-bun-policy:start -->',
24
27
  '<!-- devtools-bun-policy:end -->',
25
28
  'bun scripts/sync-renovate-owner.ts sync repository',
@@ -0,0 +1,19 @@
1
+ export declare const changesetsPolicy: {
2
+ readonly packageName: "@changesets/cli";
3
+ readonly binaryName: "ankhorage-changeset";
4
+ readonly packageScripts: {
5
+ readonly changeset: "ankhorage-changeset";
6
+ readonly 'changeset:status': "ankhorage-changeset status --since=origin/main";
7
+ readonly 'version-packages': "ankhorage-changeset version";
8
+ };
9
+ readonly ownerPackageScripts: {
10
+ readonly changeset: "bun src/cli/bin/changeset.ts";
11
+ readonly 'changeset:status': "bun src/cli/bin/changeset.ts status --since=origin/main";
12
+ readonly 'version-packages': "bun src/cli/bin/changeset.ts version";
13
+ };
14
+ readonly workflowCommands: {
15
+ readonly status: "bun run changeset:status";
16
+ readonly version: "bun run version-packages";
17
+ readonly publish: "bun run changeset -- publish";
18
+ };
19
+ };
@@ -0,0 +1,19 @@
1
+ export const changesetsPolicy = {
2
+ packageName: '@changesets/cli',
3
+ binaryName: 'ankhorage-changeset',
4
+ packageScripts: {
5
+ changeset: 'ankhorage-changeset',
6
+ 'changeset:status': 'ankhorage-changeset status --since=origin/main',
7
+ 'version-packages': 'ankhorage-changeset version',
8
+ },
9
+ ownerPackageScripts: {
10
+ changeset: 'bun src/cli/bin/changeset.ts',
11
+ 'changeset:status': 'bun src/cli/bin/changeset.ts status --since=origin/main',
12
+ 'version-packages': 'bun src/cli/bin/changeset.ts version',
13
+ },
14
+ workflowCommands: {
15
+ status: 'bun run changeset:status',
16
+ version: 'bun run version-packages',
17
+ publish: 'bun run changeset -- publish',
18
+ },
19
+ };
@@ -3,6 +3,6 @@ export declare function inspectPackageManifest(targetDirectory: string, devtools
3
3
  export declare function syncPackageManifest(targetDirectory: string, devtoolsVersion: string, options: {
4
4
  readonly dryRun: boolean;
5
5
  }): Promise<ManagedFileSyncResult>;
6
- export declare function applyManagedPackageContract(manifest: Record<string, unknown>, devtoolsVersion: string): Record<string, unknown>;
7
- export declare function isManagedPackageContractCurrent(manifest: Record<string, unknown>, devtoolsVersion: string): boolean;
6
+ export declare function applyManagedPackageContract(manifest: Record<string, unknown>, devtoolsVersion: string, changesetsConfigExists?: boolean): Record<string, unknown>;
7
+ export declare function isManagedPackageContractCurrent(manifest: Record<string, unknown>, devtoolsVersion: string, changesetsConfigExists?: boolean): boolean;
8
8
  export declare function readCurrentDevtoolsVersion(): string;
@@ -17,15 +17,17 @@
17
17
  *
18
18
  * @readme
19
19
  */
20
- import { readFileSync } from 'node:fs';
20
+ import { existsSync, readFileSync } from 'node:fs';
21
21
  import { readFile, writeFile } from 'node:fs/promises';
22
22
  import { resolve } from 'node:path';
23
23
  import { applyBunRuntimePolicy } from '../../policy/applyBunRuntimePolicy.js';
24
24
  import { bunRuntimePolicy } from '../../policy/bunRuntimePolicy.js';
25
+ import { changesetsPolicy } from '../../policy/changesetsPolicy.js';
25
26
  const PACKAGE_PATH = 'package.json';
26
27
  const DEVTOOLS_PACKAGE_NAME = '@ankhorage/devtools';
27
28
  const ANKH_PACKAGE_NAME = '@ankhorage/ankh';
28
29
  const BUN_TYPES_PACKAGE_NAME = '@types/bun';
30
+ const CHANGESETS_CONFIG_PATH = '.changeset/config.json';
29
31
  const STANDARD_SCRIPTS = {
30
32
  lint: 'ankhorage-eslint . --max-warnings=0',
31
33
  'lint:fix': 'ankhorage-eslint . --fix --max-warnings=0',
@@ -56,14 +58,15 @@ export async function inspectPackageManifest(targetDirectory, devtoolsVersion) {
56
58
  }
57
59
  return {
58
60
  relativePath: PACKAGE_PATH,
59
- state: isManagedPackageContractCurrent(snapshot.manifest, devtoolsVersion)
61
+ state: isManagedPackageContractCurrent(snapshot.manifest, devtoolsVersion, snapshot.changesetsConfigExists)
60
62
  ? 'current'
61
63
  : 'outdated',
62
64
  };
63
65
  }
64
66
  export async function syncPackageManifest(targetDirectory, devtoolsVersion, options) {
65
67
  const snapshot = await readPackageManifest(targetDirectory);
66
- if (snapshot.exists && isManagedPackageContractCurrent(snapshot.manifest, devtoolsVersion)) {
68
+ if (snapshot.exists &&
69
+ isManagedPackageContractCurrent(snapshot.manifest, devtoolsVersion, snapshot.changesetsConfigExists)) {
67
70
  return { relativePath: PACKAGE_PATH, action: 'unchanged' };
68
71
  }
69
72
  if (options.dryRun) {
@@ -72,22 +75,28 @@ export async function syncPackageManifest(targetDirectory, devtoolsVersion, opti
72
75
  action: snapshot.exists ? 'would-update' : 'would-create',
73
76
  };
74
77
  }
75
- const updatedManifest = applyManagedPackageContract(snapshot.manifest, devtoolsVersion);
78
+ const updatedManifest = applyManagedPackageContract(snapshot.manifest, devtoolsVersion, snapshot.changesetsConfigExists);
76
79
  await writeFile(resolve(targetDirectory, PACKAGE_PATH), serializePackageManifest(updatedManifest), 'utf8');
77
80
  return {
78
81
  relativePath: PACKAGE_PATH,
79
82
  action: snapshot.exists ? 'updated' : 'created',
80
83
  };
81
84
  }
82
- export function applyManagedPackageContract(manifest, devtoolsVersion) {
85
+ export function applyManagedPackageContract(manifest, devtoolsVersion, changesetsConfigExists = false) {
83
86
  if (manifest.name === DEVTOOLS_PACKAGE_NAME) {
84
87
  return applyBunRuntimePolicy(manifest, bunRuntimePolicy);
85
88
  }
86
89
  const scripts = { ...toRecord(manifest.scripts) };
90
+ const changesetsEnabled = isChangesetsEnabled(scripts, changesetsConfigExists);
87
91
  delete scripts.knip;
88
92
  Object.assign(scripts, STANDARD_SCRIPTS);
93
+ if (changesetsEnabled) {
94
+ Object.assign(scripts, changesetsPolicy.packageScripts);
95
+ }
89
96
  const devDependencies = removeOwnedDependencies(toRecord(manifest.devDependencies));
90
97
  const dependencies = toRecord(manifest.dependencies);
98
+ delete dependencies[changesetsPolicy.packageName];
99
+ delete devDependencies[changesetsPolicy.packageName];
91
100
  applyDevtoolsDependencyPlacement(manifest, dependencies, devDependencies, devtoolsVersion);
92
101
  return applyBunRuntimePolicy({
93
102
  ...manifest,
@@ -96,7 +105,7 @@ export function applyManagedPackageContract(manifest, devtoolsVersion) {
96
105
  devDependencies,
97
106
  }, bunRuntimePolicy);
98
107
  }
99
- export function isManagedPackageContractCurrent(manifest, devtoolsVersion) {
108
+ export function isManagedPackageContractCurrent(manifest, devtoolsVersion, changesetsConfigExists = false) {
100
109
  if (!hasCurrentBunRuntimePolicy(manifest)) {
101
110
  return false;
102
111
  }
@@ -106,8 +115,12 @@ export function isManagedPackageContractCurrent(manifest, devtoolsVersion) {
106
115
  const scripts = toRecord(manifest.scripts);
107
116
  const devDependencies = toRecord(manifest.devDependencies);
108
117
  const dependencies = toRecord(manifest.dependencies);
118
+ const changesetsEnabled = isChangesetsEnabled(scripts, changesetsConfigExists);
109
119
  return (hasStandardScripts(scripts) &&
110
120
  DEVTOOLS_OWNED_DEV_DEPENDENCIES.every((name) => devDependencies[name] === undefined) &&
121
+ dependencies[changesetsPolicy.packageName] === undefined &&
122
+ devDependencies[changesetsPolicy.packageName] === undefined &&
123
+ hasCurrentChangesetsScripts(scripts, changesetsEnabled) &&
111
124
  hasCurrentDevtoolsDependencyPlacement(manifest, dependencies, devDependencies, devtoolsVersion));
112
125
  }
113
126
  export function readCurrentDevtoolsVersion() {
@@ -118,17 +131,18 @@ export function readCurrentDevtoolsVersion() {
118
131
  return parsed.version;
119
132
  }
120
133
  async function readPackageManifest(targetDirectory) {
134
+ const changesetsConfigExists = existsSync(resolve(targetDirectory, CHANGESETS_CONFIG_PATH));
121
135
  try {
122
136
  const contents = await readFile(resolve(targetDirectory, PACKAGE_PATH), 'utf8');
123
137
  const parsed = JSON.parse(contents);
124
138
  if (!isRecord(parsed)) {
125
139
  throw new Error(`Expected ${PACKAGE_PATH} to contain a JSON object.`);
126
140
  }
127
- return { exists: true, manifest: parsed };
141
+ return { changesetsConfigExists, exists: true, manifest: parsed };
128
142
  }
129
143
  catch (error) {
130
144
  if (isNodeError(error) && error.code === 'ENOENT') {
131
- return { exists: false, manifest: {} };
145
+ return { changesetsConfigExists, exists: false, manifest: {} };
132
146
  }
133
147
  throw error;
134
148
  }
@@ -172,6 +186,14 @@ function hasStandardScripts(scripts) {
172
186
  return (scripts.knip === undefined &&
173
187
  Object.entries(STANDARD_SCRIPTS).every(([name, command]) => scripts[name] === command));
174
188
  }
189
+ function hasCurrentChangesetsScripts(scripts, changesetsEnabled) {
190
+ return (!changesetsEnabled ||
191
+ Object.entries(changesetsPolicy.packageScripts).every(([name, command]) => scripts[name] === command));
192
+ }
193
+ function isChangesetsEnabled(scripts, changesetsConfigExists) {
194
+ return (changesetsConfigExists ||
195
+ Object.keys(changesetsPolicy.packageScripts).some((scriptName) => scripts[scriptName] !== undefined));
196
+ }
175
197
  function serializePackageManifest(manifest) {
176
198
  return `${JSON.stringify(manifest, null, 2)}\n`;
177
199
  }
@@ -88,7 +88,7 @@ jobs:
88
88
  if: github.event_name == 'pull_request'
89
89
  run: |
90
90
  if node -e "const p=require('./package.json'); process.exit(p.scripts?.['changeset:status'] ? 0 : 1)"; then
91
- bun run changeset:status
91
+ __ANKH_CHANGESETS_STATUS_COMMAND__
92
92
  else
93
93
  echo "No changeset:status script found; skipping."
94
94
  fi
@@ -53,8 +53,8 @@ jobs:
53
53
  if: hashFiles('.changeset/config.json') != ''
54
54
  uses: changesets/action@v1
55
55
  with:
56
- version: bun run version-packages
57
- publish: bunx changeset publish
56
+ version: __ANKH_CHANGESETS_VERSION_COMMAND__
57
+ publish: __ANKH_CHANGESETS_PUBLISH_COMMAND__
58
58
  commit: Version Packages
59
59
  title: Version Packages
60
60
  env:
@@ -1,9 +1,16 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import { changesetsPolicy } from '../../policy/changesetsPolicy.js';
2
3
  export async function renderWorkflowAsync(sourceUrl, policy) {
3
4
  const template = await readFile(sourceUrl, 'utf8');
4
5
  return template
5
6
  .replaceAll(BUN_VERSION_TOKEN, policy.bunVersion)
7
+ .replaceAll(CHANGESETS_PUBLISH_COMMAND_TOKEN, changesetsPolicy.workflowCommands.publish)
8
+ .replaceAll(CHANGESETS_STATUS_COMMAND_TOKEN, changesetsPolicy.workflowCommands.status)
9
+ .replaceAll(CHANGESETS_VERSION_COMMAND_TOKEN, changesetsPolicy.workflowCommands.version)
6
10
  .replaceAll(NODE_VERSION_TOKEN, policy.nodeVersion);
7
11
  }
8
12
  const BUN_VERSION_TOKEN = '__ANKH_BUN_VERSION__';
13
+ const CHANGESETS_PUBLISH_COMMAND_TOKEN = '__ANKH_CHANGESETS_PUBLISH_COMMAND__';
14
+ const CHANGESETS_STATUS_COMMAND_TOKEN = '__ANKH_CHANGESETS_STATUS_COMMAND__';
15
+ const CHANGESETS_VERSION_COMMAND_TOKEN = '__ANKH_CHANGESETS_VERSION_COMMAND__';
9
16
  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.7.2",
3
+ "version": "1.8.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",
@@ -19,6 +19,7 @@
19
19
  "category": "devtools",
20
20
  "provider": "./dist/cli/index.js",
21
21
  "capabilities": [
22
+ "devtools.changeset",
22
23
  "devtools.lint",
23
24
  "devtools.format",
24
25
  "devtools.knip",
@@ -49,6 +50,7 @@
49
50
  "developer-tools"
50
51
  ],
51
52
  "bin": {
53
+ "ankhorage-changeset": "./dist/cli/bin/changeset.js",
52
54
  "ankhorage-eslint": "./dist/cli/bin/eslint.js",
53
55
  "ankhorage-knip": "./dist/cli/bin/knip.js",
54
56
  "ankhorage-prettier": "./dist/cli/bin/prettier.js"
@@ -94,12 +96,13 @@
94
96
  "docs": "bun run docs:check",
95
97
  "docs:check": "bun scripts/check-docs.ts",
96
98
  "test": "bun test",
97
- "changeset": "changeset",
98
- "changeset:status": "changeset status --since=origin/main",
99
- "version-packages": "changeset version"
99
+ "changeset": "bun src/cli/bin/changeset.ts",
100
+ "changeset:status": "bun src/cli/bin/changeset.ts status --since=origin/main",
101
+ "version-packages": "bun src/cli/bin/changeset.ts version"
100
102
  },
101
103
  "dependencies": {
102
104
  "@ankhorage/utility": "^0.2.0",
105
+ "@changesets/cli": "^2.31.1",
103
106
  "@eslint/compat": "^2.1.0",
104
107
  "@eslint/js": "^10.0.1",
105
108
  "eslint": "^10.9.1",
@@ -118,8 +121,7 @@
118
121
  },
119
122
  "devDependencies": {
120
123
  "@ankhorage/ankh": "^0.8.2",
121
- "@ankhorage/doctor": "0.10.1",
122
- "@changesets/cli": "^2.31.1",
124
+ "@ankhorage/doctor": "0.10.2",
123
125
  "@types/bun": "^1.4.0",
124
126
  "@types/node": "^25.9.5",
125
127
  "typescript": "^5.9.3"