code-foundry 0.17.0 → 0.19.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.
@@ -11,7 +11,7 @@ force=false
11
11
  languages="${REPO_FOUNDRY_LANGUAGES:-auto}"
12
12
  features="${REPO_FOUNDRY_FEATURES:-all}"
13
13
  package_manager="${REPO_FOUNDRY_PACKAGE_MANAGER:-auto}"
14
- runtime_repository="${REPO_FOUNDRY_RUNTIME_REPOSITORY:-0xPlayerOne/code-foundry}"
14
+ runtime_repository="${REPO_FOUNDRY_RUNTIME_REPOSITORY:-}"
15
15
  bootstrap=true
16
16
  release_type="${REPO_FOUNDRY_RELEASE_TYPE:-auto}"
17
17
  npm_publish="${REPO_FOUNDRY_NPM_PUBLISH:-false}"
@@ -38,7 +38,7 @@ Options:
38
38
  --features LIST all or comma-separated optional features:
39
39
  ci,codeql,security,test,draft-pr,release-pr,release,dependabot
40
40
  --package-manager NAME auto, bun, pnpm, yarn, or npm
41
- --runtime-repository OWNER/REPO Reusable workflow runtime repository
41
+ --runtime-repository OWNER/REPO Reusable workflow runtime repository (auto from source)
42
42
  --release-type NAME auto, node, python, rust, or simple
43
43
  --license NAME agpl-3.0-or-later, mit, preserve, or none
44
44
  --license-file PATH Use an exact custom license file
@@ -111,9 +111,9 @@ sync_args=(
111
111
  --languages "$languages"
112
112
  --features "$features"
113
113
  --package-manager "$package_manager"
114
- --runtime-repository "$runtime_repository"
115
114
  --license "$license"
116
115
  )
116
+ if [ -n "$runtime_repository" ]; then sync_args+=(--runtime-repository "$runtime_repository"); fi
117
117
  if [ -n "$license_file" ]; then sync_args+=(--license-file "$license_file"); fi
118
118
  if [ "$dry_run" = true ]; then sync_args+=(--check); else sync_args+=(--apply); fi
119
119
  if [ "$prune" = true ]; then sync_args+=(--prune); fi
@@ -284,6 +284,7 @@ files=("${filtered_files[@]}")
284
284
  standard_workflow() {
285
285
  case "$1" in
286
286
  ci.yml|codeql.yml|draft-pr.yml|release-pr.yml|release.yml|security.yml|test.yml) return 0 ;;
287
+ reusable-draft-pr.yml|reusable-release-pr.yml) return 0 ;;
287
288
  *) return 1 ;;
288
289
  esac
289
290
  }
@@ -370,16 +371,22 @@ for file in "${files[@]}"; do
370
371
  done
371
372
 
372
373
  # Reusable workflow callers must use a literal repository/ref. Render the
373
- # selected runtime repository while leaving custom workflows untouched.
374
+ # selected runtime repository while leaving custom workflows untouched. Read
375
+ # the source's configured runtime first so forks remain self-contained.
376
+ source_runtime_repository=""
377
+ if [ -f "$template_root/.github/template.yml" ]; then
378
+ source_runtime_repository="$(awk -F': ' '/^runtime_repository:/ {print $2; exit}' "$template_root/.github/template.yml")"
379
+ fi
380
+ [ -n "$source_runtime_repository" ] || source_runtime_repository="0xPlayerOne/code-foundry"
374
381
  for file in .github/workflows/ci.yml .github/workflows/test.yml .github/workflows/security.yml .github/workflows/codeql.yml; do
375
382
  [ -f "$file" ] || continue
376
- if grep -q '0xPlayerOne/code-foundry' "$file"; then
383
+ if grep -qF "$source_runtime_repository" "$file" && [ "$source_runtime_repository" != "$runtime_repository" ]; then
377
384
  changed=$((changed + 1))
