@ankhorage/devtools 1.15.6 → 1.15.8

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
@@ -3,7 +3,7 @@
3
3
 
4
4
  # @ankhorage/devtools
5
5
 
6
- ![license: MIT](././paradox/badges/license.svg) ![npm: v1.15.6](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
6
+ ![license: MIT](././paradox/badges/license.svg) ![npm: v1.15.8](././paradox/badges/npm.svg) ![runtime: bun](././paradox/badges/runtime.svg) ![typescript: strict](././paradox/badges/typescript.svg) ![eslint: checked](././paradox/badges/eslint.svg) ![prettier: checked](././paradox/badges/prettier.svg) ![build: checked](././paradox/badges/build.svg) ![tests: checked](././paradox/badges/tests.svg) ![docs: paradox](././paradox/badges/docs.svg)
7
7
 
8
8
  Shared tooling, repository automation, runtime policies, and agent standards for Ankhorage TypeScript projects
9
9
 
@@ -363,6 +363,8 @@ A repository participates in Changesets synchronization when `.changeset/config.
363
363
 
364
364
  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 the strict `changeset:status --since=origin/main` guard for pull requests. After a green change reaches `main`, Release uses the scoped Ankhorage Renovate Sync App token to apply Changesets versioning directly to `main` in a `chore(release)` `[skip ci]` commit and publishes without creating a second Version Packages pull request. Release publishing calls the Devtools-owned runner through the synchronized package script; it never resolves an ambient or mutable Changesets executable. Changesets v3 creates the local release tags; the managed workflow pushes those exact tags and creates any missing GitHub Releases directly, without parsing Changesets’ human-readable publish output.
365
365
 
366
+ For the **first npm publication** of a new `@ankhorage/*` package, the organization publishing credential must be able to create/publish packages in the `@ankhorage` scope. With a granular npm token, grant package permission **Read and write (publish and stage)** to the `@ankhorage` scope or **All Packages**. If initial publication fails after Changesets has already pushed the release commit, correct the npm credential and rerun Release; the current unpublished version is reused and must not be bumped again. The managed workflow diagnoses this first-publish state separately from registry/network failures.
367
+
366
368
  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. The trusted workflow scopes the Ankhorage Renovate Sync GitHub App token to the current repository and uses it only for the validated commit, allowing every normal pull-request CI workflow to start without manual approval. It never checks out or executes pull-request code in the privileged `pull_request_target` context.
367
369
 
368
370
  ## Managed VS Code configuration
@@ -16,10 +16,16 @@ concurrency:
16
16
  jobs:
17
17
  release:
18
18
  runs-on: ubuntu-latest
19
+ outputs:
20
+ versioned: ${{ steps.release.outputs.versioned }}
21
+ package_name: ${{ steps.release.outputs.package_name }}
22
+ package_version: ${{ steps.release.outputs.package_version }}
23
+ release_sha: ${{ steps.release.outputs.release_sha }}
19
24
 
20
25
  steps:
21
26
  - name: Create the scoped release token
22
27
  id: release-token
28
+ continue-on-error: true
23
29
  uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
24
30
  with:
25
31
  client-id: ${{ vars.ANKHORAGE_RENOVATE_SYNC_CLIENT_ID }}
@@ -28,6 +34,12 @@ jobs:
28
34
  repositories: ${{ github.event.repository.name }}
29
35
  permission-contents: write
30
36
 
37
+ - name: Diagnose unavailable scoped release token
38
+ if: steps.release-token.outcome == 'failure'
39
+ run: |
40
+ echo "::error title=Renovate Sync App repository access required::Could not create the scoped Ankhorage release token for ${GITHUB_REPOSITORY}. Ensure the ankhorage-renovate-sync GitHub App is installed and authorized for this repository, then rerun Release. Bootstrap policy: https://github.com/ankhorage/renovate/issues/115"
41
+ exit 1
42
+
31
43
  - name: Checkout repository
32
44
  uses: actions/checkout@v4
33
45
  with:
@@ -67,7 +79,16 @@ jobs:
67
79
  git checkout -B main origin/main
68
80
 
69
81
  if [ -z "$(find .changeset -maxdepth 1 -type f -name '*.md' ! -name README.md -print -quit)" ]; then
70
- echo "versioned=false" >> "$GITHUB_OUTPUT"
82
+ current_subject="$(git log -1 --pretty=%s)"
83
+ if [ "$current_subject" = "chore(release): version packages [skip ci]" ]; then
84
+ echo "Recovering the canonical versioned release commit without another version bump."
85
+ echo "versioned=true" >> "$GITHUB_OUTPUT"
86
+ echo "package_name=$(node -p "require('./package.json').name")" >> "$GITHUB_OUTPUT"
87
+ echo "package_version=$(node -p "require('./package.json').version")" >> "$GITHUB_OUTPUT"
88
+ echo "release_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
89
+ else
90
+ echo "versioned=false" >> "$GITHUB_OUTPUT"
91
+ fi
71
92
  exit 0
72
93
  fi
73
94
 
@@ -107,13 +128,33 @@ jobs:
107
128
  echo "versioned=true" >> "$GITHUB_OUTPUT"
108
129
  echo "package_name=$(node -p "require('./package.json').name")" >> "$GITHUB_OUTPUT"
109
130
  echo "package_version=$after_version" >> "$GITHUB_OUTPUT"
131
+ echo "release_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
110
132
  fi
111
133
 
112
134
  - name: Publish unpublished packages
113
135
  if: hashFiles('.changeset/config.json') != ''
114
136
  run: |
115
137
  if ! node -e "const p=require('./package.json'); process.exit(p.ankh?.apm === undefined ? 1 : 0)"; then
116
- __ANKH_CHANGESETS_PUBLISH_COMMAND__
138
+ package_name="$(node -p "require('./package.json').name")"
139
+ package_version="$(node -p "require('./package.json').version")"
140
+ registry_evidence="${RUNNER_TEMP}/npm-package-preflight.json"
141
+ package_absent=false
142
+
143
+ if npm view "$package_name" version --json > "$registry_evidence"; then
144
+ :
145
+ elif node -e "const p=require(process.argv[1]); if(p.error?.code !== 'E404') process.exit(1)" "$registry_evidence"; then
146
+ package_absent=true
147
+ else
148
+ echo "::error title=npm registry preflight failed::Could not determine whether $package_name already exists in npm. Refusing to treat an authentication or network failure as first-publish bootstrap."
149
+ exit 1
150
+ fi
151
+
152
+ if ! __ANKH_CHANGESETS_PUBLISH_COMMAND__; then
153
+ if [ "$package_absent" = true ]; then
154
+ echo "::error title=npm scope publish access required::Initial publish of ${package_name}@${package_version} failed while the package is absent from npm. Verify that the configured npm publishing credential has Read and write (publish and stage) access to the @ankhorage scope or All Packages, then rerun Release. The already-versioned release commit on main is reusable; do not create another version bump. Bootstrap guidance: https://github.com/ankhorage/devtools/issues/225"
155
+ fi
156
+ exit 1
157
+ fi
117
158
  exit 0
118
159
  fi
119
160
 
@@ -132,15 +173,29 @@ jobs:
132
173
  __ANKH_APM_RELEASE_COMMAND__ validate . --allow-owner-code --artifact "$artifact"
133
174
  npm publish "$artifact" --ignore-scripts
134
175
  fi
135
- if ! git rev-parse -q --verify "refs/tags/v${package_version}" >/dev/null; then
136
- git tag "v${package_version}"
137
- fi
176
+ - name: Skip release tooling
177
+ if: hashFiles('.changeset/config.json') == ''
178
+ run: echo "No Changesets config found; skipping versioning and publishing."
179
+
180
+ verify-publication:
181
+ needs: release
182
+ if: needs.release.outputs.versioned == 'true'
183
+ runs-on: ubuntu-latest
184
+
185
+ steps:
186
+ - name: Setup Node for npm verification
187
+ uses: actions/setup-node@v4
188
+ with:
189
+ node-version: '__ANKH_NODE_VERSION__'
190
+ registry-url: https://registry.npmjs.org
138
191
 
139
- - name: Verify published npm artifact
140
- if: steps.release.outputs.versioned == 'true'
192
+ - name: Update npm
193
+ run: npm install -g npm@latest
194
+
195
+ - name: Verify published npm artifact from a fresh runner
141
196
  env:
142
- PUBLISHED_PACKAGE_NAME: ${{ steps.release.outputs.package_name }}
143
- PUBLISHED_PACKAGE_VERSION: ${{ steps.release.outputs.package_version }}
197
+ PUBLISHED_PACKAGE_NAME: ${{ needs.release.outputs.package_name }}
198
+ PUBLISHED_PACKAGE_VERSION: ${{ needs.release.outputs.package_version }}
144
199
  run: |
145
200
  set -euo pipefail
146
201
  package_spec="${PUBLISHED_PACKAGE_NAME}@${PUBLISHED_PACKAGE_VERSION}"
@@ -157,9 +212,9 @@ jobs:
157
212
  last_stage="registry-metadata"
158
213
 
159
214
  if npm view "$package_spec" version dist.tarball dist.integrity --json > "$registry_evidence"; then
160
- resolved_version="$(node -e "const p=require(process.argv[1]); process.stdout.write(p.version ?? '')" "$registry_evidence")"
161
- tarball="$(node -e "const p=require(process.argv[1]); process.stdout.write(p['dist.tarball'] ?? '')" "$registry_evidence")"
162
- integrity="$(node -e "const p=require(process.argv[1]); process.stdout.write(p['dist.integrity'] ?? '')" "$registry_evidence")"
215
+ resolved_version="$(node -e "const raw=require(process.argv[1]); const p=Array.isArray(raw)&&raw.length===1?raw[0]:raw; process.stdout.write(typeof p?.version==='string'?p.version:'')" "$registry_evidence")"
216
+ tarball="$(node -e "const raw=require(process.argv[1]); const p=Array.isArray(raw)&&raw.length===1?raw[0]:raw; process.stdout.write(typeof p?.['dist.tarball']==='string'?p['dist.tarball']:'')" "$registry_evidence")"
217
+ integrity="$(node -e "const raw=require(process.argv[1]); const p=Array.isArray(raw)&&raw.length===1?raw[0]:raw; process.stdout.write(typeof p?.['dist.integrity']==='string'?p['dist.integrity']:'')" "$registry_evidence")"
163
218
 
164
219
  if [ "$resolved_version" = "$PUBLISHED_PACKAGE_VERSION" ] && [ -n "$tarball" ] && [ -n "$integrity" ]; then
165
220
  last_stage="tarball-download"
@@ -188,24 +243,58 @@ jobs:
188
243
  cat "$registry_evidence" 2>/dev/null || true
189
244
  exit 1
190
245
 
191
- - name: Finalize Changesets v3 tags and GitHub releases
192
- if: hashFiles('.changeset/config.json') != ''
246
+ finalize:
247
+ needs:
248
+ - release
249
+ - verify-publication
250
+ if: needs.release.outputs.versioned == 'true' && needs.verify-publication.result == 'success'
251
+ runs-on: ubuntu-latest
252
+
253
+ steps:
254
+ - name: Create the scoped release token
255
+ id: release-token
256
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
257
+ with:
258
+ client-id: ${{ vars.ANKHORAGE_RENOVATE_SYNC_CLIENT_ID }}
259
+ private-key: ${{ secrets.ANKHORAGE_RENOVATE_SYNC_PRIVATE_KEY }}
260
+ owner: ankhorage
261
+ repositories: ${{ github.event.repository.name }}
262
+ permission-contents: write
263
+
264
+ - name: Checkout exact release commit
265
+ uses: actions/checkout@v4
266
+ with:
267
+ fetch-depth: 0
268
+ ref: ${{ needs.release.outputs.release_sha }}
269
+ token: ${{ steps.release-token.outputs.token }}
270
+
271
+ - name: Finalize Changesets v3 tag and GitHub release
193
272
  env:
194
- GH_TOKEN: ${{ github.token }}
273
+ GH_TOKEN: ${{ steps.release-token.outputs.token }}
274
+ PUBLISHED_PACKAGE_VERSION: ${{ needs.release.outputs.package_version }}
275
+ RELEASE_SHA: ${{ needs.release.outputs.release_sha }}
195
276
  run: |
196
- git tag --points-at HEAD | while IFS= read -r tag; do
197
- if [ -z "$tag" ]; then
198
- continue
277
+ set -euo pipefail
278
+ tag="v${PUBLISHED_PACKAGE_VERSION}"
279
+ remote_sha="$(git ls-remote --tags origin "refs/tags/$tag" | cut -f1)"
280
+
281
+ if [ -n "$remote_sha" ]; then
282
+ if [ "$remote_sha" != "$RELEASE_SHA" ]; then
283
+ echo "Existing tag $tag points to $remote_sha, expected $RELEASE_SHA."
284
+ exit 1
199
285
  fi
286
+ else
287
+ git tag "$tag" "$RELEASE_SHA"
200
288
  git push origin "refs/tags/$tag"
201
- if ! gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
202
- gh release create "$tag" --repo "$GITHUB_REPOSITORY" --generate-notes
203
- fi
204
- done
289
+ fi
290
+
291
+ if ! gh release view "$tag" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
292
+ gh release create "$tag" --repo "$GITHUB_REPOSITORY" --generate-notes --target "$RELEASE_SHA"
293
+ fi
205
294
 
206
295
  - name: Create the scoped rollout token
207
296
  id: rollout-token
208
- if: steps.release.outputs.versioned == 'true' && steps.release.outputs.package_name == '@ankhorage/devtools'
297
+ if: needs.release.outputs.package_name == '@ankhorage/devtools'
209
298
  uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
210
299
  with:
211
300
  client-id: ${{ vars.ANKHORAGE_RENOVATE_SYNC_CLIENT_ID }}
@@ -215,11 +304,11 @@ jobs:
215
304
  permission-contents: write
216
305
 
217
306
  - name: Trigger the Devtools consumer rollout
218
- if: steps.release.outputs.versioned == 'true' && steps.release.outputs.package_name == '@ankhorage/devtools'
307
+ if: needs.release.outputs.package_name == '@ankhorage/devtools'
219
308
  uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
220
309
  env:
221
- PUBLISHED_PACKAGE_NAME: ${{ steps.release.outputs.package_name }}
222
- PUBLISHED_PACKAGE_VERSION: ${{ steps.release.outputs.package_version }}
310
+ PUBLISHED_PACKAGE_NAME: ${{ needs.release.outputs.package_name }}
311
+ PUBLISHED_PACKAGE_VERSION: ${{ needs.release.outputs.package_version }}
223
312
  with:
224
313
  github-token: ${{ steps.rollout-token.outputs.token }}
225
314
  script: |
@@ -240,7 +329,3 @@ jobs:
240
329
  event_type: 'devtools-release',
241
330
  client_payload: { version: release.version },
242
331
  });
243
-
244
- - name: Skip release tooling
245
- if: hashFiles('.changeset/config.json') == ''
246
- run: echo "No Changesets config found; skipping versioning and publishing."
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ankhorage/devtools",
3
- "version": "1.15.6",
3
+ "version": "1.15.8",
4
4
  "description": "Shared tooling, repository automation, runtime policies, and agent standards for Ankhorage TypeScript projects",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/ankhorage/devtools#readme",