code-foundry 0.31.4 → 0.31.6

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.
@@ -35,7 +35,7 @@ env:
35
35
  REPO_FOUNDRY_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before || '' }}
36
36
 
37
37
  concurrency:
38
- group: ${{ github.workflow_ref }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
38
+ group: code-foundry-ci-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
39
39
  cancel-in-progress: true
40
40
 
41
41
  jobs:
@@ -32,7 +32,7 @@ env:
32
32
  REPO_FOUNDRY_FEATURES: ${{ vars.REPO_FOUNDRY_FEATURES }}
33
33
 
34
34
  concurrency:
35
- group: ${{ github.workflow_ref }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
35
+ group: code-foundry-codeql-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
36
36
  cancel-in-progress: true
37
37
 
38
38
  jobs:
@@ -22,7 +22,7 @@ jobs:
22
22
  runs-on: ${{ inputs.runner }}
23
23
  timeout-minutes: 10
24
24
  concurrency:
25
- group: ${{ github.workflow_ref }}-${{ github.ref }}
25
+ group: code-foundry-draft-pr-${{ github.repository }}-${{ github.ref }}
26
26
  cancel-in-progress: true
27
27
  steps:
28
28
  - name: Check
@@ -0,0 +1,58 @@
1
+ name: Code Foundry
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ actions: read
10
+ contents: read
11
+ packages: read
12
+ pull-requests: read
13
+ security-events: write
14
+
15
+ jobs:
16
+ ci:
17
+ name: CI
18
+ uses: ./.github/workflows/ci.yml
19
+ with:
20
+ runtime-repository: 0xPlayerOne/code-foundry
21
+ runtime-ref: ${{ github.sha }}
22
+ runner: ubuntu-latest
23
+ secrets: inherit
24
+
25
+ test:
26
+ name: Test
27
+ uses: ./.github/workflows/test.yml
28
+ with:
29
+ runtime-repository: 0xPlayerOne/code-foundry
30
+ runtime-ref: ${{ github.sha }}
31
+ runner: ubuntu-latest
32
+ unit-runner: ubuntu-slim
33
+ secrets: inherit
34
+
35
+ security:
36
+ name: Security
37
+ uses: ./.github/workflows/security.yml
38
+ with:
39
+ runtime-repository: 0xPlayerOne/code-foundry
40
+ runtime-ref: ${{ github.sha }}
41
+ runner: ubuntu-slim
42
+ secrets: inherit
43
+
44
+ codeql:
45
+ name: CodeQL
46
+ uses: ./.github/workflows/codeql.yml
47
+ with:
48
+ runtime-repository: 0xPlayerOne/code-foundry
49
+ runtime-ref: ${{ github.sha }}
50
+ runner: ubuntu-latest
51
+ secrets: inherit
52
+
53
+ release-pr:
54
+ name: Release PR / Create
55
+ runs-on: ubuntu-slim
56
+ steps:
57
+ - name: Validate promotion
58
+ run: echo 'Main promotion validation is handled by this workflow; version release remains a post-merge action.'
@@ -22,7 +22,7 @@ jobs:
22
22
  runs-on: ${{ inputs.runner }}
23
23
  timeout-minutes: 10
24
24
  concurrency:
25
- group: ${{ github.workflow_ref }}-${{ github.ref }}
25
+ group: code-foundry-release-pr-${{ github.repository }}-${{ github.ref }}
26
26
  cancel-in-progress: true
27
27
  steps:
28
28
  - name: Check
@@ -36,17 +36,24 @@ jobs:
36
36
  --head staging \
37
37
  --state open \
38
38
  --json number | node -e 'let d=""; process.stdin.on("data", c => d += c).on("end", () => console.log(JSON.parse(d || "[]").length))')
39
+ MAIN_TREE=$(gh api "repos/${GITHUB_REPOSITORY}/commits/main" --jq '.commit.tree.sha')
40
+ STAGING_TREE=$(gh api "repos/${GITHUB_REPOSITORY}/commits/staging" --jq '.commit.tree.sha')
41
+ TREE_EQUAL=false
42
+ if [ "$MAIN_TREE" = "$STAGING_TREE" ]; then
43
+ TREE_EQUAL=true
44
+ fi
39
45
  COMPARE_URL="repos/${GITHUB_REPOSITORY}/compare/main...staging"
40
46
  TOTAL_COMMITS=$(gh api "$COMPARE_URL?per_page=1" --jq '.total_commits // 0')
41
47
  echo "existing=$EXISTING" >> "$GITHUB_OUTPUT"
42
48
  echo "commits=$TOTAL_COMMITS" >> "$GITHUB_OUTPUT"
49
+ echo "tree_equal=$TREE_EQUAL" >> "$GITHUB_OUTPUT"
43
50
 
44
51
  - name: No changes
45
- if: steps.check-pr.outputs.existing == '0' && steps.check-pr.outputs.commits == '0'
52
+ if: steps.check-pr.outputs.existing == '0' && (steps.check-pr.outputs.commits == '0' || steps.check-pr.outputs.tree_equal == 'true')
46
53
  run: echo 'staging is already aligned with main; no promotion PR is needed.'
47
54
 
48
55
  - name: Create
49
- if: steps.check-pr.outputs.existing == '0' && steps.check-pr.outputs.commits != '0'
56
+ if: steps.check-pr.outputs.existing == '0' && steps.check-pr.outputs.commits != '0' && steps.check-pr.outputs.tree_equal != 'true'
50
57
  run: |
51
58
  DATE=$(date +%Y-%m-%d)
52
59
  BODY_FILE="$RUNNER_TEMP/release-pr.md"
@@ -91,9 +98,23 @@ jobs:
91
98
  - After merge, Release Please opens a separate versioned release PR with the changelog.
92
99
  EOF
93
100
 
94
- gh pr create \
101
+ if ! gh pr create \
95
102
  --repo "$GITHUB_REPOSITORY" \
96
103
  --base main \
97
104
  --head staging \
98
105
  --title "Promote staging -> main ($DATE)" \
99
- --body-file "$BODY_FILE"
106
+ --body-file "$BODY_FILE"; then
107
+ EXISTING_NUMBER=$(gh pr list \
108
+ --repo "$GITHUB_REPOSITORY" \
109
+ --limit 1 \
110
+ --base main \
111
+ --head staging \
112
+ --state open \
113
+ --json number \
114
+ --jq '.[0].number // empty')
115
+ if [ -z "$EXISTING_NUMBER" ]; then
116
+ echo 'Pull request creation failed and no existing promotion PR was found.' >&2
117
+ exit 1
118
+ fi
119
+ echo "Promotion PR #$EXISTING_NUMBER already exists; treating the create race as success."
120
+ fi
@@ -3,6 +3,7 @@ name: Code Foundry
3
3
  on:
4
4
  push:
5
5
  branches: [staging]
6
+ workflow_dispatch:
6
7
 
7
8
  permissions:
8
9
  contents: read
@@ -39,7 +39,7 @@ jobs:
39
39
  runs-on: ${{ inputs.runner }}
40
40
  timeout-minutes: 20
41
41
  concurrency:
42
- group: ${{ github.workflow_ref }}-${{ github.ref }}
42
+ group: code-foundry-release-${{ github.repository }}-${{ github.ref }}
43
43
  cancel-in-progress: false
44
44
  outputs:
45
45
  release_created: ${{ steps.release.outputs.release_created || 'false' }}
@@ -31,7 +31,7 @@ env:
31
31
  REPO_FOUNDRY_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before || '' }}