378
385
  if [ "$mode" = "check" ]; then
379
386
  printf 'Would render runtime repository in %s\n' "$file"
380
387
  else
381
388
  rendered_workflow="$(mktemp)"
382
- sed "s#0xPlayerOne/code-foundry#$runtime_repository#g" "$file" > "$rendered_workflow"
389
+ sed "s#${source_runtime_repository}#${runtime_repository}#g" "$file" > "$rendered_workflow"
383
390
  mv "$rendered_workflow" "$file"
384
391
  printf 'Rendered runtime repository in %s\n' "$file"
385
392
  fi
@@ -0,0 +1,51 @@
1
+ name: Code Foundry Draft PR
2
+
3
+ on:
4
+ workflow_call:
5
+
6
+ permissions:
7
+ contents: read
8
+ pull-requests: write
9
+
10
+ env:
11
+ GH_TOKEN: ${{ github.token }}
12
+
13
+ jobs:
14
+ create-draft-pr:
15
+ name: Create
16
+ runs-on: ubuntu-slim
17
+ timeout-minutes: 10
18
+ concurrency:
19
+ group: ${{ github.workflow }}-${{ github.ref }}
20
+ cancel-in-progress: true
21
+ steps:
22
+ - name: Check
23
+ id: check-pr
24
+ run: |
25
+ BRANCH="${{ github.ref_name }}"
26
+ EXISTING=$(gh pr list --limit 1 --base staging --head "$BRANCH" --state open --json number,title 2>/dev/null | jq 'length')
27
+ echo "existing=$EXISTING" >> "$GITHUB_OUTPUT"
28
+
29
+ - name: Create
30
+ if: steps.check-pr.outputs.existing == '0'
31
+ run: |
32
+ BRANCH="${{ github.ref_name }}"
33
+ gh pr create \
34
+ --base staging \
35
+ --head "$BRANCH" \
36
+ --draft \
37
+ --title "[WIP] $BRANCH" \
38
+ --body "Draft PR created automatically by CI.
39
+
40
+ This PR is a draft and should not be merged until CI passes and a reviewer (or the Daily Review agent) marks it ready.
41
+
42
+ **CI Status:** Pending — this workflow triggered on push to \`$BRANCH\`."
43
+
44
+ - name: Mark ready
45
+ if: steps.check-pr.outputs.existing == '0'
46
+ run: |
47
+ BRANCH="${{ github.ref_name }}"
48
+ PR_NUMBER=$(gh pr list --limit 1 --base staging --head "$BRANCH" --state open --json number 2>/dev/null | jq '.[0].number')
49
+ if [ -n "$PR_NUMBER" ]; then
50
+ gh pr ready "$PR_NUMBER"
51
+ fi
@@ -0,0 +1,86 @@
1
+ name: Code Foundry Release PR
2
+
3
+ on:
4
+ workflow_call:
5
+
6
+ permissions:
7
+ contents: read
8
+ pull-requests: write
9
+
10
+ env:
11
+ GH_TOKEN: ${{ github.token }}
12
+
13
+ jobs:
14
+ create-release-pr:
15
+ name: Create
16
+ runs-on: ubuntu-slim
17
+ timeout-minutes: 10
18
+ concurrency:
19
+ group: ${{ github.workflow }}-${{ github.ref }}
20
+ cancel-in-progress: true
21
+ steps:
22
+ - name: Check
23
+ id: check-pr
24
+ run: |
25
+ set -euo pipefail
26
+ EXISTING=$(gh pr list \
27
+ --repo "$GITHUB_REPOSITORY" \
28
+ --limit 1 \
29
+ --base main \
30
+ --head staging \
31
+ --state open \
32
+ --json number | jq 'length')
33
+ echo "existing=$EXISTING" >> "$GITHUB_OUTPUT"
34
+
35
+ - name: Create
36
+ if: steps.check-pr.outputs.existing == '0'
37
+ run: |
38
+ DATE=$(date +%Y-%m-%d)
39
+ BODY_FILE="$RUNNER_TEMP/release-pr.md"
40
+ COMPARE_URL="repos/${GITHUB_REPOSITORY}/compare/main...staging"
41
+ COMPARE_FILE="$RUNNER_TEMP/release-compare.json"
42
+ gh api "$COMPARE_URL?per_page=100" > "$COMPARE_FILE"
43
+ TOTAL_COMMITS=$(jq -r '.total_commits // 0' "$COMPARE_FILE")
44
+ if [ "$TOTAL_COMMITS" -gt 100 ]; then
45
+ LAST_PAGE=$(( (TOTAL_COMMITS + 99) / 100 ))
46
+ gh api "$COMPARE_URL?per_page=100&page=$LAST_PAGE" > "$COMPARE_FILE"
47
+ fi
48
+ COMMITS=$(jq -r '.commits[] | "- \(.commit.message | split("\\n")[0]) (\(.sha[0:7]))"' "$COMPARE_FILE")
49
+ if [ -z "$COMMITS" ]; then
50
+ COMMITS='- No commits found since main.'
51
+ fi
52
+
53
+ if [ "$TOTAL_COMMITS" -eq 1 ]; then
54
+ CHANGE_LABEL=change
55
+ else
56
+ CHANGE_LABEL=changes
57
+ fi
58
+
59
+ cat > "$BODY_FILE" <<EOF
60
+ ## Environment promotion
61
+
62
+ Promote the validated \`staging\` environment into \`main\`.
63
+ This is a deployment-promotion PR, not the version-release PR.
64
+
65
+ - **Source:** \`staging\`
66
+ - **Target:** \`main\`
67
+ - **Changes:** $TOTAL_COMMITS $CHANGE_LABEL
68
+ - **Created:** $DATE
69
+
70
+ ### Commit summary
71
+
72
+ $COMMITS
73
+
74
+ ### Validation
75
+
76
+ - Required CI, test, security, and CodeQL checks must pass before merge.
77
+ - Merge with **rebase** to preserve the linear Conventional Commit history.
78
+ - After merge, Release Please opens a separate versioned release PR with the changelog.
79
+ EOF
80
+
81
+ gh pr create \
82
+ --repo "$GITHUB_REPOSITORY" \
83
+ --base main \
84
+ --head staging \
85
+ --title "Promote staging -> main ($DATE)" \
86
+ --body-file "$BODY_FILE"
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.19.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.18.0...v0.19.0) (2026-07-28)
4
+
5
+
6
+ ### Features
7
+
8
+ * **workflows:** add reusable promotion runtimes ([ac113a9](https://github.com/0xPlayerOne/code-foundry/commit/ac113a902700957cbf26d4a47b4e7828b157dd13))
9
+
10
+ ## [0.18.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.17.0...v0.18.0) (2026-07-28)
11
+
12
+
13
+ ### Features
14
+
15
+ * **init:** infer runtime from source template ([81a545b](https://github.com/0xPlayerOne/code-foundry/commit/81a545bc6a2d89c31be51a711e477c4e4ceca7dd))
16
+
3
17
  ## [0.17.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.16.0...v0.17.0) (2026-07-28)
4
18
 
5
19
 
package/README.md CHANGED
@@ -36,11 +36,12 @@ npx code-foundry init --license agpl-3.0-or-later
36
36
  npx code-foundry init --license-file ./legal/LICENSE.txt
37
37
  ```
