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.
Files changed (37) hide show
  1. package/.githooks/pre-commit +20 -5
  2. package/.github/CODEOWNERS +4 -2
  3. package/.github/CONTRIBUTING.md +5 -1
  4. package/.github/actions/cache/action.yml +43 -0
  5. package/.github/actions/codeql/action.yml +29 -0
  6. package/.github/actions/setup/action.yml +756 -0
  7. package/.github/scripts/bootstrap.sh +7 -0
  8. package/.github/scripts/changed-files.sh +96 -0
  9. package/.github/scripts/ci.sh +421 -28
  10. package/.github/scripts/codeql-languages.sh +71 -5
  11. package/.github/scripts/doctor.sh +1 -1
  12. package/.github/scripts/format-fast-path.sh +80 -0
  13. package/.github/scripts/init-repo.sh +39 -11
  14. package/.github/scripts/profile.sh +179 -0
  15. package/.github/scripts/security.sh +180 -20
  16. package/.github/scripts/sync-codeowners.sh +76 -0
  17. package/.github/scripts/sync-protection.sh +15 -2
  18. package/.github/scripts/sync-template.sh +222 -15
  19. package/.github/scripts/turbo-cache-probe.sh +102 -0
  20. package/.github/template.yml +14 -0
  21. package/.github/template.yml.example +10 -0
  22. package/.github/workflows/ci.yml +108 -7
  23. package/.github/workflows/codeql.yml +126 -17
  24. package/.github/workflows/draft-pr.yml +4 -7
  25. package/.github/workflows/release-pr.yml +20 -7
  26. package/.github/workflows/release.yml +99 -7
  27. package/.github/workflows/security.yml +201 -6
  28. package/.github/workflows/test.yml +100 -3
  29. package/.gitignore +4 -0
  30. package/.mise.toml +0 -2
  31. package/AGENTS.md +1 -0
  32. package/CHANGELOG.md +12 -0
  33. package/README.md +92 -10
  34. package/package.json +11 -1
  35. package/release-please-config.json +16 -0
  36. package/src/cli.mjs +20 -8
  37. 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 contexts+=('Dependency Audit'); fi
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
- languages="auto"
25
- features="all"
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
- package_manager=""
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
- package_manager="$(awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml)"
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
- for file in "${optional_files[@]}"; do
233
- if [ ! -f "$file" ]; then
234
- changed=$((changed + 1))
235
- if [ "$mode" = "check" ]; then
236
- printf 'Would initialize %s\n' "$file"
237
- else
238
- cp "$template_root/$file" "$file"
239
- printf 'Initialized %s\n' "$file"
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
- done
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
@@ -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.event_name == 'pull_request' && github.event.pull_request.number || github.ref }}
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