code-foundry 1.46.2 → 1.46.4
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/.github/workflows/release.yml +82 -4
- package/.github/workflows/validation-billing.yml +63 -38
- package/CHANGELOG.md +15 -15
- package/docs/WORKFLOWS.md +17 -0
- package/package.json +1 -1
|
@@ -447,6 +447,9 @@ jobs:
|
|
|
447
447
|
# when it validated, github.token after fallback. The token value
|
|
448
448
|
# lives only in this masked env expression, never in step outputs.
|
|
449
449
|
AUTO_MERGE: ${{ steps.credentials.outputs.auto_merge }}
|
|
450
|
+
# Read Actions ownership with the workflow credential, not an
|
|
451
|
+
# automation token that may only have release/PR write permissions.
|
|
452
|
+
VALIDATION_TOKEN: ${{ github.token }}
|
|
450
453
|
GH_TOKEN: ${{ steps.credentials.outputs.token_source == 'configured' && (secrets.CODE_FOUNDRY_TOKEN) || github.token }}
|
|
451
454
|
run: |
|
|
452
455
|
set -euo pipefail
|
|
@@ -464,12 +467,87 @@ jobs:
|
|
|
464
467
|
fi
|
|
465
468
|
|
|
466
469
|
for pr in "${release_prs[@]}"; do
|
|
467
|
-
|
|
470
|
+
pr_state=$(gh pr view "$pr" --repo "$GITHUB_REPOSITORY" \
|
|
471
|
+
--json isDraft,headRefOid,headRefName \
|
|
472
|
+
--jq '[.isDraft, .headRefOid, .headRefName] | @tsv')
|
|
473
|
+
read -r PR_DRAFT PR_HEAD PR_BRANCH <<< "$pr_state"
|
|
474
|
+
if [[ ! "$PR_DRAFT" =~ ^(true|false)$ || ! "$PR_HEAD" =~ ^[a-f0-9]{40}$ ]]; then
|
|
475
|
+
echo 'Unable to verify generated release PR state.' >&2
|
|
476
|
+
exit 1
|
|
477
|
+
fi
|
|
478
|
+
case "$PR_BRANCH" in
|
|
479
|
+
release-please--branches--main|release-please--branches--main--*) ;;
|
|
480
|
+
*) echo 'Release PR head left the generated namespace; refusing readiness mutation.' >&2; exit 1 ;;
|
|
481
|
+
esac
|
|
468
482
|
if [ "$AUTO_MERGE" = true ]; then
|
|
469
483
|
if [ "$PR_DRAFT" = false ]; then
|
|
470
|
-
# A
|
|
471
|
-
#
|
|
472
|
-
#
|
|
484
|
+
# A ready head already receives synchronize validation. Preserve
|
|
485
|
+
# its live/completed owner instead of cancelling and reallocating
|
|
486
|
+
# CI every time another queued main release run prepares it.
|
|
487
|
+
runs=$(GH_TOKEN="$VALIDATION_TOKEN" gh api \
|
|
488
|
+
"repos/$GITHUB_REPOSITORY/actions/runs" --method GET \
|
|
489
|
+
-f event=pull_request -f head_sha="$PR_HEAD" -f per_page=100)
|
|
490
|
+
validation=$(jq -er --arg head "$PR_HEAD" --arg branch "$PR_BRANCH" \
|
|
491
|
+
--arg repo "$GITHUB_REPOSITORY" '
|
|
492
|
+
def active: .status == "queued" or .status == "in_progress" or
|
|
493
|
+
.status == "waiting" or .status == "pending" or .status == "requested";
|
|
494
|
+
def valid_run:
|
|
495
|
+
type == "object" and
|
|
496
|
+
(.id | type == "number" and . > 0 and floor == .) and
|
|
497
|
+
(.run_attempt | type == "number" and . >= 1 and floor == .) and
|
|
498
|
+
(.head_sha | type == "string" and test("^[a-f0-9]{40}$")) and
|
|
499
|
+
(.head_branch | type == "string" and length > 0) and
|
|
500
|
+
(.event | type == "string" and length > 0) and
|
|
501
|
+
(.repository | type == "object") and
|
|
502
|
+
(.repository.full_name | type == "string" and length > 0) and
|
|
503
|
+
(.path | type == "string" and length > 0) and
|
|
504
|
+
((active and has("conclusion") and .conclusion == null) or
|
|
505
|
+
(.status == "completed" and
|
|
506
|
+
(.conclusion == "success" or .conclusion == "failure" or
|
|
507
|
+
.conclusion == "timed_out" or .conclusion == "action_required" or
|
|
508
|
+
.conclusion == "cancelled" or .conclusion == "skipped" or
|
|
509
|
+
.conclusion == "neutral")));
|
|
510
|
+
if (.workflow_runs | type) != "array" or
|
|
511
|
+
(.total_count | type) != "number" then
|
|
512
|
+
error("Invalid validation owner response")
|
|
513
|
+
elif .total_count != (.workflow_runs | length) then
|
|
514
|
+
error("Incomplete validation owner response")
|
|
515
|
+
elif any(.workflow_runs[]; valid_run | not) then
|
|
516
|
+
error("Invalid validation run identity or state")
|
|
517
|
+
else
|
|
518
|
+
[.workflow_runs[] | (.path | split("@")[0]) as $workflow | select(
|
|
519
|
+
.head_sha == $head and .head_branch == $branch and
|
|
520
|
+
.event == "pull_request" and .repository.full_name == $repo and
|
|
521
|
+
($workflow == ".github/workflows/validation.yml" or
|
|
522
|
+
$workflow == ".github/workflows/validation_self-ci.yml")
|
|
523
|
+
)] | sort_by(.id) | last |
|
|
524
|
+
if . == null then "missing"
|
|
525
|
+
elif active or (.status == "completed" and .conclusion == "success") then "owned"
|
|
526
|
+
elif .status == "completed" and
|
|
527
|
+
(.conclusion == "failure" or .conclusion == "timed_out" or
|
|
528
|
+
.conclusion == "action_required") then "failed"
|
|
529
|
+
elif .status == "completed" and
|
|
530
|
+
(.conclusion == "cancelled" or .conclusion == "skipped" or
|
|
531
|
+
.conclusion == "neutral") then "missing"
|
|
532
|
+
else error("Unconfirmed validation owner state") end
|
|
533
|
+
end' <<< "$runs")
|
|
534
|
+
fresh_state=$(gh pr view "$pr" --repo "$GITHUB_REPOSITORY" \
|
|
535
|
+
--json isDraft,headRefOid,headRefName \
|
|
536
|
+
--jq '[.isDraft, .headRefOid, .headRefName] | @tsv')
|
|
537
|
+
if [ "$fresh_state" != "$pr_state" ]; then
|
|
538
|
+
echo 'Release PR state changed during validation lookup; refusing a stale readiness mutation.' >&2
|
|
539
|
+
exit 1
|
|
540
|
+
fi
|
|
541
|
+
if [ "$validation" = owned ]; then
|
|
542
|
+
echo "Release PR #$pr already has current-head validation; preserving readiness."
|
|
543
|
+
continue
|
|
544
|
+
fi
|
|
545
|
+
if [ "$validation" = failed ]; then
|
|
546
|
+
echo "Current-head validation failed for release PR #$pr; resolve or explicitly retry it before release." >&2
|
|
547
|
+
exit 1
|
|
548
|
+
fi
|
|
549
|
+
# A missing, cancelled or skipped owner still needs the existing
|
|
550
|
+
# explicit ready_for_review transition with the validated token.
|
|
473
551
|
gh pr ready --undo "$pr" --repo "$GITHUB_REPOSITORY"
|
|
474
552
|
fi
|
|
475
553
|
gh pr ready "$pr" --repo "$GITHUB_REPOSITORY"
|
|
@@ -227,17 +227,6 @@ jobs:
|
|
|
227
227
|
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
228
228
|
BEFORE_SHA: ${{ github.event.before }}
|
|
229
229
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci should_run eval >> "$GITHUB_OUTPUT"
|
|
230
|
-
# --- Lint lane (mirror of ci.yml lint) ---
|
|
231
|
-
- name: Detect lint
|
|
232
|
-
id: lint_applicability
|
|
233
|
-
if: ${{ !cancelled() }}
|
|
234
|
-
env:
|
|
235
|
-
EVENT_NAME: ${{ github.event_name }}
|
|
236
|
-
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
237
|
-
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
238
|
-
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
239
|
-
BEFORE_SHA: ${{ github.event.before }}
|
|
240
|
-
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci should_run lint >> "$GITHUB_OUTPUT"
|
|
241
230
|
# --- Integration lane (mirror of test.yml integration; audit tier only) ---
|
|
242
231
|
- name: Detect integration
|
|
243
232
|
id: integration_applicability
|
|
@@ -314,13 +303,6 @@ jobs:
|
|
|
314
303
|
install: 'true'
|
|
315
304
|
cache-build: 'true'
|
|
316
305
|
task: eval
|
|
317
|
-
- name: Setup lint
|
|
318
|
-
if: ${{ !cancelled() && steps.lint_applicability.outputs.applicable == 'true' && steps.lint_applicability.outputs.affected != 'false' }}
|
|
319
|
-
uses: ./.github/actions/setup
|
|
320
|
-
with:
|
|
321
|
-
install: 'true'
|
|
322
|
-
cache-lint: 'true'
|
|
323
|
-
task: lint
|
|
324
306
|
- name: Setup integration
|
|
325
307
|
if: ${{ !cancelled() && inputs.mode == 'audit' && steps.integration_applicability.outputs.applicable == 'true' && steps.integration_applicability.outputs.affected != 'false' }}
|
|
326
308
|
uses: ./.github/actions/setup
|
|
@@ -365,11 +347,6 @@ jobs:
|
|
|
365
347
|
id: eval_execute
|
|
366
348
|
if: ${{ !cancelled() && inputs.mode == 'audit' && steps.eval_applicability.outputs.applicable == 'true' && steps.eval_applicability.outputs.affected != 'false' }}
|
|
367
349
|
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci eval
|
|
368
|
-
# --- Lint execute ---
|
|
369
|
-
- name: Lint
|
|
370
|
-
id: lint_execute
|
|
371
|
-
if: ${{ !cancelled() && steps.lint_applicability.outputs.applicable == 'true' && steps.lint_applicability.outputs.affected != 'false' }}
|
|
372
|
-
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci lint
|
|
373
350
|
# --- Integration execute ---
|
|
374
351
|
- name: Integration
|
|
375
352
|
id: integration_execute
|
|
@@ -477,19 +454,6 @@ jobs:
|
|
|
477
454
|
eval-results/summary.json
|
|
478
455
|
if-no-files-found: error
|
|
479
456
|
retention-days: 14
|
|
480
|
-
- name: Retain lint task receipt
|
|
481
|
-
if: >-
|
|
482
|
-
always() &&
|
|
483
|
-
(steps.lint_execute.outcome == 'success' || steps.lint_execute.outcome == 'failure' ||
|
|
484
|
-
steps.lint_applicability.outputs.applicable == 'false' || steps.lint_applicability.outputs.affected == 'false') &&
|
|
485
|
-
hashFiles('.code-foundry/results/lint.json') != ''
|
|
486
|
-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
487
|
-
with:
|
|
488
|
-
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-lint
|
|
489
|
-
path: .code-foundry/results/lint.json
|
|
490
|
-
include-hidden-files: true
|
|
491
|
-
if-no-files-found: error
|
|
492
|
-
retention-days: 14
|
|
493
457
|
- name: Retain integration task receipt
|
|
494
458
|
if: >-
|
|
495
459
|
always() &&
|
|
@@ -505,6 +469,67 @@ jobs:
|
|
|
505
469
|
if-no-files-found: error
|
|
506
470
|
retention-days: 14
|
|
507
471
|
|
|
472
|
+
lint:
|
|
473
|
+
name: Lint
|
|
474
|
+
if: vars.CI_BILLING_PAUSED != 'true'
|
|
475
|
+
runs-on: ${{ inputs.ci-runner }}
|
|
476
|
+
timeout-minutes: 30
|
|
477
|
+
steps:
|
|
478
|
+
- name: Checkout
|
|
479
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
480
|
+
with:
|
|
481
|
+
persist-credentials: false
|
|
482
|
+
- name: Runtime
|
|
483
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
484
|
+
with:
|
|
485
|
+
persist-credentials: false
|
|
486
|
+
repository: ${{ inputs.runtime-repository }}
|
|
487
|
+
ref: ${{ inputs.runtime-ref }}
|
|
488
|
+
path: .github/.code-foundry
|
|
489
|
+
sparse-checkout: |
|
|
490
|
+
.github/actions
|
|
491
|
+
src/lib
|
|
492
|
+
src/runtime-core.mjs
|
|
493
|
+
src/runtime.mjs
|
|
494
|
+
- name: Install runtime
|
|
495
|
+
run: |
|
|
496
|
+
mkdir -p .github/actions
|
|
497
|
+
mv .github/.code-foundry "$RUNNER_TEMP/code-foundry"
|
|
498
|
+
cp -R "$RUNNER_TEMP/code-foundry/.github/actions/." .github/actions/
|
|
499
|
+
- name: Detect
|
|
500
|
+
id: applicability
|
|
501
|
+
env:
|
|
502
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
503
|
+
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
504
|
+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
505
|
+
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
506
|
+
BEFORE_SHA: ${{ github.event.before }}
|
|
507
|
+
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci should_run lint >> "$GITHUB_OUTPUT"
|
|
508
|
+
- name: Setup
|
|
509
|
+
if: steps.applicability.outputs.applicable == 'true' && steps.applicability.outputs.affected != 'false'
|
|
510
|
+
uses: ./.github/actions/setup
|
|
511
|
+
with:
|
|
512
|
+
install: 'true'
|
|
513
|
+
cache-lint: 'true'
|
|
514
|
+
task: lint
|
|
515
|
+
- name: Lint
|
|
516
|
+
id: execute
|
|
517
|
+
if: steps.applicability.outputs.applicable == 'true' && steps.applicability.outputs.affected != 'false'
|
|
518
|
+
run: node "$RUNNER_TEMP/code-foundry/src/runtime.mjs" ci lint
|
|
519
|
+
- name: Retain task receipt
|
|
520
|
+
if: >-
|
|
521
|
+
always() &&
|
|
522
|
+
(steps.execute.outcome == 'success' || steps.execute.outcome == 'failure' ||
|
|
523
|
+
steps.applicability.outputs.applicable == 'false' || steps.applicability.outputs.affected == 'false') &&
|
|
524
|
+
hashFiles('.code-foundry/results/lint.json') != ''
|
|
525
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
|
|
526
|
+
with:
|
|
527
|
+
name: ${{ inputs.artifact-prefix }}-${{ github.run_id }}-${{ github.run_attempt }}-lint
|
|
528
|
+
path: .code-foundry/results/lint.json
|
|
529
|
+
include-hidden-files: true
|
|
530
|
+
if-no-files-found: error
|
|
531
|
+
retention-days: 14
|
|
532
|
+
|
|
508
533
|
type-check:
|
|
509
534
|
name: Type-Check
|
|
510
535
|
if: vars.CI_BILLING_PAUSED != 'true'
|
|
@@ -1074,7 +1099,7 @@ jobs:
|
|
|
1074
1099
|
# evaluateValidationGate's mode truth table is reused unchanged.
|
|
1075
1100
|
gate:
|
|
1076
1101
|
name: Gate
|
|
1077
|
-
needs: [fast-lanes, type-check, unit, e2e, audit, codeql]
|
|
1102
|
+
needs: [fast-lanes, lint, type-check, unit, e2e, audit, codeql]
|
|
1078
1103
|
if: vars.CI_BILLING_PAUSED != 'true' && always()
|
|
1079
1104
|
runs-on: ubuntu-slim
|
|
1080
1105
|
timeout-minutes: 10
|
|
@@ -1082,7 +1107,7 @@ jobs:
|
|
|
1082
1107
|
FOUNDRY_MODE: ${{ inputs.mode }}
|
|
1083
1108
|
# CI category: lint and type-check always run; the merged fast-lanes job
|
|
1084
1109
|
# carries the format and build lanes.
|
|
1085
|
-
FOUNDRY_CI: ${{ (needs.type-check.result != 'success' && needs.type-check.result) || (needs.fast-lanes.result != 'success' && needs.fast-lanes.result) || 'success' }}
|
|
1110
|
+
FOUNDRY_CI: ${{ (needs.lint.result != 'success' && needs.lint.result) || (needs.type-check.result != 'success' && needs.type-check.result) || (needs.fast-lanes.result != 'success' && needs.fast-lanes.result) || 'success' }}
|
|
1086
1111
|
# Test category: unit always runs and must succeed; integration and e2e
|
|
1087
1112
|
# are audit-tier jobs whose expected skip on fast/release is not a
|
|
1088
1113
|
# failure; the merged fast-lanes job carries performance, smoke, and
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.46.4](https://github.com/0xPlayerOne/code-foundry/compare/v1.46.3...v1.46.4) (2026-10-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **release:** preserve current-head validation ownership ([#737](https://github.com/0xPlayerOne/code-foundry/issues/737)) ([aa8bfcf](https://github.com/0xPlayerOne/code-foundry/commit/aa8bfcfbfcd3c213d15ba9e1fbfb5c65ab8ddb6b))
|
|
9
|
+
|
|
10
|
+
## [1.46.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.46.2...v1.46.3) (2026-10-07)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **billing:** keep lint standalone; join fast-lane receipts in parallel ([#744](https://github.com/0xPlayerOne/code-foundry/issues/744)) ([574690b](https://github.com/0xPlayerOne/code-foundry/commit/574690b707abffe9350ccc67589540afb82323ee))
|
|
16
|
+
|
|
3
17
|
## [1.46.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.46.1...v1.46.2) (2026-10-07)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -126,23 +140,9 @@
|
|
|
126
140
|
|
|
127
141
|
* **pre-commit:** make the commit gate change-aware ([#702](https://github.com/0xPlayerOne/code-foundry/issues/702)) ([f5bca91](https://github.com/0xPlayerOne/code-foundry/commit/f5bca91c177b30b99f5d62287255d191617b4788))
|
|
128
142
|
|
|
129
|
-
## [1.43.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.43.1...v1.43.2) (2026-10-03)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### Bug Fixes
|
|
133
|
-
|
|
134
|
-
* require candidate asset qualification before stable promotion ([#705](https://github.com/0xPlayerOne/code-foundry/issues/705)) ([f42e4f5](https://github.com/0xPlayerOne/code-foundry/commit/f42e4f5fb063ea22fa25791a13c75c90d41bf17a))
|
|
135
|
-
|
|
136
|
-
## [1.43.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.43.0...v1.43.1) (2026-10-03)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
### Bug Fixes
|
|
140
|
-
|
|
141
|
-
* **release:** make batched publication and stable promotion reliable ([#703](https://github.com/0xPlayerOne/code-foundry/issues/703)) ([271731d](https://github.com/0xPlayerOne/code-foundry/commit/271731d8fac3bff42a99b3d5f4a774eccc0a7788))
|
|
142
|
-
|
|
143
143
|
<!-- code-foundry: packaged changelog -->
|
|
144
144
|
|
|
145
145
|
---
|
|
146
146
|
|
|
147
|
-
This packaged changelog lists the 20 most recent releases;
|
|
147
|
+
This packaged changelog lists the 20 most recent releases; 239 older releases are omitted to keep the package small.
|
|
148
148
|
See the [full changelog](https://github.com/0xPlayerOne/code-foundry/blob/main/CHANGELOG.md) for the complete history.
|
package/docs/WORKFLOWS.md
CHANGED
|
@@ -82,6 +82,23 @@ to run generated gates for drafts. Cloudflare callers use their equivalent
|
|
|
82
82
|
`draft-protection: false` input. These opt-outs do not remove Draft Guard or
|
|
83
83
|
draft-PR automation; they only allow the protected gates to run for drafts.
|
|
84
84
|
|
|
85
|
+
Generated release PR preparation preserves a ready head when the canonical
|
|
86
|
+
generated `.github/workflows/validation.yml` (or Code Foundry
|
|
87
|
+
`validation_self-ci.yml`) pull-request run for that exact head, branch, and
|
|
88
|
+
repository is the newest matching run and is active or successful. API workflow
|
|
89
|
+
paths may include a Git ref suffix; ownership compares the canonical filename.
|
|
90
|
+
The workflow credential reads Actions ownership; the validated automation credential still performs readiness writes.
|
|
91
|
+
A newer failed qualification blocks release preparation until it is resolved or
|
|
92
|
+
explicitly retried. Missing, cancelled, or skipped validation still receives the
|
|
93
|
+
explicit draft/ready transition; a new draft receives its first readiness event.
|
|
94
|
+
An unavailable or malformed ownership response, an incomplete Actions page, or a
|
|
95
|
+
PR state change during the lookup fails before mutation. An older live run cannot
|
|
96
|
+
mask a newer failed qualification. Invalid automation credentials retain the
|
|
97
|
+
manual draft fallback. This changes readiness only: release creation, qualification,
|
|
98
|
+
tag publication, and branch-policy-gated exact-head merging still run through
|
|
99
|
+
the existing release pipeline. Consumers must adopt the fixed reusable workflow
|
|
100
|
+
pin before they receive this behavior; older pins still reset ready heads.
|
|
101
|
+
|
|
85
102
|
## Billing pause
|
|
86
103
|
|
|
87
104
|
Code Foundry consumers can stop all generated jobs from allocating GitHub-hosted
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-foundry",
|
|
3
|
-
"version": "1.46.
|
|
3
|
+
"version": "1.46.4",
|
|
4
4
|
"description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
|
|
5
5
|
"homepage": "https://github.com/0xPlayerOne/code-foundry#readme",
|
|
6
6
|
"bugs": {
|