38
38
 
39
- Reusable workflow callers default to this package's public runtime. Use
40
- `--runtime-repository OWNER/REPO` when initializing from an organization fork;
41
- the selected repository is saved in `.github/template.yml` and rendered into
42
- the small `ci.yml` and `test.yml` wrappers automatically. The equivalent
43
- environment variable is `REPO_FOUNDRY_RUNTIME_REPOSITORY`.
39
+ Reusable workflow callers use the source template's runtime automatically. For
40
+ example, initializing from `NiftyLeague/template-repo` selects that fork's
41
+ runtime without extra flags. Use `--runtime-repository OWNER/REPO` (or
42
+ `REPO_FOUNDRY_RUNTIME_REPOSITORY`) only when you want to override it. The
43
+ selected repository is saved in `.github/template.yml` and rendered into the
44
+ small CI, Test, Security, and CodeQL wrappers automatically.
44
45
 
45
46
  Normal synchronization imports only the local hook, agent-facing command,
46
47
  profile/release, initializer, doctor, ownership, and protection entrypoints.
@@ -60,6 +61,11 @@ main-branch wrapper while the shared runtime handles Release Please, GitHub
60
61
  release metadata, optional npm publication, provenance, and token/trusted
61
62
  publishing fallback.
62
63
 
64
+ The promotion runtimes for Draft PR and Release PR are maintained in the same
65
+ versioned repository. They are introduced before their consumer wrappers move
66
+ to a release tag, avoiding a bootstrap cycle when a new runtime version is
67
+ being published.
68
+
63
69
  Initialization defaults to AGPL for new repositories. Synchronization defaults
