code-foundry 1.46.6 → 1.47.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.47.1](https://github.com/0xPlayerOne/code-foundry/compare/v1.47.0...v1.47.1) (2026-10-08)
4
+
5
+
6
+ ### Tests
7
+
8
+ * **doctor:** pin the machine-hook shadow warning ([#753](https://github.com/0xPlayerOne/code-foundry/issues/753)) ([f634269](https://github.com/0xPlayerOne/code-foundry/commit/f634269424580d8d34323319288747587b6d6ae3))
9
+
10
+ ## [1.47.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.46.6...v1.47.0) (2026-10-08)
11
+
12
+
13
+ ### Features
14
+
15
+ * **release:** configurable dev channel on top of batched releases ([#749](https://github.com/0xPlayerOne/code-foundry/issues/749)) ([8fd8556](https://github.com/0xPlayerOne/code-foundry/commit/8fd85567a1a89735ab0db589649741253fbbeb21)), closes [#699](https://github.com/0xPlayerOne/code-foundry/issues/699)
16
+
3
17
  ## [1.46.6](https://github.com/0xPlayerOne/code-foundry/compare/v1.46.5...v1.46.6) (2026-10-08)
4
18
 
5
19
 
@@ -126,23 +140,9 @@
126
140
 
127
141
  * **ci:** declare the e2e shard inputs on the no-codeql orchestrator ([#714](https://github.com/0xPlayerOne/code-foundry/issues/714)) ([7500208](https://github.com/0xPlayerOne/code-foundry/commit/7500208f3b25dfe55c41ea45b382e021cf804012))
128
142
 
129
- ## [1.44.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.44.2...v1.44.3) (2026-10-03)
130
-
131
-
132
- ### Tests
133
-
134
- * **cli:** assert expected doctor warnings ([#712](https://github.com/0xPlayerOne/code-foundry/issues/712)) ([d036bee](https://github.com/0xPlayerOne/code-foundry/commit/d036beee79a9b200315cdbee5509fc3c2a6c4a04))
135
-
136
- ## [1.44.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.44.1...v1.44.2) (2026-10-03)
137
-
138
-
139
- ### Bug Fixes
140
-
141
- * **ci:** cancel only superseded pull-request runs in managed workflows ([#710](https://github.com/0xPlayerOne/code-foundry/issues/710)) ([f5fead6](https://github.com/0xPlayerOne/code-foundry/commit/f5fead677c032d253f2e1a2a256da4f207ddfa2a))
142
-
143
143
  <!-- code-foundry: packaged changelog -->
144
144
 
145
145
  ---
146
146
 
147
- This packaged changelog lists the 20 most recent releases; 241 older releases are omitted to keep the package small.
147
+ This packaged changelog lists the 20 most recent releases; 243 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.
@@ -141,6 +141,8 @@ See [Merge queue validation](merge-queues.md) before enabling it.
141
141
  | `release_batching_prerelease` | `true`, `false` | Defaults to `true` with a schedule. Sync enables `draft` and `force-tag-creation` in Release Please; the producer publishes the draft with the selected visibility. `false` keeps scheduled releases stable-visible. |
142
142
  | `release_batching_soak_hours` | `1`–`336` | Generate a daily promoter that promotes the newest prerelease when the oldest candidate since the last stable has soaked this many hours, stitching batch notes. Requires a batching schedule and prerelease publication. Set `STABLE_PROMOTION_HELD=true` in repository Actions variables to hold scheduled and manual promotion; `force` overrides the hold or soak, and `version` selects a plain semantic version. |
143
143
  | `release_batching_qualification_workflow` | workflow filename (for example `release-assets.yml`) | Require the latest release-event run of this workflow for the candidate tag’s exact source commit to succeed before stable promotion. Requires a batching soak. Running, missing, failed, or cancelled runs hold promotion, including named and forced promotions; `force` only bypasses hold and soak. The promoter receives read-only Actions access. |
144
+ | `release_batching_dev_channel` | `true`, `false` | Publish a dev channel: every push to main tags a `v<stable>-dev.<run>` pre-release on top of the newest stable and dispatches the configured asset lane. Dev builds are never promoted to stable, auto builds pause with `CI_BILLING_PAUSED`, and manual dispatches accept a `version` input for repairs. Defaults to `false`. |
145
+ | `release_batching_dev_workflow` | workflow filename | The `workflow_dispatch` asset lane the dev channel dispatches with a `version` input; the lane and its version injection stay app-provided because packaging differs per app. Required by `release_batching_dev_channel`. |
144
146
 
145
147
  Supported task capabilities are `format`, `lint`, `type_check`, `build`, `unit`,
146
148
  `integration`, `e2e`, `smoke`, `eval`, and `performance`. `coverage` is a policy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-foundry",
3
- "version": "1.46.6",
3
+ "version": "1.47.1",
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": {
@@ -308,6 +308,13 @@ function synchronize(options) {
308
308
  const promoterPath = join(target, '.github/workflows/promote-stable.yml')
309
309
  if (promoter) writeOrReport(promoterPath, promoter, dryRun)
310
310
  else if (existsSync(promoterPath) && !dryRun) rmSync(promoterPath)
311
+ // The dev channel rides along the same way: rendered from
312
+ // release_batching_dev_channel, removed when the key is gone so a
313
+ // disabled channel stops tagging dev builds on every push.
314
+ const devChannel = renderDevChannel(config)
315
+ const devChannelPath = join(target, '.github/workflows/dev-channel.yml')
316
+ if (devChannel) writeOrReport(devChannelPath, devChannel, dryRun)
317
+ else if (existsSync(devChannelPath) && !dryRun) rmSync(devChannelPath)
311
318
  }
312
319
  if (file === '.github/dependabot.yml') {
313
320
  content = Buffer.from(renderDependabot(content.toString('utf8'), config, languages))
@@ -936,6 +943,29 @@ function validateReleaseBatchingConfig(config) {
936
943
  )
937
944
  }
938
945
  }
946
+ if (
947
+ config.release_batching_dev_channel &&
948
+ !['true', 'false'].includes(config.release_batching_dev_channel)
949
+ ) {
950
+ throw new Error(
951
+ `Unsupported release_batching_dev_channel: ${config.release_batching_dev_channel}; use true or false.`
952
+ )
953
+ }
954
+ if (configured(config.release_batching_dev_channel, 'false') === 'true') {
955
+ if (
956
+ !config.release_batching_dev_workflow ||
957
+ !/^[A-Za-z0-9_.-]+\.ya?ml$/.test(config.release_batching_dev_workflow)
958
+ ) {
959
+ throw new Error(
960
+ 'Unsupported release_batching_dev_workflow: use the filename of the workflow_dispatch asset lane the dev build should dispatch.'
961
+ )
962
+ }
963
+ }
964
+ if (config.release_batching_dev_workflow && config.release_batching_dev_channel !== 'true') {
965
+ throw new Error(
966
+ 'Unsupported release_batching_dev_workflow: requires release_batching_dev_channel: true.'
967
+ )
968
+ }
939
969
  }
940
970
 
941
971
  /** Render the batch-soak promoter for a repository, or null when the
@@ -953,6 +983,102 @@ export function renderPromoteStable(config) {
953
983
  )
954
984
  }
955
985
 
986
+ /**
987
+ * The dev channel for repositories with `release_batching_dev_channel`. Every
988
+ * push to main tags a `v<stable>-dev.<run>` pre-release on top of the newest
989
+ * stable and dispatches the repository's own asset lane with a `version`
990
+ * input; the oldest dev builds are pruned so the channel stays bounded. Dev
991
+ * builds are never promoted: the batch-soak promoter's batch selector already
992
+ * excludes `-dev.` tags, so the channel lives beside stable forever.
993
+ * `__DEV_WORKFLOW__` is replaced with the configured asset lane at render
994
+ * time; the lane stays app-provided because packaging differs per app.
995
+ */
996
+ const DEV_CHANNEL_TEMPLATE = `# Generated by Code Foundry sync from release_batching_dev_channel; rerun sync after changing or removing the key.
997
+
998
+ name: Publish dev channel
999
+
1000
+ on:
1001
+ push:
1002
+ branches: [main]
1003
+ workflow_dispatch:
1004
+ inputs:
1005
+ version:
1006
+ description: Publish this dev version now (for example v0.75.1-dev.42). Leave empty to derive it from the newest stable.
1007
+ required: false
1008
+ type: string
1009
+
1010
+ permissions:
1011
+ contents: write
1012
+ actions: write
1013
+
1014
+ concurrency:
1015
+ group: dev-channel
1016
+ cancel-in-progress: false
1017
+
1018
+ jobs:
1019
+ publish:
1020
+ name: Tag the dev build and dispatch the asset lane
1021
+ # Auto dev builds pause with the shared billing pause; the manual dispatch
1022
+ # (the repair path) still runs.
1023
+ if: github.event_name == 'workflow_dispatch' || vars.CI_BILLING_PAUSED != 'true'
1024
+ runs-on: ubuntu-latest
1025
+ timeout-minutes: 10
1026
+ steps:
1027
+ - name: Publish the dev prerelease and dispatch the asset lane
1028
+ env:
1029
+ GH_TOKEN: \${{ github.token }}
1030
+ DEV_WORKFLOW: '__DEV_WORKFLOW__'
1031
+ REQUESTED: \${{ inputs.version }}
1032
+ run: |
1033
+ set -euo pipefail
1034
+ repo="$GITHUB_REPOSITORY"
1035
+ if [ -n "$REQUESTED" ]; then
1036
+ tag="$REQUESTED"
1037
+ else
1038
+ stable="$(gh api --paginate --slurp "repos/$repo/releases?per_page=100" | jq -r '
1039
+ [ .[] | select(.draft == false and .prerelease == false)
1040
+ | select(.tag_name | test("^v[0-9]+[.][0-9]+[.][0-9]+$")) ]
1041
+ | sort_by(.published_at) | .[-1].tag_name // empty')"
1042
+ if [ -z "$stable" ]; then
1043
+ echo "No stable release exists yet; a dev channel needs a stable baseline." | tee "$GITHUB_STEP_SUMMARY"
1044
+ exit 0
1045
+ fi
1046
+ tag="\${stable}-dev.\${GITHUB_RUN_NUMBER}"
1047
+ fi
1048
+ if gh release view "$tag" --repo "$repo" >/dev/null 2>&1; then
1049
+ echo "$tag already exists; nothing to do." | tee "$GITHUB_STEP_SUMMARY"
1050
+ exit 0
1051
+ fi
1052
+ gh release create "$tag" --repo "$repo" --prerelease --target "$GITHUB_SHA" \
1053
+ --title "$tag" --notes "Dev build of \${GITHUB_SHA::7}; a dev-channel build is never promoted to stable."
1054
+ gh workflow run "$DEV_WORKFLOW" --repo "$repo" --ref main -f version="$tag"
1055
+ echo "Dispatched $DEV_WORKFLOW for $tag." | tee "$GITHUB_STEP_SUMMARY"
1056
+
1057
+ # Keep the channel bounded: the newest dev builds stay, the rest go.
1058
+ gh api --paginate --slurp "repos/$repo/releases?per_page=100" | jq -r '
1059
+ [ .[] | select(.draft == false and .prerelease == true)
1060
+ | select(.tag_name | test("^v[0-9]+[.][0-9]+[.][0-9]+-dev[.][0-9]+$")) ]
1061
+ | sort_by(.created_at) | reverse | .[3:] | .[].tag_name' | while read -r stale; do
1062
+ echo "Pruning $stale."
1063
+ gh release delete "$stale" --repo "$repo" --yes --cleanup-tag
1064
+ done
1065
+ `
1066
+
1067
+ /**
1068
+ * Render the dev channel workflow for a repository, or null when the
1069
+ * repository has not opted in.
1070
+ *
1071
+ * @param {Record<string, string>} config
1072
+ * @returns {string | null}
1073
+ */
1074
+ export function renderDevChannel(config) {
1075
+ if (configured(config.release_batching_dev_channel, 'false') !== 'true') return null
1076
+ return DEV_CHANNEL_TEMPLATE.replaceAll(
1077
+ '__DEV_WORKFLOW__',
1078
+ config.release_batching_dev_workflow ?? ''
1079
+ )
1080
+ }
1081
+
956
1082
  /**
957
1083
  * @param {string} content
958
1084
  * @param {Record<string,string>} config