@ankhorage/devtools 1.6.0 → 1.7.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 +9 -4
- package/dist/cli/runRepositoryCommand.js +11 -9
- package/dist/tools/package/index.js +7 -4
- package/dist/tools/prettier/managed.d.ts +5 -0
- package/dist/tools/prettier/managed.js +45 -2
- package/dist/tools/workflows/files/ci.yml +4 -3
- package/dist/tools/workflows/files/renovate.yml +21 -0
- package/dist/tools/workflows/index.d.ts +1 -1
- package/dist/tools/workflows/index.js +1 -0
- package/package.json +1 -1
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
|
```
|
|
@@ -154,7 +154,7 @@ Synchronization is deterministic and idempotent:
|
|
|
154
154
|
- missing managed artifacts are created
|
|
155
155
|
- outdated centrally owned artifacts are updated
|
|
156
156
|
- the managed Bun runtime version is applied consistently to package metadata and workflows
|
|
157
|
-
- package changes are followed by `bun install
|
|
157
|
+
- 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
158
|
- current artifacts are left untouched
|
|
159
159
|
- unrelated files and package fields are preserved
|
|
160
160
|
- repeated sync produces only `unchanged` results
|
|
@@ -232,7 +232,7 @@ Use `eslint.local.config.mjs` for narrow repository-specific flat-config overrid
|
|
|
232
232
|
|
|
233
233
|
## Prettier
|
|
234
234
|
|
|
235
|
-
`ankh devtools prettier sync` owns `.prettierrc.js
|
|
235
|
+
`ankh devtools prettier sync` owns `.prettierrc.js`, emits the correct ESM or CommonJS wrapper based on the repository's `package.json` module type, and creates `prettier.local.config.js` once for narrow repository-specific options.
|
|
236
236
|
|
|
237
237
|
The consumer delegates formatting policy to:
|
|
238
238
|
|
|
@@ -240,6 +240,8 @@ The consumer delegates formatting policy to:
|
|
|
240
240
|
@ankhorage/devtools/prettier
|
|
241
241
|
```
|
|
242
242
|
|
|
243
|
+
The wrapper merges shared and local `overrides` in that order. On first synchronization, an existing non-canonical `.prettierrc.js` is preserved as `prettier.local.config.js`; former shared-only Devtools delegates become an empty local config. Later synchronization never overwrites the local file.
|
|
244
|
+
|
|
243
245
|
## Knip
|
|
244
246
|
|
|
245
247
|
`ankh devtools knip sync` bootstraps `knip.config.ts` with:
|
|
@@ -289,10 +291,13 @@ When this managed package contract changes, synchronization runs `bun install`.
|
|
|
289
291
|
|
|
290
292
|
```text
|
|
291
293
|
.github/workflows/ci.yml
|
|
294
|
+
.github/workflows/renovate.yml
|
|
292
295
|
.github/workflows/release.yml
|
|
293
296
|
```
|
|
294
297
|
|
|
295
|
-
|
|
298
|
+
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.
|
|
299
|
+
|
|
300
|
+
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.
|
|
296
301
|
|
|
297
302
|
## Managed VS Code configuration
|
|
298
303
|
|
|
@@ -52,20 +52,22 @@ async function runStatus(scope, targetDirectory, devtoolsVersion, context) {
|
|
|
52
52
|
}
|
|
53
53
|
async function runSync(scope, targetDirectory, devtoolsVersion, dryRun, context) {
|
|
54
54
|
const results = [];
|
|
55
|
+
let packageDependenciesChanged = false;
|
|
55
56
|
if (scope === 'all' || scope === 'package') {
|
|
56
57
|
const packageResult = await syncPackageManifest(targetDirectory, devtoolsVersion, { dryRun });
|
|
57
58
|
results.push(packageResult);
|
|
58
|
-
|
|
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
|
-
}
|
|
59
|
+
packageDependenciesChanged = packageResult.action !== 'unchanged';
|
|
67
60
|
}
|
|
68
61
|
results.push(...(await syncManagedFiles(targetDirectory, getManagedFiles(scope), { dryRun })));
|
|
62
|
+
if (packageDependenciesChanged) {
|
|
63
|
+
if (dryRun) {
|
|
64
|
+
results.push(planBunDependencySync(targetDirectory));
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const syncDependencies = context.syncDependencies ?? syncBunDependencies;
|
|
68
|
+
results.push(await syncDependencies(targetDirectory));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
69
71
|
writeSyncOutput(results, context);
|
|
70
72
|
return { exitCode: 0 };
|
|
71
73
|
}
|
|
@@ -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
|
|
@@ -30,7 +30,7 @@ const STANDARD_SCRIPTS = {
|
|
|
30
30
|
'lint:fix': 'ankhorage-eslint . --fix --max-warnings=0',
|
|
31
31
|
format: 'ankhorage-prettier --write .',
|
|
32
32
|
'format:check': 'ankhorage-prettier --check .',
|
|
33
|
-
knip: 'ankhorage-knip',
|
|
33
|
+
'knip:check': 'ankhorage-knip',
|
|
34
34
|
};
|
|
35
35
|
const DEVTOOLS_OWNED_DEV_DEPENDENCIES = [
|
|
36
36
|
'@eslint/js',
|
|
@@ -82,7 +82,9 @@ export function applyManagedPackageContract(manifest, devtoolsVersion) {
|
|
|
82
82
|
if (manifest.name === DEVTOOLS_PACKAGE_NAME) {
|
|
83
83
|
return applyBunRuntimePolicy(manifest);
|
|
84
84
|
}
|
|
85
|
-
const scripts = { ...toRecord(manifest.scripts)
|
|
85
|
+
const scripts = { ...toRecord(manifest.scripts) };
|
|
86
|
+
delete scripts.knip;
|
|
87
|
+
Object.assign(scripts, STANDARD_SCRIPTS);
|
|
86
88
|
const devDependencies = removeOwnedDependencies(toRecord(manifest.devDependencies));
|
|
87
89
|
const dependencies = toRecord(manifest.dependencies);
|
|
88
90
|
applyDevtoolsDependencyPlacement(manifest, dependencies, devDependencies, devtoolsVersion);
|
|
@@ -175,7 +177,8 @@ function normalizedDependencies(manifest, dependencies) {
|
|
|
175
177
|
: { dependencies };
|
|
176
178
|
}
|
|
177
179
|
function hasStandardScripts(scripts) {
|
|
178
|
-
return
|
|
180
|
+
return (scripts.knip === undefined &&
|
|
181
|
+
Object.entries(STANDARD_SCRIPTS).every(([name, command]) => scripts[name] === command));
|
|
179
182
|
}
|
|
180
183
|
function serializePackageManifest(manifest) {
|
|
181
184
|
return `${JSON.stringify(manifest, null, 2)}\n`;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export declare const prettierManagedFiles: readonly [{
|
|
2
|
+
readonly relativePath: "prettier.local.config.js";
|
|
3
|
+
readonly render: typeof renderInitialLocalConfig;
|
|
4
|
+
readonly mode: "create-only";
|
|
5
|
+
}, {
|
|
2
6
|
readonly relativePath: ".prettierrc.js";
|
|
3
7
|
readonly render: typeof renderPrettierConfig;
|
|
4
8
|
}];
|
|
5
9
|
declare function renderPrettierConfig(targetDirectory: string): Promise<string>;
|
|
10
|
+
declare function renderInitialLocalConfig(targetDirectory: string): Promise<string>;
|
|
6
11
|
export {};
|
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
|
-
const ESM_CONFIG = `
|
|
3
|
+
const ESM_CONFIG = `import sharedConfig from '@ankhorage/devtools/prettier';
|
|
4
|
+
import localConfig from './prettier.local.config.js';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
...sharedConfig,
|
|
8
|
+
...localConfig,
|
|
9
|
+
overrides: [...(sharedConfig.overrides ?? []), ...(localConfig.overrides ?? [])],
|
|
10
|
+
};
|
|
4
11
|
`;
|
|
5
|
-
const COMMONJS_CONFIG = `
|
|
12
|
+
const COMMONJS_CONFIG = `const sharedConfig = require('@ankhorage/devtools/prettier');
|
|
13
|
+
const localConfig = require('./prettier.local.config.js');
|
|
14
|
+
|
|
15
|
+
module.exports = {
|
|
16
|
+
...sharedConfig,
|
|
17
|
+
...localConfig,
|
|
18
|
+
overrides: [...(sharedConfig.overrides ?? []), ...(localConfig.overrides ?? [])],
|
|
19
|
+
};
|
|
20
|
+
`;
|
|
21
|
+
const EMPTY_ESM_LOCAL_CONFIG = `export default {};
|
|
22
|
+
`;
|
|
23
|
+
const EMPTY_COMMONJS_LOCAL_CONFIG = `module.exports = {};
|
|
24
|
+
`;
|
|
25
|
+
const LEGACY_ESM_CONFIG = `export { default } from '@ankhorage/devtools/prettier';
|
|
26
|
+
`;
|
|
27
|
+
const LEGACY_COMMONJS_CONFIG = `module.exports = require('@ankhorage/devtools/prettier');
|
|
6
28
|
`;
|
|
7
29
|
export const prettierManagedFiles = [
|
|
30
|
+
{
|
|
31
|
+
relativePath: 'prettier.local.config.js',
|
|
32
|
+
render: renderInitialLocalConfig,
|
|
33
|
+
mode: 'create-only',
|
|
34
|
+
},
|
|
8
35
|
{
|
|
9
36
|
relativePath: '.prettierrc.js',
|
|
10
37
|
render: renderPrettierConfig,
|
|
@@ -13,6 +40,19 @@ export const prettierManagedFiles = [
|
|
|
13
40
|
async function renderPrettierConfig(targetDirectory) {
|
|
14
41
|
return (await readPackageType(targetDirectory)) === 'module' ? ESM_CONFIG : COMMONJS_CONFIG;
|
|
15
42
|
}
|
|
43
|
+
async function renderInitialLocalConfig(targetDirectory) {
|
|
44
|
+
const isModule = (await readPackageType(targetDirectory)) === 'module';
|
|
45
|
+
try {
|
|
46
|
+
const existingConfig = await readFile(resolve(targetDirectory, '.prettierrc.js'), 'utf8');
|
|
47
|
+
if (!isSharedOnlyConfig(existingConfig))
|
|
48
|
+
return existingConfig;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (!isNodeError(error) || error.code !== 'ENOENT')
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
return isModule ? EMPTY_ESM_LOCAL_CONFIG : EMPTY_COMMONJS_LOCAL_CONFIG;
|
|
55
|
+
}
|
|
16
56
|
async function readPackageType(targetDirectory) {
|
|
17
57
|
try {
|
|
18
58
|
const contents = await readFile(resolve(targetDirectory, 'package.json'), 'utf8');
|
|
@@ -29,6 +69,9 @@ async function readPackageType(targetDirectory) {
|
|
|
29
69
|
function isRecord(value) {
|
|
30
70
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
31
71
|
}
|
|
72
|
+
function isSharedOnlyConfig(value) {
|
|
73
|
+
return [ESM_CONFIG, COMMONJS_CONFIG, LEGACY_ESM_CONFIG, LEGACY_COMMONJS_CONFIG].includes(value);
|
|
74
|
+
}
|
|
32
75
|
function isNodeError(error) {
|
|
33
76
|
return error instanceof Error && 'code' in error;
|
|
34
77
|
}
|
|
@@ -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@b7305e8f17f9b07238f6b827bbc9f866fd498a0f
|
|
@@ -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];
|
|
@@ -5,6 +5,7 @@ const NODE_VERSION_TOKEN = '__ANKH_NODE_VERSION__';
|
|
|
5
5
|
export const workflowManagedFiles = [
|
|
6
6
|
createWorkflowDefinition('.github/workflows/ci.yml', './files/ci.yml'),
|
|
7
7
|
createWorkflowDefinition('.github/workflows/release.yml', './files/release.yml'),
|
|
8
|
+
createWorkflowDefinition('.github/workflows/renovate.yml', './files/renovate.yml'),
|
|
8
9
|
];
|
|
9
10
|
function createWorkflowDefinition(relativePath, sourcePath) {
|
|
10
11
|
const sourceUrl = new URL(sourcePath, import.meta.url);
|
package/package.json
CHANGED