32
32
 
33
33
  concurrency:
34
- group: ${{ github.workflow_ref }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
34
+ group: code-foundry-security-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
35
35
  cancel-in-progress: true
36
36
 
37
37
  jobs:
@@ -40,7 +40,7 @@ env:
40
40
  REPO_FOUNDRY_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before || '' }}
41
41
 
42
42
  concurrency:
43
- group: ${{ github.workflow_ref }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
43
+ group: code-foundry-test-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
44
44
  cancel-in-progress: true
45
45
 
46
46
  jobs:
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.6](https://github.com/0xPlayerOne/code-foundry/compare/v0.31.5...v0.31.6) (2026-07-30)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **release:** skip identical promotion trees ([d8242b9](https://github.com/0xPlayerOne/code-foundry/commit/d8242b9897c9210a2534712fedb12d2258573686))
9
+
10
+ ## [0.31.5](https://github.com/0xPlayerOne/code-foundry/compare/v0.31.4...v0.31.5) (2026-07-30)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **ci:** isolate reusable workflow concurrency ([330a939](https://github.com/0xPlayerOne/code-foundry/commit/330a9396390aef11a482bf370dbbbee22151759e))
16
+ * **ci:** use stable workflow concurrency keys ([a7eca35](https://github.com/0xPlayerOne/code-foundry/commit/a7eca35836f24f9fb240234b999462f95fa4e7da))
17
+ * **ci:** validate main promotion pull requests ([00377bb](https://github.com/0xPlayerOne/code-foundry/commit/00377bb6e54288e296245efa1466e8e61477f25a))
18
+ * **release:** allow manual promotion validation ([aaa453f](https://github.com/0xPlayerOne/code-foundry/commit/aaa453f3bc63c4e256352f094468f983dad236a9))
19
+ * **release:** promote staging runtime safeguards ([e53df8c](https://github.com/0xPlayerOne/code-foundry/commit/e53df8cca59e59a8ffd7aad3f05effc915ba2196))
20
+
3
21
  ## [0.31.4](https://github.com/0xPlayerOne/code-foundry/compare/v0.31.3...v0.31.4) (2026-07-29)
4
22
 
5
23
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-foundry",
3
- "version": "0.31.4",
3
+ "version": "0.31.6",
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/runtime.mjs CHANGED
@@ -20,6 +20,20 @@ function hasLanguage(language) {
20
20
  return languages.includes(language) || languages.includes('all')
21
21
  }
22
22
 
23
+ function hasRootJavascriptProject() {
24
+ return Boolean(readPackage()) && packageManager !== 'none'
25
+ }
26
+
27
+ function hasRootPythonProject() {
28
+ return existsSync(resolve(root, 'pyproject.toml')) ||
29
+ existsSync(resolve(root, 'uv.lock')) ||
30
+ capture('git', ['ls-files', '*requirements*.txt']) !== ''
31
+ }
32
+
33
+ function hasRootRustProject() {
34
+ return existsSync(resolve(root, 'Cargo.toml'))
35
+ }
36
+
23
37
  /** @param {string} name */
24
38
  function hasScript(name) {
25
39
  return readPackage()?.scripts?.[name] !== undefined
@@ -130,8 +144,24 @@ function relevant(task) {
130
144
  const js = hasLanguage('typescript') || hasLanguage('javascript')
131
145
  const python = hasLanguage('python')
132
146
  const rust = hasLanguage('rust')
133
- if (task === 'format' || task === 'lint' || task === 'type_check' || task === 'build') return js || python || rust
134
- if (['unit', 'integration', 'e2e', 'smoke'].includes(task)) return js || python || rust || hasLanguage('solidity')
147
+ const scripted = {
148
+ format: ['format:check', 'format', 'fmt'],
149
+ lint: ['lint'],
150
+ type_check: ['type-check', 'typecheck', 'type:check'],
151
+ build: ['build'],
152
+ unit: ['test:unit', 'test:coverage', 'test'],
153
+ integration: ['test:integration'],
154
+ e2e: ['test:e2e', 'e2e'],
155
+ smoke: ['test:smoke', 'smoke'],
156
+ }[task]
157
+ if (scripted && scripted.some((candidate) => hasScript(candidate))) return packageManager !== 'none'
158
+ if (task === 'format' || task === 'lint' || task === 'type_check' || task === 'build') {
159
+ return (js && hasRootJavascriptProject()) || (python && hasRootPythonProject()) || (rust && hasRootRustProject())
160
+ }
161
+ if (['unit', 'integration', 'e2e', 'smoke'].includes(task)) {
162
+ return (js && hasRootJavascriptProject()) || (python && hasRootPythonProject()) ||
163
+ (rust && hasRootRustProject()) || (hasLanguage('solidity') && hasRootJavascriptProject())
164
+ }
135
165
  return true
136
166
  }
137
167
 
@@ -151,7 +181,7 @@ function hasDependencyManifest(ecosystem) {
151
181
  function ci(task) {
152
182
  if (task === 'install') return install()
153
183
  if (task === 'should_run' || task === 'task_profile') {
154
- const selected = process.argv[3]
184
+ const selected = process.argv[4]
155
185
  writeOutput('applicable', relevant(selected) ? 'true' : 'false')
156
186
  writeOutput('javascript', (hasLanguage('typescript') || hasLanguage('javascript')) ? 'true' : 'false')
157
187
  writeOutput('python', hasLanguage('python') ? 'true' : 'false')
@@ -160,27 +190,27 @@ function ci(task) {
160
190
  }
161
191
  if (task === 'format') {
162
192
  const scripted = runScript(['format:check', 'format', 'fmt'])
163
- if (!scripted && (hasLanguage('typescript') || hasLanguage('javascript'))) runTool('prettier', ['--check', '.'])
164
- if (hasLanguage('python')) runTool('ruff', ['format', '--check', '.'])
165
- if (hasLanguage('rust')) run('cargo', ['fmt', '--check'])
193
+ if (!scripted && (hasLanguage('typescript') || hasLanguage('javascript')) && hasRootJavascriptProject()) runTool('prettier', ['--check', '.'])
194
+ if (hasLanguage('python') && hasRootPythonProject()) runTool('ruff', ['format', '--check', '.'])
195
+ if (hasLanguage('rust') && hasRootRustProject()) run('cargo', ['fmt', '--check'])
166
196
  return
167
197
  }
168
198
  if (task === 'lint') {
169
199
  const scripted = runScript(['lint'])
170
- if (!scripted && (hasLanguage('typescript') || hasLanguage('javascript'))) runTool('eslint', ['.'])
171
- if (hasLanguage('python')) runTool('ruff', ['check', '.'])
172
- if (hasLanguage('rust')) run('cargo', ['clippy', '--all-targets', '--', '-D', 'warnings'])
200
+ if (!scripted && (hasLanguage('typescript') || hasLanguage('javascript')) && hasRootJavascriptProject()) runTool('eslint', ['.'])
201
+ if (hasLanguage('python') && hasRootPythonProject()) runTool('ruff', ['check', '.'])
202
+ if (hasLanguage('rust') && hasRootRustProject()) run('cargo', ['clippy', '--all-targets', '--', '-D', 'warnings'])
173
203
  return
174
204
  }
175
205
  if (task === 'type_check') {
176
206
  const scripted = runScript(['type-check', 'typecheck', 'type:check'])
177
207
  if (!scripted && existsSync(resolve(root, 'tsconfig.json'))) runTool('tsc', ['--noEmit'])
178
- if (hasLanguage('rust')) run('cargo', ['check', '--all-targets'])
208
+ if (hasLanguage('rust') && hasRootRustProject()) run('cargo', ['check', '--all-targets'])
179
209
  return
180
210
  }
181
211
  if (task === 'build') {
182
212
  const scripted = runScript(['build'])
183
- if (!scripted && hasLanguage('rust')) run('cargo', ['build', '--all-targets'])
213
+ if (!scripted && hasLanguage('rust') && hasRootRustProject()) run('cargo', ['build', '--all-targets'])
184
214
  return
185
215
  }
186
216
  /** @type {Record<string, string[]>} */
@@ -192,8 +222,8 @@ function ci(task) {
192
222
  }
193
223
  const scripts = scriptsByTask[task]
194
224
  if (scripts) runScript(scripts)
195
- if (hasLanguage('rust')) run('cargo', ['test'])
196
- if (hasLanguage('python')) {
225
+ if (hasLanguage('rust') && hasRootRustProject()) run('cargo', ['test'])
226
+ if (hasLanguage('python') && hasRootPythonProject()) {
197
227
  const python = existsSync(resolve(root, '.venv/bin/python')) ? resolve(root, '.venv/bin/python') : 'python'
198
228
  const integrationTests = resolve(root, 'tests/integration')
199
229
  if (task !== 'integration' || existsSync(integrationTests)) {
@@ -291,7 +321,7 @@ function codeql() {
291
321
  /** @param {string} command */
292
322
  function printProfile(command) {
293
323
  if (command === 'get') {
294
- const key = process.argv[3]
324
+ const key = process.argv[4]
295
325
  console.log(config[key] ?? (key === 'languages' ? languages.join(',') : key === 'package_manager' ? packageManager : ''))
296
326
  return
297
327
  }