64
70
  to `--license preserve`, so an existing repository's license is never replaced
65
71
  unless you explicitly select a license or provide `--license-file`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-foundry",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "A fast, language-aware repository factory for agent-ready workflows, testing, security, and release automation.",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-or-later",
package/src/cli.mjs CHANGED
@@ -50,7 +50,10 @@ function parseArgs(argv) {
50
50
  languages: process.env.REPO_FOUNDRY_LANGUAGES || 'auto',
51
51
  features: process.env.REPO_FOUNDRY_FEATURES || 'all',
52
52
  packageManager: process.env.REPO_FOUNDRY_PACKAGE_MANAGER || 'auto',
53
- runtimeRepository: process.env.REPO_FOUNDRY_RUNTIME_REPOSITORY || '0xPlayerOne/code-foundry',
53
+ // Leave this empty unless the caller explicitly selects a runtime. The
54
+ // initializer can then derive the runtime from --source, which keeps
55
+ // organization forks plug-and-play.
56
+ runtimeRepository: process.env.REPO_FOUNDRY_RUNTIME_REPOSITORY || '',
54
57
  releaseType: process.env.REPO_FOUNDRY_RELEASE_TYPE || 'auto',
55
58
  license: process.env.REPO_FOUNDRY_LICENSE || (command === 'init' ? 'agpl-3.0-or-later' : 'preserve'),
56
59
  licenseFile: process.env.REPO_FOUNDRY_LICENSE_FILE || '',
@@ -123,7 +126,7 @@ function main() {
123
126
  if (options.licenseFile) common.push('--license-file', options.licenseFile)
124
127
  if (options.languagesSet) common.push('--languages', options.languages)
125
128
  if (options.featuresSet) common.push('--features', options.features)
126
- common.push('--runtime-repository', options.runtimeRepository)
129
+ if (options.runtimeRepository) common.push('--runtime-repository', options.runtimeRepository)
127
130
  if (options.prune) common.push('--prune')
128
131
  if (options.force) common.push('--force')
129
132
  if (options.dryRun) common.push('--check')
@@ -137,10 +140,10 @@ function main() {
137
140
  '--languages', options.languages,
138
141
  '--features', options.features,
139
142
  '--package-manager', options.packageManager,
140
- '--runtime-repository', options.runtimeRepository,
141
143
  '--release-type', options.releaseType,
142
144
  '--license', options.license,
143
145
  ]
146
+ if (options.runtimeRepository) initArgs.push('--runtime-repository', options.runtimeRepository)
144
147
  if (options.licenseFile) initArgs.push('--license-file', options.licenseFile)
145
148
  if (options.protection) initArgs.push('--protection')
146
149
  if (options.dryRun) initArgs.push('--dry-run')