code-foundry 0.3.0 → 0.5.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/.githooks/pre-commit +2 -64
- package/.github/scripts/init-repo.sh +4 -0
- package/.github/scripts/pre-commit.sh +67 -0
- package/.github/scripts/sync-template.sh +18 -1
- package/CHANGELOG.md +14 -0
- package/README.md +8 -0
- package/package.json +1 -1
- package/src/cli.mjs +5 -0
package/.githooks/pre-commit
CHANGED
|
@@ -1,67 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
git
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
has_script() {
|
|
8
|
-
[ -f package.json ] && node -e 'const p=require("./package.json"); process.exit(p.scripts?.[process.argv[1]] ? 0 : 1)' "$1"
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
package_manager() {
|
|
12
|
-
if [ -f bun.lock ] || [ -f bun.lockb ]; then echo bun
|
|
13
|
-
elif [ -f pnpm-lock.yaml ]; then echo pnpm
|
|
14
|
-
elif [ -f yarn.lock ]; then echo yarn
|
|
15
|
-
else echo npm
|
|
16
|
-
fi
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
run_lint_staged() {
|
|
20
|
-
if ! has_script lint-staged; then return 1; fi
|
|
21
|
-
case "$(package_manager)" in
|
|
22
|
-
bun) bun run lint-staged ;;
|
|
23
|
-
pnpm) corepack pnpm run lint-staged ;;
|
|
24
|
-
yarn) corepack yarn run lint-staged ;;
|
|
25
|
-
npm) npm run lint-staged ;;
|
|
26
|
-
esac
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if printf '%s\n' "$changed" | grep -Eq '\.(js|jsx|ts|tsx|json|md|mdx|yml|yaml)$'; then
|
|
30
|
-
if ! run_lint_staged; then
|
|
31
|
-
bash .github/scripts/ci.sh format
|
|
32
|
-
bash .github/scripts/ci.sh lint
|
|
33
|
-
fi
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
if printf '%s\n' "$changed" | grep -Eq '\.rs$|(^|/)Cargo\.toml$'; then
|
|
37
|
-
cargo fmt --check
|
|
38
|
-
cargo clippy --all-targets -- -D warnings
|
|
39
|
-
fi
|
|
40
|
-
|
|
41
|
-
python_files=()
|
|
42
|
-
python_config=false
|
|
43
|
-
while IFS= read -r file; do
|
|
44
|
-
case "$file" in
|
|
45
|
-
*.py)
|
|
46
|
-
[ -f "$file" ] && python_files+=("$file")
|
|
47
|
-
;;
|
|
48
|
-
pyproject.toml|requirements.txt|requirements-dev.txt)
|
|
49
|
-
python_config=true
|
|
50
|
-
;;
|
|
51
|
-
esac
|
|
52
|
-
done <<< "$changed"
|
|
53
|
-
|
|
54
|
-
if [ "$python_config" = true ] || [ "${#python_files[@]}" -gt 0 ]; then
|
|
55
|
-
ruff_targets=(.)
|
|
56
|
-
[ "$python_config" = true ] || ruff_targets=("${python_files[@]}")
|
|
57
|
-
if [ -x .venv/bin/ruff ]; then
|
|
58
|
-
.venv/bin/ruff format --check "${ruff_targets[@]}"
|
|
59
|
-
.venv/bin/ruff check "${ruff_targets[@]}"
|
|
60
|
-
elif command -v ruff >/dev/null 2>&1; then
|
|
61
|
-
ruff format --check "${ruff_targets[@]}"
|
|
62
|
-
ruff check "${ruff_targets[@]}"
|
|
63
|
-
else
|
|
64
|
-
echo "Python files changed but Ruff is not installed; run the template setup first." >&2
|
|
65
|
-
exit 1
|
|
66
|
-
fi
|
|
67
|
-
fi
|
|
4
|
+
repo_root="$(git rev-parse --show-toplevel)"
|
|
5
|
+
exec bash "$repo_root/.github/scripts/pre-commit.sh" "$@"
|
|
@@ -7,6 +7,7 @@ profile="${REPO_FOUNDRY_PROFILE:-auto}"
|
|
|
7
7
|
protection=false
|
|
8
8
|
dry_run=false
|
|
9
9
|
prune=false
|
|
10
|
+
force=false
|
|
10
11
|
languages="${REPO_FOUNDRY_LANGUAGES:-auto}"
|
|
11
12
|
features="${REPO_FOUNDRY_FEATURES:-all}"
|
|
12
13
|
package_manager="${REPO_FOUNDRY_PACKAGE_MANAGER:-auto}"
|
|
@@ -41,6 +42,7 @@ Options:
|
|
|
41
42
|
--license-file PATH Use an exact custom license file
|
|
42
43
|
--npm-publish Enable npm publication in the release workflow
|
|
43
44
|
--dry-run Preview changes without writing files
|
|
45
|
+
--force Replace protected standard docs/templates
|
|
44
46
|
--prune Remove disabled standard workflows (never custom workflows)
|
|
45
47
|
--protection Synchronize main branch required checks
|
|
46
48
|
--no-bootstrap Skip mise/hooks/doctor bootstrap after init
|
|
@@ -66,6 +68,7 @@ while [ "$#" -gt 0 ]; do
|
|
|
66
68
|
--license-file) license_file="${2:?missing license file}"; shift 2 ;;
|
|
67
69
|
--npm-publish) npm_publish=true; shift ;;
|
|
68
70
|
--dry-run) dry_run=true; shift ;;
|
|
71
|
+
--force) force=true; shift ;;
|
|
69
72
|
--prune) prune=true; shift ;;
|
|
70
73
|
--protection) protection=true; shift ;;
|
|
71
74
|
--no-bootstrap) bootstrap=false; shift ;;
|
|
@@ -110,6 +113,7 @@ sync_args=(
|
|
|
110
113
|
if [ -n "$license_file" ]; then sync_args+=(--license-file "$license_file"); fi
|
|
111
114
|
if [ "$dry_run" = true ]; then sync_args+=(--check); else sync_args+=(--apply); fi
|
|
112
115
|
if [ "$prune" = true ]; then sync_args+=(--prune); fi
|
|
116
|
+
if [ "$force" = true ]; then sync_args+=(--force); fi
|
|
113
117
|
bash "$sync_script" "${sync_args[@]}"
|
|
114
118
|
|
|
115
119
|
if [ "$dry_run" = true ]; then
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
git diff --cached --check
|
|
5
|
+
changed=$(git diff --cached --name-only)
|
|
6
|
+
|
|
7
|
+
has_script() {
|
|
8
|
+
[ -f package.json ] && node -e 'const p=require("./package.json"); process.exit(p.scripts?.[process.argv[1]] ? 0 : 1)' "$1"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
package_manager() {
|
|
12
|
+
if [ -f bun.lock ] || [ -f bun.lockb ]; then echo bun
|
|
13
|
+
elif [ -f pnpm-lock.yaml ]; then echo pnpm
|
|
14
|
+
elif [ -f yarn.lock ]; then echo yarn
|
|
15
|
+
else echo npm
|
|
16
|
+
fi
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
run_lint_staged() {
|
|
20
|
+
if ! has_script lint-staged; then return 1; fi
|
|
21
|
+
case "$(package_manager)" in
|
|
22
|
+
bun) bun run lint-staged ;;
|
|
23
|
+
pnpm) corepack pnpm run lint-staged ;;
|
|
24
|
+
yarn) corepack yarn run lint-staged ;;
|
|
25
|
+
npm) npm run lint-staged ;;
|
|
26
|
+
esac
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if printf '%s\n' "$changed" | grep -Eq '\.(js|jsx|ts|tsx|json|md|mdx|yml|yaml)$'; then
|
|
30
|
+
if ! run_lint_staged; then
|
|
31
|
+
bash .github/scripts/ci.sh format
|
|
32
|
+
bash .github/scripts/ci.sh lint
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
if printf '%s\n' "$changed" | grep -Eq '\.rs$|(^|/)Cargo\.toml$'; then
|
|
37
|
+
cargo fmt --check
|
|
38
|
+
cargo clippy --all-targets -- -D warnings
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
python_files=()
|
|
42
|
+
python_config=false
|
|
43
|
+
while IFS= read -r file; do
|
|
44
|
+
case "$file" in
|
|
45
|
+
*.py)
|
|
46
|
+
[ -f "$file" ] && python_files+=("$file")
|
|
47
|
+
;;
|
|
48
|
+
pyproject.toml|requirements.txt|requirements-dev.txt)
|
|
49
|
+
python_config=true
|
|
50
|
+
;;
|
|
51
|
+
esac
|
|
52
|
+
done <<< "$changed"
|
|
53
|
+
|
|
54
|
+
if [ "$python_config" = true ] || [ "${#python_files[@]}" -gt 0 ]; then
|
|
55
|
+
ruff_targets=(.)
|
|
56
|
+
[ "$python_config" = true ] || ruff_targets=("${python_files[@]}")
|
|
57
|
+
if [ -x .venv/bin/ruff ]; then
|
|
58
|
+
.venv/bin/ruff format --check "${ruff_targets[@]}"
|
|
59
|
+
.venv/bin/ruff check "${ruff_targets[@]}"
|
|
60
|
+
elif command -v ruff >/dev/null 2>&1; then
|
|
61
|
+
ruff format --check "${ruff_targets[@]}"
|
|
62
|
+
ruff check "${ruff_targets[@]}"
|
|
63
|
+
else
|
|
64
|
+
echo "Python files changed but Ruff is not installed; run the template setup first." >&2
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
fi
|
|
@@ -18,6 +18,7 @@ Options:
|
|
|
18
18
|
--check Preview changes (default)
|
|
19
19
|
--apply Apply changes
|
|
20
20
|
--prune Remove disabled standard workflows
|
|
21
|
+
--force Replace protected standard docs/templates
|
|
21
22
|
EOF
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -29,6 +30,7 @@ profile="${REPO_FOUNDRY_PROFILE:-auto}"
|
|
|
29
30
|
languages="${REPO_FOUNDRY_LANGUAGES:-auto}"
|
|
30
31
|
features="${REPO_FOUNDRY_FEATURES:-all}"
|
|
31
32
|
prune=false
|
|
33
|
+
force=false
|
|
32
34
|
languages_set=false
|
|
33
35
|
profile_set=false
|
|
34
36
|
features_set=false
|
|
@@ -107,6 +109,7 @@ while [ "$#" -gt 0 ]; do
|
|
|
107
109
|
--check) mode="check"; shift ;;
|
|
108
110
|
--apply) mode="apply"; shift ;;
|
|
109
111
|
--prune) prune=true; shift ;;
|
|
112
|
+
--force) force=true; shift ;;
|
|
110
113
|
-h|--help) usage; exit 0 ;;
|
|
111
114
|
*) usage >&2; exit 2 ;;
|
|
112
115
|
esac
|
|
@@ -159,9 +162,10 @@ contains_word "$valid_release_types" "$release_type" || {
|
|
|
159
162
|
exit 2
|
|
160
163
|
}
|
|
161
164
|
case "$license" in
|
|
162
|
-
preserve|agpl-3.0-or-later|mit|none) ;;
|
|
165
|
+
preserve|agpl-3.0-or-later|mit|custom|none) ;;
|
|
163
166
|
*) printf 'Unsupported license: %s\n' "$license" >&2; exit 2 ;;
|
|
164
167
|
esac
|
|
168
|
+
[ -z "$license_file" ] || license=custom
|
|
165
169
|
|
|
166
170
|
if [ -d "$source" ] && [ -f "$source/.github/scripts/sync-template.sh" ]; then
|
|
167
171
|
template_root="$source"
|
|
@@ -229,6 +233,7 @@ files=(
|
|
|
229
233
|
.github/scripts/doctor.sh
|
|
230
234
|
.github/scripts/security.sh
|
|
231
235
|
.github/scripts/sitecustomize.py
|
|
236
|
+
.github/scripts/pre-commit.sh
|
|
232
237
|
.github/scripts/sync-template.sh
|
|
233
238
|
.github/scripts/init-repo.sh
|
|
234
239
|
.github/scripts/sync-codeowners.sh
|
|
@@ -268,6 +273,14 @@ standard_workflow() {
|
|
|
268
273
|
esac
|
|
269
274
|
}
|
|
270
275
|
|
|
276
|
+
protected_standard_file() {
|
|
277
|
+
case "$1" in
|
|
278
|
+
AGENTS.md|.github/CODE_OF_CONDUCT.md|.github/CONTRIBUTING.md|.github/PULL_REQUEST_TEMPLATE.md|.github/SECURITY.md|.github/ISSUE_TEMPLATE/*)
|
|
279
|
+
return 0 ;;
|
|
280
|
+
*) return 1 ;;
|
|
281
|
+
esac
|
|
282
|
+
}
|
|
283
|
+
|
|
271
284
|
custom_workflows=()
|
|
272
285
|
if [ -d .github/workflows ]; then
|
|
273
286
|
while IFS= read -r workflow; do
|
|
@@ -292,6 +305,10 @@ for file in "${files[@]}"; do
|
|
|
292
305
|
echo "Template file missing: $file" >&2
|
|
293
306
|
exit 1
|
|
294
307
|
fi
|
|
308
|
+
if [ "$force" != true ] && [ -f "$file" ] && protected_standard_file "$file"; then
|
|
309
|
+
printf 'Preserving existing authored file %s (use --force to refresh).\n' "$file"
|
|
310
|
+
continue
|
|
311
|
+
fi
|
|
295
312
|
if { [ "$file" = LICENSE ] || [ "$file" = NOTICE ]; } && [ "$license" = preserve ] && [ -f "$file" ]; then
|
|
296
313
|
continue
|
|
297
314
|
fi
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.4.0...v0.5.0) (2026-07-28)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **init:** preserve authored repository docs ([9fab688](https://github.com/0xPlayerOne/code-foundry/commit/9fab6880ad82762cf4e2eb7d813355ab22cdb905))
|
|
9
|
+
|
|
10
|
+
## [0.4.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.3.0...v0.4.0) (2026-07-28)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **hooks:** centralize generated pre-commit runtime ([8e8111c](https://github.com/0xPlayerOne/code-foundry/commit/8e8111c1b1b95fcb356fe1b1366e369393cbf946))
|
|
16
|
+
|
|
3
17
|
## [0.3.0](https://github.com/0xPlayerOne/code-foundry/compare/v0.2.0...v0.3.0) (2026-07-28)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -40,6 +40,10 @@ Initialization defaults to AGPL for new repositories. Synchronization defaults
|
|
|
40
40
|
to `--license preserve`, so an existing repository's license is never replaced
|
|
41
41
|
unless you explicitly select a license or provide `--license-file`.
|
|
42
42
|
|
|
43
|
+
The generated Git hook is intentionally a small launcher. It points at the
|
|
44
|
+
package-managed `.github/scripts/pre-commit.sh`, so repositories do not need to
|
|
45
|
+
maintain a second copy of the language-aware hook logic.
|
|
46
|
+
|
|
43
47
|
## Setup
|
|
44
48
|
|
|
45
49
|
1. Install [mise](https://mise.jdx.dev/).
|
|
@@ -61,6 +65,10 @@ Use `bash .github/scripts/sync-template.sh --source ... --check` to preview diff
|
|
|
61
65
|
|
|
62
66
|
The initializer is intentionally flag-driven so the same baseline can be used as a small repository package. Language selection controls CodeQL and records the repository profile; feature selection controls which standard workflows are installed. `auto` detects supported languages, while `all` enables every standard workflow. The resolver profiles the repository for language, package manager, project shape, release type, runners, and cache policy.
|
|
63
67
|
|
|
68
|
+
Existing authored documentation and issue/PR templates are preserved by
|
|
69
|
+
default. Pass `--force` when intentionally refreshing those files from the
|
|
70
|
+
baseline; repository-specific workflows and application files remain protected.
|
|
71
|
+
|
|
64
72
|
```bash
|
|
65
73
|
# Preview a TypeScript + Python repository without changing files
|
|
66
74
|
bash .github/scripts/init-repo.sh \
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -27,6 +27,7 @@ Init/sync options:
|
|
|
27
27
|
--license-file PATH Use an exact custom license file
|
|
28
28
|
--npm-publish Enable npm publication in the release workflow
|
|
29
29
|
--dry-run Preview changes without writing files
|
|
30
|
+
--force Replace protected standard docs/templates
|
|
30
31
|
--prune Remove disabled standard workflows
|
|
31
32
|
--protection Synchronize main branch protections (init only)
|
|
32
33
|
--no-bootstrap Skip mise/hooks/doctor bootstrap after init
|
|
@@ -56,6 +57,7 @@ function parseArgs(argv) {
|
|
|
56
57
|
featuresSet: false,
|
|
57
58
|
dryRun: false,
|
|
58
59
|
prune: false,
|
|
60
|
+
force: false,
|
|
59
61
|
protection: false,
|
|
60
62
|
bootstrap: true,
|
|
61
63
|
}
|
|
@@ -87,6 +89,7 @@ function parseArgs(argv) {
|
|
|
87
89
|
continue
|
|
88
90
|
}
|
|
89
91
|
if (arg === '--dry-run') options.dryRun = true
|
|
92
|
+
else if (arg === '--force') options.force = true
|
|
90
93
|
else if (arg === '--prune') options.prune = true
|
|
91
94
|
else if (arg === '--protection') options.protection = true
|
|
92
95
|
else if (arg === '--no-bootstrap') options.bootstrap = false
|
|
@@ -118,6 +121,7 @@ function main() {
|
|
|
118
121
|
if (options.languagesSet) common.push('--languages', options.languages)
|
|
119
122
|
if (options.featuresSet) common.push('--features', options.features)
|
|
120
123
|
if (options.prune) common.push('--prune')
|
|
124
|
+
if (options.force) common.push('--force')
|
|
121
125
|
if (options.dryRun) common.push('--check')
|
|
122
126
|
else common.push('--apply')
|
|
123
127
|
|
|
@@ -136,6 +140,7 @@ function main() {
|
|
|
136
140
|
if (options.protection) initArgs.push('--protection')
|
|
137
141
|
if (options.dryRun) initArgs.push('--dry-run')
|
|
138
142
|
if (options.prune) initArgs.push('--prune')
|
|
143
|
+
if (options.force) initArgs.push('--force')
|
|
139
144
|
if (options.npmPublish) initArgs.push('--npm-publish')
|
|
140
145
|
if (!options.bootstrap) initArgs.push('--no-bootstrap')
|
|
141
146
|
run('init-repo.sh', initArgs, target)
|