code-foundry 0.1.3 → 0.2.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 +20 -5
- package/.github/CODEOWNERS +4 -2
- package/.github/CONTRIBUTING.md +5 -1
- package/.github/actions/cache/action.yml +43 -0
- package/.github/actions/codeql/action.yml +29 -0
- package/.github/actions/setup/action.yml +756 -0
- package/.github/scripts/bootstrap.sh +7 -0
- package/.github/scripts/changed-files.sh +96 -0
- package/.github/scripts/ci.sh +421 -28
- package/.github/scripts/codeql-languages.sh +71 -5
- package/.github/scripts/doctor.sh +1 -1
- package/.github/scripts/format-fast-path.sh +80 -0
- package/.github/scripts/init-repo.sh +39 -11
- package/.github/scripts/profile.sh +179 -0
- package/.github/scripts/security.sh +180 -20
- package/.github/scripts/sync-codeowners.sh +76 -0
- package/.github/scripts/sync-protection.sh +15 -2
- package/.github/scripts/sync-template.sh +222 -15
- package/.github/scripts/turbo-cache-probe.sh +102 -0
- package/.github/template.yml +14 -0
- package/.github/template.yml.example +10 -0
- package/.github/workflows/ci.yml +108 -7
- package/.github/workflows/codeql.yml +126 -17
- package/.github/workflows/draft-pr.yml +4 -7
- package/.github/workflows/release-pr.yml +20 -7
- package/.github/workflows/release.yml +99 -7
- package/.github/workflows/security.yml +201 -6
- package/.github/workflows/test.yml +100 -3
- package/.gitignore +4 -0
- package/.mise.toml +0 -2
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +12 -0
- package/README.md +92 -10
- package/package.json +11 -1
- package/release-please-config.json +16 -0
- package/src/cli.mjs +20 -8
- package/.github/workflows/publish.yml +0 -25
|
@@ -49,13 +49,19 @@ done
|
|
|
49
49
|
command -v gh >/dev/null 2>&1 || { echo "gh is required" >&2; exit 1; }
|
|
50
50
|
|
|
51
51
|
is_private="$(gh repo view "$repo" --json isPrivate --jq '.isPrivate')"
|
|
52
|
+
code_security_status="disabled"
|
|
53
|
+
if [ "$is_private" = true ]; then
|
|
54
|
+
code_security_status="$(gh api "repos/$repo" --jq '.security_and_analysis.code_security.status // "disabled"' 2>/dev/null || printf 'disabled')"
|
|
55
|
+
fi
|
|
52
56
|
contexts=()
|
|
53
57
|
if feature_enabled ci; then contexts+=(Format Lint Type-Check Build); fi
|
|
54
58
|
if feature_enabled test; then contexts+=(Unit Integration E2E Smoke); fi
|
|
55
|
-
if feature_enabled security; then
|
|
59
|
+
if feature_enabled security; then
|
|
60
|
+
contexts+=(Profile 'Dependency Audit (JavaScript)' 'Dependency Audit (Rust)' 'Dependency Audit (Python)')
|
|
61
|
+
fi
|
|
56
62
|
if feature_enabled codeql; then contexts+=(Detect); fi
|
|
57
63
|
|
|
58
|
-
if [ "$is_private" != true ] && feature_enabled codeql; then
|
|
64
|
+
if { [ "$is_private" != true ] || [ "$code_security_status" = enabled ]; } && feature_enabled codeql; then
|
|
59
65
|
contexts+=("Analyze (Actions)")
|
|
60
66
|
if language_enabled typescript && git ls-files -- '*.ts' '*.tsx' '*.js' '*.jsx' package.json tsconfig\*.json | grep -q .; then contexts+=("Analyze (TypeScript)"); fi
|
|
61
67
|
if language_enabled python && git ls-files -- '*.py' pyproject.toml requirements\*.txt setup.py ':!.github/**' | grep -q .; then contexts+=("Analyze (Python)"); fi
|
|
@@ -77,6 +83,13 @@ while IFS= read -r context; do
|
|
|
77
83
|
[ -n "$context" ] || continue
|
|
78
84
|
case "$context" in
|
|
79
85
|
'Slither / Analyze') continue ;; # removed from the standard workflow set
|
|
86
|
+
# Remove both the current concise job names and older workflow-prefixed
|
|
87
|
+
# names before rebuilding the standard set. This prevents stale required
|
|
88
|
+
# checks from remaining required after a repository changes visibility or
|
|
89
|
+
# template version.
|
|
90
|
+
'Dependency Audit'|'Dependency Audit ('*|'Profile'|'Test Profile'|\
|
|
91
|
+
'Format'|'Lint'|'Type-Check'|'Build'|'Unit'|'Integration'|'E2E'|'Smoke'|\
|
|
92
|
+
'Detect'|'Analyze'|'Analyze ('*|\
|
|
80
93
|
'CI / '*|'Test / '*|'Security / '*|'CodeQL / '*) ;;
|
|
81
94
|
*) preserved+=("$context") ;;
|
|
82
95
|
esac
|
|
@@ -9,8 +9,10 @@ Synchronize the repository-owned baseline without replacing project-specific
|
|
|
9
9
|
README files, mise tool selections, or additional workflows.
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
|
+
--profile NAME auto, application, monorepo, or minimal
|
|
12
13
|
--languages LIST auto or comma-separated: typescript,rust,python,solidity
|
|
13
14
|
--features LIST all or comma-separated standard features
|
|
15
|
+
--package-manager NAME auto, bun, pnpm, yarn, or npm
|
|
14
16
|
--check Preview changes (default)
|
|
15
17
|
--apply Apply changes
|
|
16
18
|
--prune Remove disabled standard workflows
|
|
@@ -21,17 +23,39 @@ source_ref="main"
|
|
|
21
23
|
mode="check"
|
|
22
24
|
source=""
|
|
23
25
|
temp_dir=""
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
profile="${REPO_FOUNDRY_PROFILE:-auto}"
|
|
27
|
+
languages="${REPO_FOUNDRY_LANGUAGES:-auto}"
|
|
28
|
+
features="${REPO_FOUNDRY_FEATURES:-all}"
|
|
26
29
|
prune=false
|
|
27
30
|
languages_set=false
|
|
31
|
+
profile_set=false
|
|
28
32
|
features_set=false
|
|
29
|
-
|
|
33
|
+
[ -n "${REPO_FOUNDRY_LANGUAGES:-}" ] && languages_set=true
|
|
34
|
+
[ -n "${REPO_FOUNDRY_PROFILE:-}" ] && profile_set=true
|
|
35
|
+
[ -n "${REPO_FOUNDRY_FEATURES:-}" ] && features_set=true
|
|
36
|
+
package_manager="${REPO_FOUNDRY_PACKAGE_MANAGER:-}"
|
|
37
|
+
package_manager_set=false
|
|
38
|
+
[ -n "${REPO_FOUNDRY_PACKAGE_MANAGER:-}" ] && package_manager_set=true
|
|
39
|
+
template_ref=""
|
|
40
|
+
release_type="${REPO_FOUNDRY_RELEASE_TYPE:-auto}"
|
|
41
|
+
npm_publish="${REPO_FOUNDRY_NPM_PUBLISH:-false}"
|
|
42
|
+
release_type_set=false
|
|
43
|
+
npm_publish_set=false
|
|
44
|
+
[ -n "${REPO_FOUNDRY_RELEASE_TYPE:-}" ] && release_type_set=true
|
|
45
|
+
[ -n "${REPO_FOUNDRY_NPM_PUBLISH:-}" ] && npm_publish_set=true
|
|
46
|
+
node_version="24.18.0"
|
|
47
|
+
bun_version="1.3.14"
|
|
48
|
+
python_version="3.13"
|
|
49
|
+
rust_version="1.97.1"
|
|
50
|
+
uv_version="0.11.32"
|
|
51
|
+
ruff_version="0.16.0"
|
|
30
52
|
gitignore_backup=""
|
|
31
53
|
custom_ignores=""
|
|
32
54
|
|
|
33
55
|
valid_languages="typescript rust python solidity"
|
|
34
56
|
valid_features="ci codeql security test draft-pr release-pr release dependabot"
|
|
57
|
+
valid_profiles="auto application monorepo minimal"
|
|
58
|
+
valid_release_types="auto node python rust simple none"
|
|
35
59
|
|
|
36
60
|
contains_word() {
|
|
37
61
|
case " $1 " in *" $2 "*) return 0 ;; *) return 1 ;; esac
|
|
@@ -68,8 +92,10 @@ while [ "$#" -gt 0 ]; do
|
|
|
68
92
|
case "$1" in
|
|
69
93
|
--source) source="${2:?missing source path or URL}"; shift 2 ;;
|
|
70
94
|
--ref) source_ref="${2:?missing ref}"; shift 2 ;;
|
|
95
|
+
--profile) profile="${2:?missing profile}"; profile_set=true; shift 2 ;;
|
|
71
96
|
--languages) languages="${2:?missing language list}"; languages_set=true; shift 2 ;;
|
|
72
97
|
--features) features="${2:?missing feature list}"; features_set=true; shift 2 ;;
|
|
98
|
+
--package-manager) package_manager="${2:?missing package manager}"; package_manager_set=true; shift 2 ;;
|
|
73
99
|
--check) mode="check"; shift ;;
|
|
74
100
|
--apply) mode="apply"; shift ;;
|
|
75
101
|
--prune) prune=true; shift ;;
|
|
@@ -80,6 +106,10 @@ done
|
|
|
80
106
|
|
|
81
107
|
[ -n "$source" ] || { usage >&2; exit 2; }
|
|
82
108
|
if [ -f .github/template.yml ]; then
|
|
109
|
+
if [ "$profile_set" = false ]; then
|
|
110
|
+
configured_profile="$(awk -F': ' '/^profile:/ {print $2; exit}' .github/template.yml)"
|
|
111
|
+
[ -n "$configured_profile" ] && profile="$configured_profile"
|
|
112
|
+
fi
|
|
83
113
|
if [ "$languages_set" = false ]; then
|
|
84
114
|
configured_languages="$(awk -F': ' '/^languages:/ {print $2; exit}' .github/template.yml)"
|
|
85
115
|
[ -n "$configured_languages" ] && languages="$configured_languages"
|
|
@@ -88,10 +118,34 @@ if [ -f .github/template.yml ]; then
|
|
|
88
118
|
configured_features="$(awk -F': ' '/^features:/ {print $2; exit}' .github/template.yml)"
|
|
89
119
|
[ -n "$configured_features" ] && features="$configured_features"
|
|
90
120
|
fi
|
|
91
|
-
|
|
121
|
+
if [ "$package_manager_set" = false ]; then
|
|
122
|
+
package_manager="$(awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml)"
|
|
123
|
+
fi
|
|
124
|
+
template_ref="$(awk -F': ' '/^template:/ {print $2; exit}' .github/template.yml)"
|
|
125
|
+
if [ "$release_type_set" != true ]; then
|
|
126
|
+
configured_release_type="$(awk -F': ' '/^release_type:/ {print $2; exit}' .github/template.yml)"
|
|
127
|
+
[ -n "$configured_release_type" ] && release_type="$configured_release_type"
|
|
128
|
+
fi
|
|
129
|
+
if [ "$npm_publish_set" != true ]; then
|
|
130
|
+
configured_npm_publish="$(awk -F': ' '/^npm_publish:/ {print $2; exit}' .github/template.yml)"
|
|
131
|
+
[ -n "$configured_npm_publish" ] && npm_publish="$configured_npm_publish"
|
|
132
|
+
fi
|
|
92
133
|
fi
|
|
134
|
+
[ -n "$package_manager" ] || package_manager=auto
|
|
135
|
+
case "$package_manager" in
|
|
136
|
+
auto|bun|pnpm|yarn|npm) ;;
|
|
137
|
+
*) printf 'Unsupported package manager: %s\n' "$package_manager" >&2; exit 2 ;;
|
|
138
|
+
esac
|
|
93
139
|
validate_list language "$languages" "$valid_languages"
|
|
94
140
|
validate_list feature "$features" "$valid_features"
|
|
141
|
+
contains_word "$valid_profiles" "$profile" || {
|
|
142
|
+
printf 'Unsupported profile: %s\n' "$profile" >&2
|
|
143
|
+
exit 2
|
|
144
|
+
}
|
|
145
|
+
contains_word "$valid_release_types" "$release_type" || {
|
|
146
|
+
printf 'Unsupported release type: %s\n' "$release_type" >&2
|
|
147
|
+
exit 2
|
|
148
|
+
}
|
|
95
149
|
|
|
96
150
|
if [ -d "$source" ] && [ -f "$source/.github/scripts/sync-template.sh" ]; then
|
|
97
151
|
template_root="$source"
|
|
@@ -102,10 +156,36 @@ else
|
|
|
102
156
|
template_root="$temp_dir/template-repo"
|
|
103
157
|
fi
|
|
104
158
|
|
|
159
|
+
# Resolve the effective profile after the template is available. This gives
|
|
160
|
+
# CLI flags and GitHub repository variables priority over template defaults,
|
|
161
|
+
# while making automatic detection concrete in the generated profile.
|
|
162
|
+
if [ -f "$template_root/.github/scripts/profile.sh" ]; then
|
|
163
|
+
profile_output="$(
|
|
164
|
+
REPO_FOUNDRY_PROFILE="$profile" \
|
|
165
|
+
REPO_FOUNDRY_LANGUAGES="$languages" \
|
|
166
|
+
REPO_FOUNDRY_FEATURES="$features" \
|
|
167
|
+
REPO_FOUNDRY_PACKAGE_MANAGER="$package_manager" \
|
|
168
|
+
REPO_FOUNDRY_RELEASE_TYPE="$release_type" \
|
|
169
|
+
REPO_FOUNDRY_NPM_PUBLISH="$npm_publish" \
|
|
170
|
+
bash "$template_root/.github/scripts/profile.sh" detect --root "$PWD"
|
|
171
|
+
)"
|
|
172
|
+
profile="$(printf '%s\n' "$profile_output" | awk -F= '$1 == "profile" {print substr($0, index($0, "=") + 1)}')"
|
|
173
|
+
languages="$(printf '%s\n' "$profile_output" | awk -F= '$1 == "languages" {print substr($0, index($0, "=") + 1)}')"
|
|
174
|
+
package_manager="$(printf '%s\n' "$profile_output" | awk -F= '$1 == "package_manager" {print substr($0, index($0, "=") + 1)}')"
|
|
175
|
+
release_type="$(printf '%s\n' "$profile_output" | awk -F= '$1 == "release_type" {print substr($0, index($0, "=") + 1)}')"
|
|
176
|
+
npm_publish="$(printf '%s\n' "$profile_output" | awk -F= '$1 == "npm_publish" {print substr($0, index($0, "=") + 1)}')"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
if [ -f "$template_root/package.json" ]; then
|
|
180
|
+
template_version="$(awk -F'"' '/"version"[[:space:]]*:/ {print $4; exit}' "$template_root/package.json")"
|
|
181
|
+
[ -n "$template_version" ] && template_ref="code-foundry@$template_version"
|
|
182
|
+
fi
|
|
183
|
+
|
|
105
184
|
files=(
|
|
106
185
|
.editorconfig
|
|
107
186
|
.gitattributes
|
|
108
187
|
.gitignore
|
|
188
|
+
release-please-config.json
|
|
109
189
|
.githooks/pre-commit
|
|
110
190
|
AGENTS.md
|
|
111
191
|
LICENSE
|
|
@@ -123,7 +203,11 @@ files=(
|
|
|
123
203
|
.github/ISSUE_TEMPLATE/config.yml
|
|
124
204
|
.github/ISSUE_TEMPLATE/feature_request.yml
|
|
125
205
|
.github/actions/setup/action.yml
|
|
206
|
+
.github/actions/cache/action.yml
|
|
207
|
+
.github/actions/codeql/action.yml
|
|
208
|
+
.github/scripts/profile.sh
|
|
126
209
|
.github/scripts/bootstrap.sh
|
|
210
|
+
.github/scripts/changed-files.sh
|
|
127
211
|
.github/scripts/ci.sh
|
|
128
212
|
.github/scripts/codeql-languages.sh
|
|
129
213
|
.github/scripts/doctor.sh
|
|
@@ -131,6 +215,7 @@ files=(
|
|
|
131
215
|
.github/scripts/sitecustomize.py
|
|
132
216
|
.github/scripts/sync-template.sh
|
|
133
217
|
.github/scripts/init-repo.sh
|
|
218
|
+
.github/scripts/sync-codeowners.sh
|
|
134
219
|
.github/scripts/sync-protection.sh
|
|
135
220
|
.github/workflows/ci.yml
|
|
136
221
|
.github/workflows/codeql.yml
|
|
@@ -157,8 +242,6 @@ for file in "${files[@]}"; do
|
|
|
157
242
|
done
|
|
158
243
|
files=("${filtered_files[@]}")
|
|
159
244
|
|
|
160
|
-
optional_files=(.mise.toml)
|
|
161
|
-
|
|
162
245
|
# Workflows outside the standard baseline are repository-owned extensions.
|
|
163
246
|
# The sync operation never deletes or replaces them; surface them explicitly
|
|
164
247
|
# so maintainers can verify custom deployment, indexing, or security flows.
|
|
@@ -193,6 +276,9 @@ for file in "${files[@]}"; do
|
|
|
193
276
|
echo "Template file missing: $file" >&2
|
|
194
277
|
exit 1
|
|
195
278
|
fi
|
|
279
|
+
if [ "$file" = .github/CODEOWNERS ] && [ -f "$file" ]; then
|
|
280
|
+
continue
|
|
281
|
+
fi
|
|
196
282
|
if [ ! -f "$file" ] || ! cmp -s "$template_file" "$file"; then
|
|
197
283
|
changed=$((changed + 1))
|
|
198
284
|
if [ "$mode" = "check" ]; then
|
|
@@ -229,28 +315,149 @@ for file in "${files[@]}"; do
|
|
|
229
315
|
fi
|
|
230
316
|
done
|
|
231
317
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
318
|
+
# Release Please owns changelog updates after initialization. Preserve an
|
|
319
|
+
# existing project history, but give new repositories the expected file.
|
|
320
|
+
if [ ! -f CHANGELOG.md ]; then
|
|
321
|
+
changed=$((changed + 1))
|
|
322
|
+
if [ "$mode" = "check" ]; then
|
|
323
|
+
printf '%s\n' 'Would initialize CHANGELOG.md'
|
|
324
|
+
else
|
|
325
|
+
cat > CHANGELOG.md <<'EOF'
|
|
326
|
+
# Changelog
|
|
327
|
+
|
|
328
|
+
All notable changes to this project are documented here.
|
|
329
|
+
EOF
|
|
330
|
+
printf '%s\n' 'Initialized CHANGELOG.md'
|
|
331
|
+
fi
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
detect_languages() {
|
|
335
|
+
local detected=()
|
|
336
|
+
if [ -f package.json ] || git ls-files -- '*.js' '*.jsx' '*.ts' '*.tsx' | grep -q .; then
|
|
337
|
+
detected+=(typescript)
|
|
338
|
+
fi
|
|
339
|
+
if [ -f Cargo.toml ] || git ls-files -- '*.rs' | grep -q .; then
|
|
340
|
+
detected+=(rust)
|
|
341
|
+
fi
|
|
342
|
+
if [ -f pyproject.toml ] || [ -f requirements.txt ] || [ -f requirements-dev.txt ] ||
|
|
343
|
+
git ls-files -- '*.py' ':!.github/**' | grep -q .; then
|
|
344
|
+
detected+=(python)
|
|
345
|
+
fi
|
|
346
|
+
if find . -path './.git' -prune -o -type f -name '*.sol' -print | grep -q .; then
|
|
347
|
+
detected+=(solidity)
|
|
348
|
+
fi
|
|
349
|
+
printf '%s\n' "${detected[*]}"
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
initialize_mise() {
|
|
353
|
+
local selected_languages="$1"
|
|
354
|
+
local language selected_package_manager
|
|
355
|
+
if [ -f .mise.toml ]; then
|
|
356
|
+
return
|
|
357
|
+
fi
|
|
358
|
+
if [ "$selected_languages" = auto ]; then
|
|
359
|
+
selected_languages="$(detect_languages)"
|
|
360
|
+
fi
|
|
361
|
+
selected_package_manager="$package_manager"
|
|
362
|
+
if [ "$selected_package_manager" = auto ]; then
|
|
363
|
+
if [ -f bun.lock ] || [ -f bun.lockb ]; then selected_package_manager=bun
|
|
364
|
+
elif [ -f pnpm-lock.yaml ]; then selected_package_manager=pnpm
|
|
365
|
+
elif [ -f yarn.lock ]; then selected_package_manager=yarn
|
|
366
|
+
elif [ -f package-lock.json ]; then selected_package_manager=npm
|
|
367
|
+
else selected_package_manager=bun
|
|
240
368
|
fi
|
|
241
369
|
fi
|
|
242
|
-
|
|
370
|
+
changed=$((changed + 1))
|
|
371
|
+
if [ "$mode" = check ]; then
|
|
372
|
+
printf '%s\n' 'Would initialize .mise.toml'
|
|
373
|
+
return
|
|
374
|
+
fi
|
|
375
|
+
{
|
|
376
|
+
printf '%s\n' '[tools]'
|
|
377
|
+
for language in $(normalize_csv "$selected_languages"); do
|
|
378
|
+
case "$language" in
|
|
379
|
+
typescript|solidity)
|
|
380
|
+
printf 'node = "%s"\n' "$node_version"
|
|
381
|
+
if [ "$selected_package_manager" = bun ]; then
|
|
382
|
+
printf 'bun = "%s"\n' "$bun_version"
|
|
383
|
+
fi
|
|
384
|
+
;;
|
|
385
|
+
python)
|
|
386
|
+
printf 'python = "%s"\n' "$python_version"
|
|
387
|
+
printf 'uv = "%s"\n' "$uv_version"
|
|
388
|
+
printf 'ruff = "%s"\n' "$ruff_version"
|
|
389
|
+
;;
|
|
390
|
+
rust)
|
|
391
|
+
printf 'rust = { version = "%s", components = ["rustfmt", "clippy"] }\n' "$rust_version"
|
|
392
|
+
;;
|
|
393
|
+
esac
|
|
394
|
+
done | awk '!seen[$0]++'
|
|
395
|
+
printf '\n%s\n' '[settings]'
|
|
396
|
+
printf '%s\n' 'experimental = true'
|
|
397
|
+
} > .mise.toml
|
|
398
|
+
printf '%s\n' 'Initialized .mise.toml'
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
initialize_mise "$languages"
|
|
402
|
+
|
|
403
|
+
initialize_mise_lock() {
|
|
404
|
+
if [ ! -f .mise.toml ] || [ -f mise.lock ]; then
|
|
405
|
+
return
|
|
406
|
+
fi
|
|
407
|
+
if ! command -v mise >/dev/null 2>&1; then
|
|
408
|
+
return
|
|
409
|
+
fi
|
|
410
|
+
changed=$((changed + 1))
|
|
411
|
+
if [ "$mode" = check ]; then
|
|
412
|
+
printf '%s\n' 'Would initialize mise.lock'
|
|
413
|
+
return
|
|
414
|
+
fi
|
|
415
|
+
if MISE_TRUSTED_CONFIG_PATHS="$PWD" mise lock >/dev/null 2>&1; then
|
|
416
|
+
printf '%s\n' 'Initialized mise.lock'
|
|
417
|
+
else
|
|
418
|
+
printf '%s\n' 'Warning: mise.lock could not be generated; CI will resolve pinned tools normally.' >&2
|
|
419
|
+
fi
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
initialize_mise_lock
|
|
423
|
+
|
|
424
|
+
if [ -x .github/scripts/sync-codeowners.sh ]; then
|
|
425
|
+
if [ "$mode" = "check" ]; then
|
|
426
|
+
bash .github/scripts/sync-codeowners.sh --check
|
|
427
|
+
else
|
|
428
|
+
bash .github/scripts/sync-codeowners.sh --apply
|
|
429
|
+
fi
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
# Release Please's simple strategy needs a version file. Initialize one only
|
|
433
|
+
# for Solidity-only repositories, where no package manifest owns the version.
|
|
434
|
+
if [ ! -f version.txt ] && find . -path './.git' -prune -o -type f -name '*.sol' -print | grep -q . &&
|
|
435
|
+
[ ! -f package.json ] && [ ! -f Cargo.toml ] && [ ! -f pyproject.toml ]; then
|
|
436
|
+
changed=$((changed + 1))
|
|
437
|
+
if [ "$mode" = "check" ]; then
|
|
438
|
+
printf '%s\n' 'Would initialize version.txt'
|
|
439
|
+
else
|
|
440
|
+
printf '%s\n' '0.1.0' > version.txt
|
|
441
|
+
printf '%s\n' 'Initialized version.txt'
|
|
442
|
+
fi
|
|
443
|
+
fi
|
|
243
444
|
|
|
244
445
|
if [ "$mode" = "apply" ]; then
|
|
245
446
|
git config core.hooksPath .githooks
|
|
246
447
|
mkdir -p .github
|
|
247
448
|
{
|
|
248
449
|
printf 'version: 1\n'
|
|
450
|
+
if [ -n "$template_ref" ]; then
|
|
451
|
+
printf 'template: %s\n' "$template_ref"
|
|
452
|
+
fi
|
|
453
|
+
printf 'profile: %s\n' "$profile"
|
|
249
454
|
printf 'languages: %s\n' "$languages"
|
|
250
455
|
printf 'features: %s\n' "$features"
|
|
251
456
|
if [ -n "$package_manager" ]; then
|
|
252
457
|
printf 'package_manager: %s\n' "$package_manager"
|
|
253
458
|
fi
|
|
459
|
+
printf 'release_type: %s\n' "$release_type"
|
|
460
|
+
printf 'npm_publish: %s\n' "$npm_publish"
|
|
254
461
|
} > .github/template.yml
|
|
255
462
|
fi
|
|
256
463
|
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# A Turbo remote-cache hit can be resolved from package manifests alone. This
|
|
5
|
+
# probe lets CI skip the full dependency install when every executable task is
|
|
6
|
+
# already cached. It is deliberately conservative: only Bun projects with a
|
|
7
|
+
# pinned Turbo version are eligible, and synthetic dependency-only tasks are
|
|
8
|
+
# excluded from the hit decision.
|
|
9
|
+
|
|
10
|
+
task="${1:?task name required}"
|
|
11
|
+
|
|
12
|
+
set_output() {
|
|
13
|
+
printf '%s=%s\n' "$1" "$2" >> "${GITHUB_OUTPUT:-/dev/null}"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
set_output hit false
|
|
17
|
+
|
|
18
|
+
[ -n "${TURBO_TOKEN:-}" ] || exit 0
|
|
19
|
+
[ -n "${TURBO_TEAM:-}" ] || exit 0
|
|
20
|
+
[ -f package.json ] || exit 0
|
|
21
|
+
[ -f bun.lock ] || [ -f bun.lockb ] || exit 0
|
|
22
|
+
|
|
23
|
+
turbo_version="$(node <<'NODE'
|
|
24
|
+
const fs = require('node:fs');
|
|
25
|
+
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
26
|
+
const groups = [
|
|
27
|
+
packageJson.devDependencies,
|
|
28
|
+
packageJson.dependencies,
|
|
29
|
+
packageJson.optionalDependencies,
|
|
30
|
+
packageJson.pnpm?.overrides,
|
|
31
|
+
];
|
|
32
|
+
for (const group of groups) {
|
|
33
|
+
const value = group?.turbo;
|
|
34
|
+
const match = typeof value === 'string' && value.match(/(\d+\.\d+\.\d+)/);
|
|
35
|
+
if (match) {
|
|
36
|
+
console.log(match[1]);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
NODE
|
|
41
|
+
)"
|
|
42
|
+
[ -n "$turbo_version" ] || exit 0
|
|
43
|
+
|
|
44
|
+
turbo_home="${RUNNER_TEMP:-/tmp}/repo-foundry-turbo"
|
|
45
|
+
probe_file="$(mktemp)"
|
|
46
|
+
install_file="$(mktemp)"
|
|
47
|
+
result_file="$(mktemp)"
|
|
48
|
+
cleanup() { rm -f "$probe_file" "$install_file" "$result_file"; }
|
|
49
|
+
trap cleanup EXIT
|
|
50
|
+
|
|
51
|
+
if ! npm install --prefix "$turbo_home" --no-save --ignore-scripts --no-audit --no-fund \
|
|
52
|
+
"turbo@${turbo_version}" >"$install_file" 2>&1; then
|
|
53
|
+
echo "Turbo Probe: CLI unavailable; using full install"
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
turbo_bin="$turbo_home/node_modules/.bin/turbo"
|
|
58
|
+
if [ ! -x "$turbo_bin" ]; then
|
|
59
|
+
echo "Turbo Probe: CLI unavailable; using full install"
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if ! "$turbo_bin" run "$task" \
|
|
64
|
+
--dry=json --cache=remote:r --output-logs=none >"$probe_file" 2>&1; then
|
|
65
|
+
echo "Turbo Probe: CLI unavailable; using full install"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
node - "$probe_file" >"$result_file" <<'NODE'
|
|
70
|
+
const fs = require('node:fs');
|
|
71
|
+
const file = process.argv[2];
|
|
72
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
73
|
+
const start = text.indexOf('{');
|
|
74
|
+
if (start < 0) {
|
|
75
|
+
console.log('invalid-json');
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
let report;
|
|
79
|
+
try {
|
|
80
|
+
report = JSON.parse(text.slice(start));
|
|
81
|
+
} catch {
|
|
82
|
+
console.log('invalid-json');
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
const runnable = (report.tasks || []).filter((entry) =>
|
|
86
|
+
entry.command !== '<NONEXISTENT>' && entry.resolvedTaskDefinition?.cache !== false,
|
|
87
|
+
);
|
|
88
|
+
const hits = runnable.filter((entry) => entry.cache?.status === 'HIT').length;
|
|
89
|
+
const hit = runnable.length > 0 && hits === runnable.length;
|
|
90
|
+
console.log(`tasks=${report.tasks?.length || 0} runnable=${runnable.length} hits=${hits} hit=${hit}`);
|
|
91
|
+
NODE
|
|
92
|
+
probe_result="$(cat "$result_file")"
|
|
93
|
+
echo "Turbo Probe: $probe_result"
|
|
94
|
+
if ! grep -q 'hit=true' <<<"$probe_result"; then
|
|
95
|
+
echo "Turbo Probe: remote cache incomplete; using full install"
|
|
96
|
+
exit 0
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
turbo_bin_dir="${turbo_bin%/*}"
|
|
100
|
+
echo "$turbo_bin_dir" >> "${GITHUB_PATH:-/dev/null}"
|
|
101
|
+
printf 'REPO_FOUNDRY_TURBO_REMOTE_ONLY=true\n' >> "${GITHUB_ENV:-/dev/null}"
|
|
102
|
+
set_output hit true
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
template: code-foundry@latest
|
|
3
|
+
profile: auto
|
|
4
|
+
languages: typescript
|
|
5
|
+
features: all
|
|
6
|
+
package_manager: bun
|
|
7
|
+
release_type: node
|
|
8
|
+
npm_publish: true
|
|
9
|
+
runner: ubuntu-latest
|
|
10
|
+
unit_runner: ubuntu-slim
|
|
11
|
+
cache_packages: auto
|
|
12
|
+
cache_build: auto
|
|
13
|
+
coverage_minimum: 80
|
|
14
|
+
turbo_remote: auto
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Repository-specific settings for the shared initializer and sync scripts.
|
|
2
2
|
version: 1
|
|
3
|
+
template: code-foundry@latest
|
|
4
|
+
profile: auto
|
|
3
5
|
languages: auto
|
|
4
6
|
features: all
|
|
5
7
|
package_manager: auto
|
|
8
|
+
release_type: auto
|
|
9
|
+
npm_publish: false
|
|
10
|
+
runner: ubuntu-latest
|
|
11
|
+
unit_runner: ubuntu-slim
|
|
12
|
+
cache_packages: auto
|
|
13
|
+
cache_build: auto
|
|
14
|
+
coverage_minimum: 80
|
|
15
|
+
turbo_remote: auto
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -13,60 +13,161 @@ permissions:
|
|
|
13
13
|
env:
|
|
14
14
|
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
|
15
15
|
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
|
16
|
+
REPO_FOUNDRY_PROFILE: ${{ vars.REPO_FOUNDRY_PROFILE }}
|
|
17
|
+
REPO_FOUNDRY_LANGUAGES: ${{ vars.REPO_FOUNDRY_LANGUAGES }}
|
|
18
|
+
REPO_FOUNDRY_FEATURES: ${{ vars.REPO_FOUNDRY_FEATURES }}
|
|
19
|
+
REPO_FOUNDRY_PACKAGE_MANAGER: ${{ vars.REPO_FOUNDRY_PACKAGE_MANAGER }}
|
|
20
|
+
REPO_FOUNDRY_RUNNER: ${{ vars.REPO_FOUNDRY_RUNNER }}
|
|
21
|
+
REPO_FOUNDRY_CACHE_PACKAGES: ${{ vars.REPO_FOUNDRY_CACHE_PACKAGES || 'auto' }}
|
|
22
|
+
REPO_FOUNDRY_CACHE_BUILD: ${{ vars.REPO_FOUNDRY_CACHE_BUILD || 'auto' }}
|
|
23
|
+
REPO_FOUNDRY_BASE_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before || '' }}
|
|
16
24
|
|
|
17
25
|
concurrency:
|
|
18
|
-
group: ${{ github.workflow }}-${{ github.
|
|
26
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.event.pull_request.head.ref || github.ref_name }}
|
|
19
27
|
cancel-in-progress: true
|
|
20
28
|
|
|
21
29
|
jobs:
|
|
22
30
|
format:
|
|
23
31
|
name: Format
|
|
24
|
-
runs-on: ubuntu-latest
|
|
32
|
+
runs-on: ${{ vars.REPO_FOUNDRY_RUNNER || vars.REPO_FOUNDRY_FAST_RUNNER || 'ubuntu-latest' }}
|
|
33
|
+
timeout-minutes: 15
|
|
25
34
|
steps:
|
|
26
35
|
- name: Checkout
|
|
27
36
|
uses: actions/checkout@v7
|
|
37
|
+
with:
|
|
38
|
+
fetch-depth: 2
|
|
39
|
+
- name: Detect
|
|
40
|
+
id: applicability
|
|
41
|
+
run: bash .github/scripts/ci.sh should_run format >> "$GITHUB_OUTPUT"
|
|
42
|
+
- name: Format Probe
|
|
43
|
+
id: format-probe
|
|
44
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
45
|
+
run: bash .github/scripts/format-fast-path.sh
|
|
28
46
|
- name: Setup
|
|
47
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
29
48
|
uses: ./.github/actions/setup
|
|
30
49
|
with:
|
|
31
|
-
install: true
|
|
50
|
+
install: ${{ steps.format-probe.outputs.hit != 'true' }}
|
|
51
|
+
install-python: false
|
|
52
|
+
install-rust: false
|
|
53
|
+
cache-build: false
|
|
54
|
+
# Warm the format cache on branch pushes without adding archive
|
|
55
|
+
# time to pull-request checks.
|
|
56
|
+
cache-save: ${{ github.event_name == 'push' }}
|
|
57
|
+
cache-format: true
|
|
58
|
+
task: format
|
|
32
59
|
- name: Format
|
|
60
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
61
|
+
id: format-fast
|
|
62
|
+
continue-on-error: ${{ steps.format-probe.outputs.hit == 'true' }}
|
|
63
|
+
run: bash .github/scripts/ci.sh format
|
|
64
|
+
- name: Setup Fallback
|
|
65
|
+
if: >-
|
|
66
|
+
steps.applicability.outputs.applicable == 'true' &&
|
|
67
|
+
steps.format-probe.outputs.hit == 'true' &&
|
|
68
|
+
steps.format-fast.outcome == 'failure'
|
|
69
|
+
uses: ./.github/actions/setup
|
|
70
|
+
with:
|
|
71
|
+
install: true
|
|
72
|
+
install-python: false
|
|
73
|
+
install-rust: false
|
|
74
|
+
cache-build: false
|
|
75
|
+
cache-save: false
|
|
76
|
+
cache-format: true
|
|
77
|
+
task: format
|
|
78
|
+
- name: Format Fallback
|
|
79
|
+
if: >-
|
|
80
|
+
steps.applicability.outputs.applicable == 'true' &&
|
|
81
|
+
steps.format-probe.outputs.hit == 'true' &&
|
|
82
|
+
steps.format-fast.outcome == 'failure'
|
|
33
83
|
run: bash .github/scripts/ci.sh format
|
|
34
84
|
|
|
35
85
|
lint:
|
|
36
86
|
name: Lint
|
|
37
|
-
runs-on: ubuntu-latest
|
|
87
|
+
runs-on: ${{ vars.REPO_FOUNDRY_RUNNER || vars.REPO_FOUNDRY_FAST_RUNNER || 'ubuntu-latest' }}
|
|
88
|
+
timeout-minutes: 20
|
|
38
89
|
steps:
|
|
39
90
|
- name: Checkout
|
|
40
91
|
uses: actions/checkout@v7
|
|
92
|
+
with:
|
|
93
|
+
fetch-depth: 2
|
|
94
|
+
- name: Detect
|
|
95
|
+
id: applicability
|
|
96
|
+
run: bash .github/scripts/ci.sh should_run lint >> "$GITHUB_OUTPUT"
|
|
97
|
+
- name: Turbo Probe
|
|
98
|
+
id: turbo
|
|
99
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
100
|
+
run: bash .github/scripts/turbo-cache-probe.sh lint
|
|
41
101
|
- name: Setup
|
|
102
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
42
103
|
uses: ./.github/actions/setup
|
|
43
104
|
with:
|
|
44
|
-
install: true
|
|
105
|
+
install: ${{ steps.turbo.outputs.hit != 'true' }}
|
|
106
|
+
install-python: false
|
|
107
|
+
cache-build: false
|
|
108
|
+
# Warm the lint cache on branch pushes without adding archive time
|
|
109
|
+
# to pull-request checks.
|
|
110
|
+
cache-save: ${{ github.event_name == 'push' }}
|
|
111
|
+
cache-lint: true
|
|
112
|
+
task: lint
|
|
45
113
|
- name: Lint
|
|
114
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
46
115
|
run: bash .github/scripts/ci.sh lint
|
|
47
116
|
|
|
48
117
|
type-check:
|
|
49
118
|
name: Type-Check
|
|
50
119
|
runs-on: ubuntu-latest
|
|
120
|
+
timeout-minutes: 20
|
|
51
121
|
steps:
|
|
52
122
|
- name: Checkout
|
|
53
123
|
uses: actions/checkout@v7
|
|
124
|
+
with:
|
|
125
|
+
fetch-depth: 2
|
|
126
|
+
- name: Detect
|
|
127
|
+
id: applicability
|
|
128
|
+
run: bash .github/scripts/ci.sh should_run type_check >> "$GITHUB_OUTPUT"
|
|
129
|
+
- name: Turbo Probe
|
|
130
|
+
id: turbo
|
|
131
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
132
|
+
run: bash .github/scripts/turbo-cache-probe.sh type-check
|
|
54
133
|
- name: Setup
|
|
134
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
55
135
|
uses: ./.github/actions/setup
|
|
56
136
|
with:
|
|
57
|
-
install: true
|
|
137
|
+
install: ${{ steps.turbo.outputs.hit != 'true' }}
|
|
138
|
+
install-python: false
|
|
139
|
+
cache-build: ${{ steps.applicability.outputs.rust == 'true' || vars.REPO_FOUNDRY_CACHE_BUILD == 'true' }}
|
|
140
|
+
cache-save: ${{ github.event_name == 'push' }}
|
|
141
|
+
task: type_check
|
|
58
142
|
- name: Type-Check
|
|
143
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
59
144
|
run: bash .github/scripts/ci.sh type_check
|
|
60
145
|
|
|
61
146
|
build:
|
|
62
147
|
name: Build
|
|
63
148
|
runs-on: ubuntu-latest
|
|
149
|
+
timeout-minutes: 30
|
|
64
150
|
steps:
|
|
65
151
|
- name: Checkout
|
|
66
152
|
uses: actions/checkout@v7
|
|
153
|
+
with:
|
|
154
|
+
fetch-depth: 2
|
|
155
|
+
- name: Detect
|
|
156
|
+
id: applicability
|
|
157
|
+
run: bash .github/scripts/ci.sh should_run build >> "$GITHUB_OUTPUT"
|
|
158
|
+
- name: Turbo Probe
|
|
159
|
+
id: turbo
|
|
160
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
161
|
+
run: bash .github/scripts/turbo-cache-probe.sh build
|
|
67
162
|
- name: Setup
|
|
163
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
68
164
|
uses: ./.github/actions/setup
|
|
69
165
|
with:
|
|
70
|
-
install: true
|
|
166
|
+
install: ${{ steps.turbo.outputs.hit != 'true' }}
|
|
167
|
+
install-python: false
|
|
168
|
+
cache-build: ${{ steps.applicability.outputs.rust == 'true' || vars.REPO_FOUNDRY_CACHE_BUILD == 'true' }}
|
|
169
|
+
cache-save: ${{ github.event_name == 'push' }}
|
|
170
|
+
task: build
|
|
71
171
|
- name: Build
|
|
172
|
+
if: steps.applicability.outputs.applicable == 'true'
|
|
72
173
|
run: bash .github/scripts/ci.sh build
|