code-foundry 1.48.2 → 1.48.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/code-foundry.schema.json +13 -0
- package/.github/workflows/cloudflare-delivery.yml +1 -1
- package/.github/workflows/cloudflare-deploy.yml +1 -1
- package/.github/workflows/release.yml +1 -1
- package/.github/workflows/security.yml +1 -1
- package/.github/workflows/validation-billing.yml +1 -1
- package/.mise.toml +1 -1
- package/CHANGELOG.md +15 -15
- package/docs/CONFIGURATION.md +38 -0
- package/package.json +3 -3
- package/src/cli.mjs +14 -0
- package/src/commands/doctor.mjs +7 -1
- package/src/commands/format.mjs +44 -0
- package/src/commands/sync.mjs +9 -4
- package/src/lib/formatter.mjs +32 -0
- package/src/lib/pre-commit.mjs +2 -1
- package/src/lib/task-policy.mjs +5 -1
- package/src/runtime-core.mjs +23 -4
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Code Foundry configuration",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"formatter": {
|
|
7
|
+
"description": "Explicit JavaScript formatter. Omit to preserve repository scripts and the Oxfmt fallback.",
|
|
8
|
+
"type": "string",
|
|
9
|
+
"enum": ["oxfmt", "prettier", "bun"]
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"additionalProperties": true
|
|
13
|
+
}
|
|
@@ -802,7 +802,7 @@ jobs:
|
|
|
802
802
|
# fail with `bun: command not found` before publish.
|
|
803
803
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
804
804
|
with:
|
|
805
|
-
bun-version:
|
|
805
|
+
bun-version: 1.4.4
|
|
806
806
|
- name: Enable Corepack (pnpm/yarn managers)
|
|
807
807
|
run: corepack enable
|
|
808
808
|
- name: Install dependencies
|
|
@@ -311,7 +311,7 @@ jobs:
|
|
|
311
311
|
steps.audit-toolchain.outputs.package_manager == 'bun'
|
|
312
312
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
313
313
|
with:
|
|
314
|
-
bun-version: 1.4.
|
|
314
|
+
bun-version: 1.4.4
|
|
315
315
|
- name: Enable Corepack
|
|
316
316
|
if: >-
|
|
317
317
|
matrix.entry.language == 'javascript' &&
|
|
@@ -950,7 +950,7 @@ jobs:
|
|
|
950
950
|
steps.audit-toolchain.outputs.package_manager == 'bun'
|
|
951
951
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
|
952
952
|
with:
|
|
953
|
-
bun-version: 1.4.
|
|
953
|
+
bun-version: 1.4.4
|
|
954
954
|
- name: Enable Corepack
|
|
955
955
|
if: >-
|
|
956
956
|
steps.profile.outputs.javascript == 'true' &&
|
package/.mise.toml
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.48.4](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.3...v1.48.4) (2026-10-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Maintenance
|
|
7
|
+
|
|
8
|
+
* bun-1.4.4-cloud ([#771](https://github.com/0xPlayerOne/code-foundry/issues/771)) ([c99f0ed](https://github.com/0xPlayerOne/code-foundry/commit/c99f0ed8b66129f3b599326bc67d900719b921f4))
|
|
9
|
+
|
|
10
|
+
## [1.48.3](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.2...v1.48.3) (2026-10-17)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Tests
|
|
14
|
+
|
|
15
|
+
* read the chained hook fixture with fs.readFileSync ([#769](https://github.com/0xPlayerOne/code-foundry/issues/769)) ([5d5925a](https://github.com/0xPlayerOne/code-foundry/commit/5d5925ac38975f5b1cc9b0785aa492db029e420c))
|
|
16
|
+
|
|
3
17
|
## [1.48.2](https://github.com/0xPlayerOne/code-foundry/compare/v1.48.1...v1.48.2) (2026-10-17)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -126,23 +140,9 @@
|
|
|
126
140
|
|
|
127
141
|
* **billing:** declare the runner inputs the callers always forward ([#733](https://github.com/0xPlayerOne/code-foundry/issues/733)) ([f7a29cb](https://github.com/0xPlayerOne/code-foundry/commit/f7a29cb6dbc9a8695a9bef9e85ccce82aa9c84db))
|
|
128
142
|
|
|
129
|
-
## [1.45.0](https://github.com/0xPlayerOne/code-foundry/compare/v1.44.11...v1.45.0) (2026-10-06)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### Features
|
|
133
|
-
|
|
134
|
-
* add a merged-lane billing orchestrator for minute-billed consumers ([#731](https://github.com/0xPlayerOne/code-foundry/issues/731)) ([a91402e](https://github.com/0xPlayerOne/code-foundry/commit/a91402e701f458ece0d2ea4715f9586a06b15108))
|
|
135
|
-
|
|
136
|
-
## [1.44.11](https://github.com/0xPlayerOne/code-foundry/compare/v1.44.10...v1.44.11) (2026-10-06)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
### Bug Fixes
|
|
140
|
-
|
|
141
|
-
* **release:** retry the token probe and REST fallback writes with backoff ([#729](https://github.com/0xPlayerOne/code-foundry/issues/729)) ([ad28e18](https://github.com/0xPlayerOne/code-foundry/commit/ad28e18326f0ee8c25950fb649eb81cdb3dc0a1a))
|
|
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; 252 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/CONFIGURATION.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Configuration reference
|
|
2
2
|
|
|
3
|
+
## JavaScript formatter
|
|
4
|
+
|
|
5
|
+
Set `formatter: oxfmt`, `formatter: prettier`, or `formatter: bun` in
|
|
6
|
+
`.github/code-foundry.yml` to select the formatter explicitly. The shipped
|
|
7
|
+
`.github/code-foundry.schema.json` describes this enum; sync, doctor, task
|
|
8
|
+
discovery and execution reject unknown choices. With no key, existing package
|
|
9
|
+
format scripts retain precedence and the existing Oxfmt fallback is unchanged.
|
|
10
|
+
Current application repositories continue to use Oxfmt.
|
|
11
|
+
|
|
12
|
+
An explicit choice controls the format gate without changing lint, type-check,
|
|
13
|
+
test or build scripts. Sync retains repository-owned Prettier configuration
|
|
14
|
+
for Prettier/Bun choices, and only installs/migrates the Oxfmt baseline for
|
|
15
|
+
Oxfmt. It does not install packages or rewrite dependency lockfiles: install
|
|
16
|
+
the selected Prettier/Oxfmt dependency using the repository's package manager,
|
|
17
|
+
or install official Bun 1.4.4 for the Bun choice.
|
|
18
|
+
|
|
19
|
+
`code-foundry format --check` checks the selected formatter; omit `--check`
|
|
20
|
+
to write. Bun opt-in also supports `code-foundry format --staged` and
|
|
21
|
+
`code-foundry format --changed REF`, with optional `--check`. These invoke
|
|
22
|
+
`bun fmt --no-script --staged` and `bun fmt --no-script --changed=REF`.
|
|
23
|
+
The scopes are mutually exclusive and fail for other formatters. Changed mode
|
|
24
|
+
does not stage files. Staged write mode restages formatted files and refuses
|
|
25
|
+
partially staged files before running Bun, protecting intentionally unstaged
|
|
26
|
+
edits. The commit gate uses check-only staged formatting and continues its
|
|
27
|
+
independent staged lint, type-check, Python and Rust checks.
|
|
28
|
+
|
|
29
|
+
For Bun opt-in, a hook can replace its **formatter-only** lint-staged operation
|
|
30
|
+
with `code-foundry format --staged`. Retain every other lint-staged lint/test
|
|
31
|
+
command and any hook ordering. Remove the lint-staged dependency only when no
|
|
32
|
+
remaining command needs it, and regenerate the dependency lockfile normally.
|
|
33
|
+
Sync never removes lint-staged, its configuration or other hook operations
|
|
34
|
+
automatically. Oxfmt/Prettier consumers retain them unchanged.
|
|
35
|
+
|
|
36
|
+
Type-check-only native fallbacks use `bun check --no-script -p tsconfig.json`
|
|
37
|
+
on Bun 1.4.4. Repository scripts remain authoritative, including framework
|
|
38
|
+
checks and declaration-emitting builds. Bun's checker emits no files; it must
|
|
39
|
+
not replace a `tsc` build that emits declarations or other compiler output.
|
|
40
|
+
|
|
3
41
|
Code Foundry has one repository-owned control plane:
|
|
4
42
|
`.github/code-foundry.yml`. `init` creates it, `sync` renders the selected
|
|
5
43
|
baseline, and `doctor` checks local and GitHub-facing prerequisites.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-foundry",
|
|
3
|
-
"version": "1.48.
|
|
3
|
+
"version": "1.48.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": {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"performance:check": "node scripts/performance-check.mjs",
|
|
48
48
|
"test": "node --test",
|
|
49
49
|
"test:unit": "node --test",
|
|
50
|
-
"type-check": "
|
|
50
|
+
"type-check": "bun check --no-script -p jsconfig.json",
|
|
51
51
|
"pack:check": "npm pack --dry-run",
|
|
52
52
|
"prepublishOnly": "npm test && npm run pack:check"
|
|
53
53
|
},
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"engines": {
|
|
61
61
|
"node": ">=24"
|
|
62
62
|
},
|
|
63
|
-
"packageManager": "bun@1.4.
|
|
63
|
+
"packageManager": "bun@1.4.4"
|
|
64
64
|
}
|
package/src/cli.mjs
CHANGED
|
@@ -16,6 +16,7 @@ Usage:
|
|
|
16
16
|
npx code-foundry init [--target PATH]
|
|
17
17
|
npx code-foundry sync [--target PATH]
|
|
18
18
|
npx code-foundry doctor [--target PATH]
|
|
19
|
+
npx code-foundry format [--target PATH] [--check] [--staged | --changed REF]
|
|
19
20
|
npx code-foundry plan [--target PATH] [--tier fast|audit] [--changed] [--base REF] [--json]
|
|
20
21
|
npx code-foundry check [--target PATH] [--tier fast|audit] [--changed] [--base REF] [--timeout SECONDS] [--json]
|
|
21
22
|
npx code-foundry affected [--target PATH] [--base REF] [--head REF] [--tool auto|turbo|vite-plus]
|
|
@@ -179,6 +180,19 @@ function parseArgs(argv) {
|
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
async function main() {
|
|
183
|
+
if (process.argv[2] === 'format') {
|
|
184
|
+
if (process.argv.slice(3).some((arg) => arg === '--help' || arg === '-h')) {
|
|
185
|
+
console.log('Usage: code-foundry format [--target PATH] [--check] [--staged | --changed REF]')
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const { formatCommand } = await import('./commands/format.mjs')
|
|
190
|
+
process.exitCode = formatCommand(process.argv.slice(3))
|
|
191
|
+
} catch (error) {
|
|
192
|
+
fail(error instanceof Error ? error.message : String(error))
|
|
193
|
+
}
|
|
194
|
+
return
|
|
195
|
+
}
|
|
182
196
|
if (['plan', 'check'].includes(process.argv[2])) {
|
|
183
197
|
const { agentCommand } = await import('./commands/agent-check.mjs')
|
|
184
198
|
process.exitCode = agentCommand(process.argv.slice(2))
|
package/src/commands/doctor.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import { recommendRunners, resolveMonorepoTool, resolveProfile } from '../lib/pr
|
|
|
8
8
|
import { packageManagerForLockfile } from '../lib/lockfiles.mjs'
|
|
9
9
|
import { doctorGithub } from '../lib/github-doctor.mjs'
|
|
10
10
|
import { isGeneratedEventCaller } from './sync.mjs'
|
|
11
|
+
import { formatterChoice } from '../lib/formatter.mjs'
|
|
11
12
|
|
|
12
13
|
/** @param {string} message */
|
|
13
14
|
function warn(message) {
|
|
@@ -28,6 +29,11 @@ export function doctor(root, options = {}) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
const toolchain = config.toolchain ?? 'auto'
|
|
32
|
+
try {
|
|
33
|
+
formatterChoice(config.formatter)
|
|
34
|
+
} catch (cause) {
|
|
35
|
+
error(cause instanceof Error ? cause.message : String(cause))
|
|
36
|
+
}
|
|
31
37
|
if (!['auto', 'native', 'mise'].includes(toolchain)) error(`unsupported toolchain: ${toolchain}`)
|
|
32
38
|
if (toolchain === 'mise' && !existsSync(join(target, '.mise.toml')))
|
|
33
39
|
error('.mise.toml is required when toolchain: mise')
|
|
@@ -237,7 +243,7 @@ export function doctor(root, options = {}) {
|
|
|
237
243
|
error('validation caller registers draft-time checks; run code-foundry sync.')
|
|
238
244
|
}
|
|
239
245
|
if (
|
|
240
|
-
!/uses:\s+(?:\.\/\.github\/workflows\/validation(?:-no-codeql)?\.yml|\S+\/\.github\/workflows\/validation(?:-no-codeql)?\.yml@)/.test(
|
|
246
|
+
!/uses:\s+(?:\.\/\.github\/workflows\/validation(?:-no-codeql|-billing)?\.yml|\S+\/\.github\/workflows\/validation(?:-no-codeql|-billing)?\.yml@)/.test(
|
|
241
247
|
caller
|
|
242
248
|
)
|
|
243
249
|
) {
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { resolve } from 'node:path'
|
|
4
|
+
import { spawnSync } from 'node:child_process'
|
|
5
|
+
import { readConfig } from '../lib/config.mjs'
|
|
6
|
+
import { formatterCommand } from '../lib/formatter.mjs'
|
|
7
|
+
|
|
8
|
+
/** @param {string[]} argv */
|
|
9
|
+
export function formatCommand(argv) {
|
|
10
|
+
let target = process.cwd()
|
|
11
|
+
let check = false
|
|
12
|
+
let staged = false
|
|
13
|
+
/** @type {string|undefined} */
|
|
14
|
+
let changed
|
|
15
|
+
while (argv.length) {
|
|
16
|
+
const arg = argv.shift()
|
|
17
|
+
if (arg === '--target') target = argv.shift() || ''
|
|
18
|
+
else if (arg === '--check') check = true
|
|
19
|
+
else if (arg === '--staged') staged = true
|
|
20
|
+
else if (arg === '--changed') changed = argv.shift() || ''
|
|
21
|
+
else throw new Error(`Unknown format option: ${arg}`)
|
|
22
|
+
}
|
|
23
|
+
if (!target || target.startsWith('-')) throw new Error('--target requires a path')
|
|
24
|
+
target = resolve(target)
|
|
25
|
+
const config = readConfig(resolve(target, '.github/code-foundry.yml'))
|
|
26
|
+
const [tool, ...args] = formatterCommand(config.formatter, { check, staged, changed })
|
|
27
|
+
if (staged && !check) {
|
|
28
|
+
// Bun restages working-tree contents. Refuse partial staging rather than
|
|
29
|
+
// silently including edits that the caller deliberately left out.
|
|
30
|
+
/** @param {string[]} diffArgs */
|
|
31
|
+
const paths = (diffArgs) => {
|
|
32
|
+
const result = spawnSync('git', diffArgs, { cwd: target, encoding: 'utf8' })
|
|
33
|
+
if (result.status !== 0) throw new Error('Cannot inspect staged formatter changes')
|
|
34
|
+
return result.stdout.split('\0').filter(Boolean)
|
|
35
|
+
}
|
|
36
|
+
const index = new Set(paths(['diff', '--cached', '--name-only', '-z']))
|
|
37
|
+
if (paths(['diff', '--name-only', '-z']).some((file) => index.has(file)))
|
|
38
|
+
throw new Error('Partially staged files: format and stage those files explicitly first')
|
|
39
|
+
}
|
|
40
|
+
const command = tool === 'bun' ? [tool, ...args] : ['npx', '--no-install', tool, ...args]
|
|
41
|
+
const result = spawnSync(command[0], command.slice(1), { cwd: target, stdio: 'inherit' })
|
|
42
|
+
if (result.error) throw result.error
|
|
43
|
+
return result.status ?? 1
|
|
44
|
+
}
|
package/src/commands/sync.mjs
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
} from '../lib/config.mjs'
|
|
28
28
|
import { buildReleaseConfig, buildReleaseManifest } from '../lib/release-manifest.mjs'
|
|
29
29
|
import { customWorkflowFiles, overlayPolicy } from '../lib/overlay.mjs'
|
|
30
|
+
import { formatterChoice } from '../lib/formatter.mjs'
|
|
30
31
|
import {
|
|
31
32
|
inspectQueueCaller,
|
|
32
33
|
mergeQueueEnabled,
|
|
@@ -54,6 +55,7 @@ const standardFiles = [
|
|
|
54
55
|
'.github/PULL_REQUEST_TEMPLATE.md',
|
|
55
56
|
'.github/SECURITY.md',
|
|
56
57
|
'.github/dependabot.yml',
|
|
58
|
+
'.github/code-foundry.schema.json',
|
|
57
59
|
'.github/ISSUE_TEMPLATE/bug_report.yml',
|
|
58
60
|
'.github/ISSUE_TEMPLATE/config.yml',
|
|
59
61
|
'.github/ISSUE_TEMPLATE/feature_request.yml',
|
|
@@ -153,6 +155,7 @@ function synchronize(options) {
|
|
|
153
155
|
}
|
|
154
156
|
const defaults = createDefaultConfig(target, source, existingConfig.git_workflow)
|
|
155
157
|
let config = { ...defaults, ...existingConfig }
|
|
158
|
+
formatterChoice(config.formatter)
|
|
156
159
|
const workflow = gitWorkflow(config.git_workflow)
|
|
157
160
|
if (!['direct', 'staging-release'].includes(workflow)) {
|
|
158
161
|
throw new Error(`Unsupported git_workflow: ${workflow}; use direct or staging-release.`)
|
|
@@ -514,7 +517,7 @@ function synchronize(options) {
|
|
|
514
517
|
else rmSync(join(target, file), { force: true })
|
|
515
518
|
}
|
|
516
519
|
}
|
|
517
|
-
if (includesValue(languages, 'typescript')) {
|
|
520
|
+
if (includesValue(languages, 'typescript') && formatterChoice(config.formatter) === 'oxfmt') {
|
|
518
521
|
// The Oxfmt baseline supersedes .prettierrc/.prettierignore. Entries the
|
|
519
522
|
// consumer added beyond the baseline survive the migration inside the
|
|
520
523
|
// .oxfmtrc.json ignorePatterns list.
|
|
@@ -543,7 +546,7 @@ function synchronize(options) {
|
|
|
543
546
|
changed,
|
|
544
547
|
dryRun
|
|
545
548
|
)
|
|
546
|
-
} else {
|
|
549
|
+
} else if (!includesValue(languages, 'typescript')) {
|
|
547
550
|
for (const file of ['.prettierrc', '.prettierignore']) {
|
|
548
551
|
if (existsSync(join(target, file))) {
|
|
549
552
|
changed.push(file)
|
|
@@ -690,9 +693,11 @@ function renderReleaseConfig(target, sourceFile, config) {
|
|
|
690
693
|
|
|
691
694
|
/** @param {string} file @param {string} languages @param {string} features @param {Record<string, string>} config */
|
|
692
695
|
function shouldInclude(file, languages, features, config) {
|
|
696
|
+
if (file === '.github/code-foundry.schema.json') return includesValue(languages, 'typescript')
|
|
693
697
|
if (file === 'ruff.toml') return includesValue(languages, 'python')
|
|
694
|
-
if (file === '.oxfmtrc.json'
|
|
695
|
-
return includesValue(languages, 'typescript')
|
|
698
|
+
if (file === '.oxfmtrc.json')
|
|
699
|
+
return includesValue(languages, 'typescript') && formatterChoice(config.formatter) === 'oxfmt'
|
|
700
|
+
if (file === '.oxlintrc.json') return includesValue(languages, 'typescript')
|
|
696
701
|
if (file === '.github/dependabot.yml')
|
|
697
702
|
return (
|
|
698
703
|
configured(config.dependency_updater, 'dependabot') === 'dependabot' &&
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/** Explicit formatter choices. An absent key preserves repository-script precedence. */
|
|
4
|
+
export const FORMATTERS = Object.freeze(['oxfmt', 'prettier', 'bun'])
|
|
5
|
+
|
|
6
|
+
/** @param {string|undefined} value */
|
|
7
|
+
export function formatterChoice(value) {
|
|
8
|
+
const choice = value || 'oxfmt'
|
|
9
|
+
if (!FORMATTERS.includes(choice))
|
|
10
|
+
throw new Error(`Unsupported formatter: ${choice}; use oxfmt, prettier, or bun.`)
|
|
11
|
+
return choice
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** @param {string|undefined} value @param {{check?: boolean, staged?: boolean, changed?: string, files?: string[]}} [options] */
|
|
15
|
+
export function formatterCommand(value, options = {}) {
|
|
16
|
+
const choice = formatterChoice(value)
|
|
17
|
+
const { check = true, staged = false, changed, files = ['.'] } = options
|
|
18
|
+
if (staged && changed !== undefined) throw new Error('--staged and --changed are exclusive')
|
|
19
|
+
if ((staged || changed !== undefined) && choice !== 'bun')
|
|
20
|
+
throw new Error('--staged and --changed require formatter: bun')
|
|
21
|
+
if (changed !== undefined && (!changed.trim() || changed.startsWith('-')))
|
|
22
|
+
throw new Error('--changed requires a Git branch or commit')
|
|
23
|
+
if (choice === 'bun')
|
|
24
|
+
return [
|
|
25
|
+
'bun',
|
|
26
|
+
'fmt',
|
|
27
|
+
'--no-script',
|
|
28
|
+
...(check ? ['--check'] : []),
|
|
29
|
+
...(staged ? ['--staged'] : changed !== undefined ? [`--changed=${changed}`] : files),
|
|
30
|
+
]
|
|
31
|
+
return [choice, ...(check ? ['--check'] : ['--write']), ...files]
|
|
32
|
+
}
|
package/src/lib/pre-commit.mjs
CHANGED
|
@@ -20,7 +20,8 @@ const LINTABLE = /\.(?:[cm]?[jt]sx?|vue|svelte|astro)$/i
|
|
|
20
20
|
const TYPED = /\.(?:ts|tsx|mts|cts)$|(?:^|\/)tsconfig[^/]*\.json$/i
|
|
21
21
|
/** JavaScript sources, type-checked only when the repository opts in with a root jsconfig. */
|
|
22
22
|
const JAVASCRIPT = /\.(?:js|jsx|mjs|cjs)$|(?:^|\/)jsconfig[^/]*\.json$/i
|
|
23
|
-
const FORMAT_CONFIG =
|
|
23
|
+
const FORMAT_CONFIG =
|
|
24
|
+
/^(?:\.oxfmtrc\.jsonc?|oxfmt\.config\.[^/]+|\.prettierrc(?:\.[^/]+)?|prettier\.config\.[^/]+|\.prettierignore|bunfig\.toml|\.github\/code-foundry\.yml)$/
|
|
24
25
|
const LINT_CONFIG = /^(?:\.oxlintrc\.json|oxlint\.config\.[^/]+)$/
|
|
25
26
|
const PYTHON = /\.pyi?$/i
|
|
26
27
|
const PYTHON_CONFIG =
|
package/src/lib/task-policy.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import { spawnSync } from 'node:child_process'
|
|
|
5
5
|
import { isAbsolute, relative, resolve, sep } from 'node:path'
|
|
6
6
|
import { createHash } from 'node:crypto'
|
|
7
7
|
import { listValue, readConfig } from './config.mjs'
|
|
8
|
+
import { formatterChoice } from './formatter.mjs'
|
|
8
9
|
|
|
9
10
|
export const TASKS = Object.freeze([
|
|
10
11
|
'format',
|
|
@@ -36,6 +37,7 @@ export const TASK_SCRIPTS = Object.freeze({
|
|
|
36
37
|
/** @param {string} root */
|
|
37
38
|
export function readTaskPolicy(root) {
|
|
38
39
|
const config = readConfig(resolve(root, '.github/code-foundry.yml'))
|
|
40
|
+
formatterChoice(config.formatter)
|
|
39
41
|
const required = listValue(config.required_capabilities ?? '')
|
|
40
42
|
for (const capability of required) {
|
|
41
43
|
if (![...TASKS, 'coverage'].includes(capability))
|
|
@@ -114,7 +116,9 @@ export function describeTask(root, task, profile) {
|
|
|
114
116
|
rust ||
|
|
115
117
|
(['format', 'lint'].includes(task) && python) ||
|
|
116
118
|
(task === 'type_check' && existsSync(resolve(root, 'tsconfig.json'))) ||
|
|
117
|
-
(js &&
|
|
119
|
+
(js &&
|
|
120
|
+
task === 'format' &&
|
|
121
|
+
(Boolean(policy.config.formatter) || hasNativeToolSetup(root, pkg, 'oxfmt'))) ||
|
|
118
122
|
(js && task === 'lint' && hasNativeToolSetup(root, pkg, 'oxlint'))
|
|
119
123
|
if (!native) {
|
|
120
124
|
applicable = false
|
package/src/runtime-core.mjs
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
freshCloneBuildHint,
|
|
29
29
|
} from './lib/pre-commit.mjs'
|
|
30
30
|
import { runNodePackagePerformance } from './lib/node-package-performance.mjs'
|
|
31
|
+
import { formatterChoice, formatterCommand } from './lib/formatter.mjs'
|
|
31
32
|
import {
|
|
32
33
|
EVAL_BUDGET_FILE_DEFAULT,
|
|
33
34
|
EVAL_REPORT_FILE,
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
|
|
39
40
|
const root = process.cwd()
|
|
40
41
|
const config = readConfig(resolve(root, '.github/code-foundry.yml'))
|
|
42
|
+
formatterChoice(config.formatter)
|
|
41
43
|
const repoProfile = resolveProfile(root)
|
|
42
44
|
/** @type {string[]} */
|
|
43
45
|
const languages = configured(config.languages, repoProfile.languages).split(',').filter(Boolean)
|
|
@@ -737,7 +739,8 @@ function ci(task) {
|
|
|
737
739
|
return
|
|
738
740
|
}
|
|
739
741
|
if (task === 'format') {
|
|
740
|
-
|
|
742
|
+
if (config.formatter && hasRootJavascriptProject()) runSelectedFormatter()
|
|
743
|
+
const scripted = config.formatter || runScript(['format:check', 'format', 'fmt'])
|
|
741
744
|
if (
|
|
742
745
|
!scripted &&
|
|
743
746
|
(hasLanguage('typescript') || hasLanguage('javascript')) &&
|
|
@@ -774,7 +777,8 @@ function ci(task) {
|
|
|
774
777
|
}
|
|
775
778
|
if (task === 'type_check') {
|
|
776
779
|
const scripted = runScript(['type-check', 'typecheck', 'type:check'])
|
|
777
|
-
if (!scripted && existsSync(resolve(root, 'tsconfig.json')))
|
|
780
|
+
if (!scripted && existsSync(resolve(root, 'tsconfig.json')))
|
|
781
|
+
run('bun', ['check', '--no-script', '-p', 'tsconfig.json'])
|
|
778
782
|
if (hasLanguage('rust') && hasRootRustProject()) run('cargo', ['check', '--all-targets'])
|
|
779
783
|
return
|
|
780
784
|
}
|
|
@@ -1189,6 +1193,13 @@ function runToolOnFiles(tool, args, files) {
|
|
|
1189
1193
|
for (const chunk of chunkFiles(files)) runTool(tool, [...args, ...chunk])
|
|
1190
1194
|
}
|
|
1191
1195
|
|
|
1196
|
+
/** @param {{staged?: boolean, files?: string[]}} [options] */
|
|
1197
|
+
function runSelectedFormatter(options = {}) {
|
|
1198
|
+
const [tool, ...args] = formatterCommand(config.formatter, options)
|
|
1199
|
+
if (tool === 'bun') run(tool, args)
|
|
1200
|
+
else runTool(tool, args)
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1192
1203
|
/**
|
|
1193
1204
|
* The local commit gate.
|
|
1194
1205
|
*
|
|
@@ -1221,7 +1232,15 @@ export function preCommit() {
|
|
|
1221
1232
|
const js = (hasLanguage('typescript') || hasLanguage('javascript')) && hasRootJavascriptProject()
|
|
1222
1233
|
|
|
1223
1234
|
if (js && (plan.formatAll || plan.formatFiles.length)) {
|
|
1224
|
-
if (
|
|
1235
|
+
if (config.formatter) {
|
|
1236
|
+
runSelectedFormatter(
|
|
1237
|
+
plan.formatAll
|
|
1238
|
+
? {}
|
|
1239
|
+
: config.formatter === 'bun'
|
|
1240
|
+
? { staged: true }
|
|
1241
|
+
: { files: plan.formatFiles }
|
|
1242
|
+
)
|
|
1243
|
+
} else if (plan.formatAll) {
|
|
1225
1244
|
if (!runScript(['format:check', 'format', 'fmt']) && hasOxfmtSetup())
|
|
1226
1245
|
runTool('oxfmt', ['--check', '.'])
|
|
1227
1246
|
} else if (hasOxfmtSetup()) {
|
|
@@ -1265,7 +1284,7 @@ export function preCommit() {
|
|
|
1265
1284
|
onFailure: buildHint,
|
|
1266
1285
|
})
|
|
1267
1286
|
if (!scripted && existsSync(resolve(root, 'tsconfig.json')))
|
|
1268
|
-
|
|
1287
|
+
run('bun', ['check', '--no-script', '-p', 'tsconfig.json'], { onFailure: buildHint })
|
|
1269
1288
|
}
|
|
1270
1289
|
if (plan.rust) {
|
|
1271
1290
|
run('cargo', ['fmt', '--check'])
|