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.
- package/.github/scripts/init-repo.sh +3 -3
- package/.github/scripts/sync-template.sh +10 -3
- package/.github/workflows/reusable-draft-pr.yml +51 -0
- package/.github/workflows/reusable-release-pr.yml +86 -0
- package/CHANGELOG.md +14 -0
- package/README.md +11 -5
- package/package.json +1 -1
- package/src/cli.mjs +6 -3
|
@@ -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:-
|
|
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 -
|
|
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
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
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
|
-
|
|
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')
|