code-foundry 0.26.0 → 0.27.1
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 +3 -4
- package/.github/CONTRIBUTING.md +10 -13
- package/.github/actions/setup/action.yml +68 -3
- package/.github/code-foundry.yml +6 -1
- package/.github/workflows/ci.yml +29 -24
- package/.github/workflows/ci_self-ci.yml +1 -1
- package/.github/workflows/codeql.yml +23 -29
- package/.github/workflows/codeql_self-ci.yml +1 -1
- package/.github/workflows/draft-pr.yml +2 -2
- package/.github/workflows/release-pr.yml +4 -4
- package/.github/workflows/release.yml +17 -29
- package/.github/workflows/security.yml +24 -19
- package/.github/workflows/security_self-ci.yml +1 -1
- package/.github/workflows/test.yml +20 -20
- package/.github/workflows/test_self-ci.yml +1 -1
- package/AGENTS.md +13 -10
- package/CHANGELOG.md +29 -0
- package/README.md +16 -6
- package/docs/CONFIGURATION.md +10 -1
- package/docs/INITIALIZATION.md +6 -5
- package/docs/RELEASES.md +5 -0
- package/docs/WORKFLOWS.md +12 -5
- package/package.json +7 -3
- package/src/cli.mjs +29 -25
- package/src/commands/doctor.mjs +70 -0
- package/src/commands/sync.mjs +224 -0
- package/src/lib/config.mjs +38 -0
- package/src/lib/profile.mjs +114 -0
- package/src/runtime.mjs +321 -0
- package/.github/scripts/bootstrap.sh +0 -21
- package/.github/scripts/changed-files.sh +0 -96
- package/.github/scripts/ci.sh +0 -641
- package/.github/scripts/codeql-languages.sh +0 -91
- package/.github/scripts/doctor.sh +0 -98
- package/.github/scripts/format-fast-path.sh +0 -80
- package/.github/scripts/init-repo.sh +0 -268
- package/.github/scripts/pre-commit.sh +0 -67
- package/.github/scripts/profile.sh +0 -186
- package/.github/scripts/security.sh +0 -214
- package/.github/scripts/sitecustomize.py +0 -17
- package/.github/scripts/sync-codeowners.sh +0 -76
- package/.github/scripts/sync-protection.sh +0 -138
- package/.github/scripts/sync-template.sh +0 -748
- package/.github/scripts/turbo-cache-probe.sh +0 -102
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
languages=()
|
|
5
|
-
changed_files=""
|
|
6
|
-
|
|
7
|
-
# Scheduled and manually dispatched scans are always full scans. For pushes
|
|
8
|
-
# and pull requests, a shallow commit diff lets the matrix skip analyzers for
|
|
9
|
-
# languages untouched by the change while keeping every matrix check visible.
|
|
10
|
-
case "${GITHUB_EVENT_NAME:-}" in
|
|
11
|
-
schedule|workflow_dispatch) ;;
|
|
12
|
-
*)
|
|
13
|
-
base_sha="${CODEQL_BASE_SHA:-}"
|
|
14
|
-
if [ -n "$base_sha" ] && [ "$base_sha" != "0000000000000000000000000000000000000000" ]; then
|
|
15
|
-
if ! git cat-file -e "$base_sha^{commit}" 2>/dev/null; then
|
|
16
|
-
git fetch --no-tags --filter=blob:none --depth=1 origin "$base_sha" >/dev/null 2>&1 || true
|
|
17
|
-
fi
|
|
18
|
-
changed_files="$(git diff --name-only "$base_sha" "${GITHUB_SHA:-HEAD}" 2>/dev/null || true)"
|
|
19
|
-
fi
|
|
20
|
-
[ -n "$changed_files" ] || changed_files="$(git diff --name-only HEAD^ HEAD 2>/dev/null || true)"
|
|
21
|
-
;;
|
|
22
|
-
esac
|
|
23
|
-
|
|
24
|
-
if find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) -print -quit | grep -q .; then languages+=(actions); fi
|
|
25
|
-
configured=""
|
|
26
|
-
if [ -x .github/scripts/profile.sh ]; then
|
|
27
|
-
configured="$(bash .github/scripts/profile.sh get languages 2>/dev/null || true)"
|
|
28
|
-
else
|
|
29
|
-
config_file=.github/code-foundry.yml
|
|
30
|
-
if [ -f "$config_file" ]; then
|
|
31
|
-
configured="$(awk -F': ' '/^languages:/ {print $2; exit}' "$config_file")"
|
|
32
|
-
fi
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
if [ "$configured" = auto ] || [ "$configured" = all ] || [ -z "$configured" ]; then
|
|
36
|
-
tracked_files="$(git ls-files)"
|
|
37
|
-
if printf '%s\n' "$tracked_files" | grep -Eq '(^|/)([^/]+\.(ts|tsx|js|jsx)|package\.json|tsconfig[^/]*\.json)$'; then
|
|
38
|
-
languages+=(javascript-typescript)
|
|
39
|
-
fi
|
|
40
|
-
if printf '%s\n' "$tracked_files" | awk '!/^\.github\// && /(^|\/)([^\/]+\.py|pyproject\.toml|requirements[^\/]*\.txt|setup\.py)$/ { found=1; exit } END { exit !found }'; then
|
|
41
|
-
languages+=(python)
|
|
42
|
-
fi
|
|
43
|
-
if printf '%s\n' "$tracked_files" | grep -Eq '(^|/)([^/]+\.rs|Cargo\.toml|Cargo\.lock)$'; then
|
|
44
|
-
languages+=(rust)
|
|
45
|
-
fi
|
|
46
|
-
else
|
|
47
|
-
case ",$configured," in *,typescript,*) languages+=(javascript-typescript) ;; esac
|
|
48
|
-
case ",$configured," in *,python,*) languages+=(python) ;; esac
|
|
49
|
-
case ",$configured," in *,rust,*) languages+=(rust) ;; esac
|
|
50
|
-
fi
|
|
51
|
-
|
|
52
|
-
changed_json="$(printf '%s\n' "$changed_files" | jq -Rsc 'split("\n") | map(select(length > 0))')"
|
|
53
|
-
json=$(printf '%s\n' "${languages[@]}" | jq -Rsc --argjson changed "$changed_json" '
|
|
54
|
-
split("\n")
|
|
55
|
-
| map(select(length > 0) | {
|
|
56
|
-
language: .,
|
|
57
|
-
name: (if . == "javascript-typescript" then "TypeScript" elif . == "actions" then "Actions" else (. | ascii_upcase[0:1] + .[1:]) end),
|
|
58
|
-
"build-mode": "none",
|
|
59
|
-
changed: (
|
|
60
|
-
if ($changed | length) == 0 then true
|
|
61
|
-
elif . == "actions" then any($changed[]; test("(^|/)(\\.github/workflows/|action\\.yml$)"))
|
|
62
|
-
elif . == "javascript-typescript" then any($changed[]; test("(^|/)(.*\\.(js|jsx|mjs|cjs|ts|tsx|mts|cts)|package\\.json|tsconfig[^/]*\\.json|((bun|pnpm|yarn|package-lock)\\.lock))$"))
|
|
63
|
-
elif . == "python" then any($changed[]; test("(^|/)(.*\\.py|pyproject\\.toml|requirements[^/]*\\.txt|setup\\.py|Pipfile\\.lock|poetry\\.lock)$"))
|
|
64
|
-
elif . == "rust" then any($changed[]; test("(^|/)(.*\\.rs|Cargo\\.toml|Cargo\\.lock)$"))
|
|
65
|
-
else true
|
|
66
|
-
end
|
|
67
|
-
)
|
|
68
|
-
})
|
|
69
|
-
')
|
|
70
|
-
printf 'languages=%s\n' "$json" >> "$GITHUB_OUTPUT"
|
|
71
|
-
|
|
72
|
-
# Expose stable, shell-friendly outputs for explicit analyzer jobs. Keeping
|
|
73
|
-
# these separate from the JSON matrix lets workflow-level conditions avoid
|
|
74
|
-
# allocating a runner for languages that are absent or unchanged.
|
|
75
|
-
for codeql_language in actions javascript-typescript python rust; do
|
|
76
|
-
output_prefix="$codeql_language"
|
|
77
|
-
case "$codeql_language" in
|
|
78
|
-
javascript-typescript) output_prefix="javascript" ;;
|
|
79
|
-
esac
|
|
80
|
-
|
|
81
|
-
entry="$(jq -c --arg language "$codeql_language" '.[] | select(.language == $language)' <<< "$json")"
|
|
82
|
-
if [ -n "$entry" ]; then
|
|
83
|
-
printf '%s_available=true\n' "$output_prefix" >> "$GITHUB_OUTPUT"
|
|
84
|
-
printf '%s_changed=%s\n' "$output_prefix" "$(jq -r '.changed' <<< "$entry")" >> "$GITHUB_OUTPUT"
|
|
85
|
-
printf '%s_build_mode=%s\n' "$output_prefix" "$(jq -r '."build-mode"' <<< "$entry")" >> "$GITHUB_OUTPUT"
|
|
86
|
-
else
|
|
87
|
-
printf '%s_available=false\n' "$output_prefix" >> "$GITHUB_OUTPUT"
|
|
88
|
-
printf '%s_changed=false\n' "$output_prefix" >> "$GITHUB_OUTPUT"
|
|
89
|
-
printf '%s_build_mode=none\n' "$output_prefix" >> "$GITHUB_OUTPUT"
|
|
90
|
-
fi
|
|
91
|
-
done
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
errors=0
|
|
5
|
-
|
|
6
|
-
configured_features="all"
|
|
7
|
-
config_file=.github/code-foundry.yml
|
|
8
|
-
if [ -f "$config_file" ]; then
|
|
9
|
-
configured_features="$(awk -F': ' '/^features:/ {print $2; exit}' "$config_file")"
|
|
10
|
-
[ -n "$configured_features" ] || configured_features="all"
|
|
11
|
-
fi
|
|
12
|
-
|
|
13
|
-
feature_enabled() {
|
|
14
|
-
[ "$configured_features" = all ] && return 0
|
|
15
|
-
case " $(printf '%s' "$configured_features" | tr ',' ' ') " in
|
|
16
|
-
*" $1 "*) return 0 ;;
|
|
17
|
-
*) return 1 ;;
|
|
18
|
-
esac
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
error() {
|
|
22
|
-
printf 'ERROR: %s\n' "$1" >&2
|
|
23
|
-
errors=$((errors + 1))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
warn() {
|
|
27
|
-
printf 'WARN: %s\n' "$1" >&2
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if [ ! -f .mise.toml ]; then
|
|
31
|
-
error ".mise.toml is missing"
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
if [ "$(git config --get core.hooksPath || true)" != ".githooks" ]; then
|
|
35
|
-
warn "Git hooks are not enabled; run bash .github/scripts/bootstrap.sh"
|
|
36
|
-
fi
|
|
37
|
-
|
|
38
|
-
if [ -f package.json ]; then
|
|
39
|
-
node -e 'JSON.parse(require("fs").readFileSync("package.json", "utf8"))'
|
|
40
|
-
lockfiles=0
|
|
41
|
-
for lockfile in bun.lock bun.lockb pnpm-lock.yaml yarn.lock package-lock.json; do
|
|
42
|
-
if [ -f "$lockfile" ]; then lockfiles=$((lockfiles + 1)); fi
|
|
43
|
-
done
|
|
44
|
-
if [ "$lockfiles" -eq 0 ]; then
|
|
45
|
-
if node -e 'const p=require("./package.json"); const groups=[p.dependencies,p.devDependencies,p.optionalDependencies,p.peerDependencies]; process.exit(groups.some((g)=>g && Object.keys(g).length) ? 0 : 1)' 2>/dev/null; then
|
|
46
|
-
error "package.json exists but no supported lockfile was found"
|
|
47
|
-
else
|
|
48
|
-
printf '%s\n' "INFO: package.json has no dependencies; a lockfile is optional"
|
|
49
|
-
fi
|
|
50
|
-
elif [ "$lockfiles" -gt 1 ]; then
|
|
51
|
-
error "multiple JavaScript lockfiles found; keep one package manager"
|
|
52
|
-
fi
|
|
53
|
-
if node -e 'const p=require("./package.json"); process.exit(p.packageManager ? 0 : 1)' 2>/dev/null; then
|
|
54
|
-
declared="$(node -p 'require("./package.json").packageManager.split("@")[0]')"
|
|
55
|
-
actual=""
|
|
56
|
-
[ -f bun.lock ] || [ -f bun.lockb ] && actual="bun"
|
|
57
|
-
[ -f pnpm-lock.yaml ] && actual="pnpm"
|
|
58
|
-
[ -f yarn.lock ] && actual="yarn"
|
|
59
|
-
[ -f package-lock.json ] && actual="npm"
|
|
60
|
-
[ -n "$actual" ] && [ "$declared" != "$actual" ] && error "packageManager ($declared) does not match $actual lockfile"
|
|
61
|
-
fi
|
|
62
|
-
if git ls-files -- '*.ts' '*.tsx' '*.js' '*.jsx' | grep -q .; then
|
|
63
|
-
if ! node -e 'const p=require("./package.json"); const s=p.scripts||{}; process.exit(s.test||s["test:unit"]||s["test:integration"] ? 0 : 1)' 2>/dev/null; then
|
|
64
|
-
warn "JavaScript/TypeScript sources found but no test or test:unit/test:integration script is defined"
|
|
65
|
-
fi
|
|
66
|
-
fi
|
|
67
|
-
if [ -f bunfig.toml ] && node -e 'const p=require("./package.json"); process.exit(p.scripts?.["test:coverage"] ? 0 : 1)' 2>/dev/null; then
|
|
68
|
-
if ! grep -q 'coverageThreshold' bunfig.toml; then
|
|
69
|
-
printf '%s\n' "INFO: shared CI enforces the Bun aggregate coverage threshold"
|
|
70
|
-
fi
|
|
71
|
-
fi
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
if [ -f Cargo.toml ]; then
|
|
75
|
-
command -v cargo >/dev/null 2>&1 || error "Cargo is required for this repository"
|
|
76
|
-
cargo metadata --no-deps --format-version 1 >/dev/null
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
if [ -f pyproject.toml ] || [ -f requirements.txt ] || [ -f requirements-dev.txt ]; then
|
|
80
|
-
if ! command -v python >/dev/null 2>&1 && [ ! -x .venv/bin/python ]; then
|
|
81
|
-
error "Python is required for this repository"
|
|
82
|
-
fi
|
|
83
|
-
fi
|
|
84
|
-
|
|
85
|
-
for workflow in ci codeql security test draft-pr release-pr release; do
|
|
86
|
-
if feature_enabled "$workflow"; then
|
|
87
|
-
[ -f ".github/workflows/$workflow.yml" ] || error "missing enabled workflow: $workflow.yml"
|
|
88
|
-
fi
|
|
89
|
-
done
|
|
90
|
-
|
|
91
|
-
printf '%s\n' 'Remote CI, Test, Security, CodeQL, and release runtimes are loaded by reusable workflow wrappers.'
|
|
92
|
-
|
|
93
|
-
if [ "$errors" -gt 0 ]; then
|
|
94
|
-
printf '%s\n' "Repository doctor found $errors error(s)." >&2
|
|
95
|
-
exit 1
|
|
96
|
-
fi
|
|
97
|
-
|
|
98
|
-
printf '%s\n' "Repository doctor passed."
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# Bootstrap only the formatter for Bun projects whose repository-owned format
|
|
5
|
-
# script directly invokes Prettier. The workflow keeps a full-install fallback
|
|
6
|
-
# because formatter configs may import arbitrary repository dependencies.
|
|
7
|
-
|
|
8
|
-
set_output() {
|
|
9
|
-
printf '%s=%s\n' "$1" "$2" >> "${GITHUB_OUTPUT:-/dev/null}"
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
set_output hit false
|
|
13
|
-
|
|
14
|
-
[ -f package.json ] || exit 0
|
|
15
|
-
[ -f bun.lock ] || [ -f bun.lockb ] || exit 0
|
|
16
|
-
|
|
17
|
-
project_profile="$(node <<'NODE'
|
|
18
|
-
const fs = require('node:fs');
|
|
19
|
-
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
20
|
-
process.stdout.write(Array.isArray(packageJson.workspaces) ||
|
|
21
|
-
(packageJson.workspaces && typeof packageJson.workspaces === 'object') ? 'workspace' : 'standalone');
|
|
22
|
-
NODE
|
|
23
|
-
)"
|
|
24
|
-
lock_file='bun.lock'
|
|
25
|
-
[ -f "$lock_file" ] || lock_file='bun.lockb'
|
|
26
|
-
lock_bytes="$(wc -c < "$lock_file")"
|
|
27
|
-
# A temporary npm formatter install has fixed overhead. For small standalone
|
|
28
|
-
# projects, the normal Bun install is usually faster; workspace graphs and
|
|
29
|
-
# larger lockfiles still benefit materially from avoiding their full tree.
|
|
30
|
-
if [ "$project_profile" = standalone ] && [ "$lock_bytes" -le 524288 ]; then
|
|
31
|
-
exit 0
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
format_script="$(node <<'NODE'
|
|
35
|
-
const fs = require('node:fs');
|
|
36
|
-
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
37
|
-
console.log(packageJson.scripts?.['format:check'] || packageJson.scripts?.format || '');
|
|
38
|
-
NODE
|
|
39
|
-
)"
|
|
40
|
-
if ! grep -Eiq '(^|[[:space:];&|])prettier([[:space:]]|$)' <<<"$format_script" ||
|
|
41
|
-
grep -Eiq '(^|[[:space:];&|])turbo([[:space:]]|$)' <<<"$format_script"; then
|
|
42
|
-
exit 0
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
prettier_version="$(node <<'NODE'
|
|
46
|
-
const fs = require('node:fs');
|
|
47
|
-
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
48
|
-
for (const group of [packageJson.devDependencies, packageJson.dependencies, packageJson.optionalDependencies]) {
|
|
49
|
-
const value = group?.prettier;
|
|
50
|
-
const match = typeof value === 'string' && value.match(/(\d+\.\d+\.\d+)/);
|
|
51
|
-
if (match) {
|
|
52
|
-
console.log(match[1]);
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
NODE
|
|
57
|
-
)"
|
|
58
|
-
[ -n "$prettier_version" ] || exit 0
|
|
59
|
-
|
|
60
|
-
tool_home="${RUNNER_TEMP:-/tmp}/repo-foundry-format"
|
|
61
|
-
install_log="$(mktemp)"
|
|
62
|
-
cleanup() { rm -f "$install_log"; }
|
|
63
|
-
trap cleanup EXIT
|
|
64
|
-
|
|
65
|
-
if ! npm install --prefix "$tool_home" --no-save --ignore-scripts --no-audit --no-fund \
|
|
66
|
-
"prettier@${prettier_version}" >"$install_log" 2>&1; then
|
|
67
|
-
echo 'Format Probe: formatter bootstrap unavailable; using full install'
|
|
68
|
-
exit 0
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
tool_bin="$tool_home/node_modules/.bin"
|
|
72
|
-
if [ ! -x "$tool_bin/prettier" ]; then
|
|
73
|
-
echo 'Format Probe: formatter binary unavailable; using full install'
|
|
74
|
-
exit 0
|
|
75
|
-
fi
|
|
76
|
-
|
|
77
|
-
echo "$tool_bin" >> "${GITHUB_PATH:-/dev/null}"
|
|
78
|
-
printf 'REPO_FOUNDRY_FORMAT_FAST=true\n' >> "${GITHUB_ENV:-/dev/null}"
|
|
79
|
-
echo "Format Probe: using Prettier ${prettier_version} without project install"
|
|
80
|
-
set_output hit true
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
source="${REPO_FOUNDRY_SOURCE:-https://github.com/${GITHUB_REPOSITORY_OWNER:-OWNER}/code-foundry.git}"
|
|
5
|
-
ref="main"
|
|
6
|
-
profile="${REPO_FOUNDRY_PROFILE:-auto}"
|
|
7
|
-
protection=false
|
|
8
|
-
dry_run=false
|
|
9
|
-
prune=false
|
|
10
|
-
force=false
|
|
11
|
-
languages="${REPO_FOUNDRY_LANGUAGES:-auto}"
|
|
12
|
-
features="${REPO_FOUNDRY_FEATURES:-all}"
|
|
13
|
-
package_manager="${REPO_FOUNDRY_PACKAGE_MANAGER:-auto}"
|
|
14
|
-
runtime_repository="${REPO_FOUNDRY_RUNTIME_REPOSITORY:-}"
|
|
15
|
-
runtime_ref="${REPO_FOUNDRY_RUNTIME_REF:-}"
|
|
16
|
-
bootstrap=true
|
|
17
|
-
release_type="${REPO_FOUNDRY_RELEASE_TYPE:-auto}"
|
|
18
|
-
npm_publish="${REPO_FOUNDRY_NPM_PUBLISH:-false}"
|
|
19
|
-
prune_standard="${REPO_FOUNDRY_PRUNE_STANDARD:-false}"
|
|
20
|
-
license="${REPO_FOUNDRY_LICENSE:-gpl-3.0-or-later}"
|
|
21
|
-
license_file="${REPO_FOUNDRY_LICENSE_FILE:-}"
|
|
22
|
-
runner="${REPO_FOUNDRY_RUNNER:-ubuntu-latest}"
|
|
23
|
-
unit_runner="${REPO_FOUNDRY_UNIT_RUNNER:-ubuntu-slim}"
|
|
24
|
-
ci_runner="${REPO_FOUNDRY_CI_RUNNER:-ubuntu-latest}"
|
|
25
|
-
test_runner="${REPO_FOUNDRY_TEST_RUNNER:-ubuntu-latest}"
|
|
26
|
-
security_runner="${REPO_FOUNDRY_SECURITY_RUNNER:-ubuntu-slim}"
|
|
27
|
-
codeql_runner="${REPO_FOUNDRY_CODEQL_RUNNER:-ubuntu-latest}"
|
|
28
|
-
pr_runner="${REPO_FOUNDRY_PR_RUNNER:-ubuntu-slim}"
|
|
29
|
-
release_runner="${REPO_FOUNDRY_RELEASE_RUNNER:-ubuntu-slim}"
|
|
30
|
-
cache_packages="${REPO_FOUNDRY_CACHE_PACKAGES:-auto}"
|
|
31
|
-
cache_build="${REPO_FOUNDRY_CACHE_BUILD:-auto}"
|
|
32
|
-
coverage_minimum="${REPO_FOUNDRY_COVERAGE_MINIMUM:-80}"
|
|
33
|
-
turbo_remote="${REPO_FOUNDRY_TURBO_REMOTE:-auto}"
|
|
34
|
-
|
|
35
|
-
profile_set=false
|
|
36
|
-
languages_set=false
|
|
37
|
-
features_set=false
|
|
38
|
-
package_manager_set=false
|
|
39
|
-
runtime_repository_set=false
|
|
40
|
-
runtime_ref_set=false
|
|
41
|
-
release_type_set=false
|
|
42
|
-
npm_publish_set=false
|
|
43
|
-
license_set=false
|
|
44
|
-
[ -n "${REPO_FOUNDRY_PROFILE:-}" ] && profile_set=true
|
|
45
|
-
[ -n "${REPO_FOUNDRY_LANGUAGES:-}" ] && languages_set=true
|
|
46
|
-
[ -n "${REPO_FOUNDRY_FEATURES:-}" ] && features_set=true
|
|
47
|
-
[ -n "${REPO_FOUNDRY_PACKAGE_MANAGER:-}" ] && package_manager_set=true
|
|
48
|
-
[ -n "${REPO_FOUNDRY_RUNTIME_REPOSITORY:-}" ] && runtime_repository_set=true
|
|
49
|
-
[ -n "${REPO_FOUNDRY_RUNTIME_REF:-}" ] && runtime_ref_set=true
|
|
50
|
-
[ -n "${REPO_FOUNDRY_RELEASE_TYPE:-}" ] && release_type_set=true
|
|
51
|
-
[ -n "${REPO_FOUNDRY_NPM_PUBLISH:-}" ] && npm_publish_set=true
|
|
52
|
-
[ -n "${REPO_FOUNDRY_LICENSE:-}" ] && license_set=true
|
|
53
|
-
tool_dir=""
|
|
54
|
-
|
|
55
|
-
cleanup() {
|
|
56
|
-
if [ -n "$tool_dir" ]; then rm -rf "$tool_dir"; fi
|
|
57
|
-
}
|
|
58
|
-
trap cleanup EXIT
|
|
59
|
-
|
|
60
|
-
usage() {
|
|
61
|
-
cat <<'EOF'
|
|
62
|
-
Usage: init-repo.sh [options]
|
|
63
|
-
|
|
64
|
-
Initialize or synchronize a repository from the shared baseline.
|
|
65
|
-
|
|
66
|
-
Options:
|
|
67
|
-
--source PATH_OR_URL Template source (default: REPO_FOUNDRY_SOURCE or GitHub owner)
|
|
68
|
-
--ref REF Template branch or tag (default: main)
|
|
69
|
-
--profile NAME auto, application, monorepo, or minimal
|
|
70
|
-
--languages LIST auto or comma-separated: typescript,rust,python,solidity
|
|
71
|
-
--features LIST all or comma-separated optional features:
|
|
72
|
-
ci,codeql,security,test,draft-pr,release-pr,release,dependabot
|
|
73
|
-
--package-manager NAME auto, bun, pnpm, yarn, or npm
|
|
74
|
-
--runtime-repository OWNER/REPO Reusable workflow runtime repository (auto from source)
|
|
75
|
-
--runtime-ref REF Reusable workflow runtime tag or branch
|
|
76
|
-
--release-type NAME auto, node, python, rust, or simple
|
|
77
|
-
--license NAME gpl-3.0-or-later, agpl-3.0-or-later, mit, preserve, or none
|
|
78
|
-
--license-file PATH Use an exact custom license file
|
|
79
|
-
--npm-publish Enable npm publication in the release workflow
|
|
80
|
-
--dry-run Preview changes without writing files
|
|
81
|
-
--force Replace protected standard docs/templates
|
|
82
|
-
--prune Remove disabled standard workflows (never custom workflows)
|
|
83
|
-
--protection Synchronize main branch required checks
|
|
84
|
-
--no-bootstrap Skip mise/hooks/doctor bootstrap after init
|
|
85
|
-
-h, --help Show this help
|
|
86
|
-
|
|
87
|
-
Examples:
|
|
88
|
-
bash .github/scripts/init-repo.sh --languages typescript,python
|
|
89
|
-
bash .github/scripts/init-repo.sh --languages rust --features ci,codeql,security,test
|
|
90
|
-
bash .github/scripts/init-repo.sh --features all --dry-run
|
|
91
|
-
EOF
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
while [ "$#" -gt 0 ]; do
|
|
95
|
-
case "$1" in
|
|
96
|
-
--source) source="${2:?missing source path or URL}"; shift 2 ;;
|
|
97
|
-
--ref) ref="${2:?missing ref}"; shift 2 ;;
|
|
98
|
-
--profile) profile="${2:?missing profile}"; profile_set=true; shift 2 ;;
|
|
99
|
-
--languages) languages="${2:?missing language list}"; languages_set=true; shift 2 ;;
|
|
100
|
-
--features) features="${2:?missing feature list}"; features_set=true; shift 2 ;;
|
|
101
|
-
--package-manager) package_manager="${2:?missing package manager}"; package_manager_set=true; shift 2 ;;
|
|
102
|
-
--runtime-repository) runtime_repository="${2:?missing runtime repository}"; runtime_repository_set=true; shift 2 ;;
|
|
103
|
-
--runtime-ref) runtime_ref="${2:?missing runtime ref}"; runtime_ref_set=true; shift 2 ;;
|
|
104
|
-
--release-type) release_type="${2:?missing release type}"; release_type_set=true; shift 2 ;;
|
|
105
|
-
--license) license="${2:?missing license}"; license_set=true; shift 2 ;;
|
|
106
|
-
--license-file) license_file="${2:?missing license file}"; shift 2 ;;
|
|
107
|
-
--npm-publish) npm_publish=true; shift ;;
|
|
108
|
-
--dry-run) dry_run=true; shift ;;
|
|
109
|
-
--force) force=true; shift ;;
|
|
110
|
-
--prune) prune=true; shift ;;
|
|
111
|
-
--protection) protection=true; shift ;;
|
|
112
|
-
--no-bootstrap) bootstrap=false; shift ;;
|
|
113
|
-
-h|--help) usage; exit 0 ;;
|
|
114
|
-
*)
|
|
115
|
-
printf 'Unknown option: %s\n' "$1" >&2
|
|
116
|
-
exit 2
|
|
117
|
-
;;
|
|
118
|
-
esac
|
|
119
|
-
done
|
|
120
|
-
|
|
121
|
-
config_path=.github/code-foundry.yml
|
|
122
|
-
if [ -f "$config_path" ]; then
|
|
123
|
-
config_value() {
|
|
124
|
-
awk -F': ' -v key="$1" '$1 == key { value=$2; sub(/[[:space:]]+#.*/, "", value); gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); print value; exit }' "$config_path"
|
|
125
|
-
}
|
|
126
|
-
if [ "$profile_set" = false ]; then profile="$(config_value profile)"; fi
|
|
127
|
-
if [ "$languages_set" = false ]; then languages="$(config_value languages)"; fi
|
|
128
|
-
if [ "$features_set" = false ]; then features="$(config_value features)"; fi
|
|
129
|
-
if [ "$package_manager_set" = false ]; then package_manager="$(config_value package_manager)"; fi
|
|
130
|
-
if [ "$runtime_repository_set" = false ]; then runtime_repository="$(config_value runtime_repository)"; fi
|
|
131
|
-
if [ "$runtime_ref_set" = false ]; then runtime_ref="$(config_value runtime_ref)"; fi
|
|
132
|
-
if [ "$release_type_set" = false ]; then release_type="$(config_value release_type)"; fi
|
|
133
|
-
if [ "$npm_publish_set" = false ]; then npm_publish="$(config_value npm_publish)"; fi
|
|
134
|
-
if [ "$license_set" = false ]; then license="$(config_value license)"; fi
|
|
135
|
-
[ -n "$license_file" ] || license_file="$(config_value license_file)"
|
|
136
|
-
prune_standard="$(config_value prune_standard)"
|
|
137
|
-
[ -n "${REPO_FOUNDRY_RUNNER:-}" ] || runner="$(config_value runner)"
|
|
138
|
-
[ -n "${REPO_FOUNDRY_UNIT_RUNNER:-}" ] || unit_runner="$(config_value unit_runner)"
|
|
139
|
-
[ -n "${REPO_FOUNDRY_CI_RUNNER:-}" ] || ci_runner="$(config_value ci_runner)"
|
|
140
|
-
[ -n "${REPO_FOUNDRY_TEST_RUNNER:-}" ] || test_runner="$(config_value test_runner)"
|
|
141
|
-
[ -n "${REPO_FOUNDRY_SECURITY_RUNNER:-}" ] || security_runner="$(config_value security_runner)"
|
|
142
|
-
[ -n "${REPO_FOUNDRY_CODEQL_RUNNER:-}" ] || codeql_runner="$(config_value codeql_runner)"
|
|
143
|
-
[ -n "${REPO_FOUNDRY_PR_RUNNER:-}" ] || pr_runner="$(config_value pr_runner)"
|
|
144
|
-
[ -n "${REPO_FOUNDRY_RELEASE_RUNNER:-}" ] || release_runner="$(config_value release_runner)"
|
|
145
|
-
[ -n "${REPO_FOUNDRY_CACHE_PACKAGES:-}" ] || cache_packages="$(config_value cache_packages)"
|
|
146
|
-
[ -n "${REPO_FOUNDRY_CACHE_BUILD:-}" ] || cache_build="$(config_value cache_build)"
|
|
147
|
-
[ -n "${REPO_FOUNDRY_COVERAGE_MINIMUM:-}" ] || coverage_minimum="$(config_value coverage_minimum)"
|
|
148
|
-
[ -n "${REPO_FOUNDRY_TURBO_REMOTE:-}" ] || turbo_remote="$(config_value turbo_remote)"
|
|
149
|
-
[ -n "$runner" ] || runner=ubuntu-latest
|
|
150
|
-
[ -n "$unit_runner" ] || unit_runner=ubuntu-slim
|
|
151
|
-
[ -n "$ci_runner" ] || ci_runner="$runner"
|
|
152
|
-
[ -n "$test_runner" ] || test_runner="$runner"
|
|
153
|
-
[ -n "$security_runner" ] || security_runner=ubuntu-slim
|
|
154
|
-
[ -n "$codeql_runner" ] || codeql_runner="$runner"
|
|
155
|
-
[ -n "$pr_runner" ] || pr_runner=ubuntu-slim
|
|
156
|
-
[ -n "$release_runner" ] || release_runner=ubuntu-slim
|
|
157
|
-
[ -n "$cache_packages" ] || cache_packages=auto
|
|
158
|
-
[ -n "$cache_build" ] || cache_build=auto
|
|
159
|
-
[ -n "$coverage_minimum" ] || coverage_minimum=80
|
|
160
|
-
[ -n "$turbo_remote" ] || turbo_remote=auto
|
|
161
|
-
[ -n "$prune_standard" ] || prune_standard=false
|
|
162
|
-
[ "$prune_standard" = true ] && prune=true
|
|
163
|
-
elif [ -f LICENSE ] && [ -z "${REPO_FOUNDRY_LICENSE:-}" ] && [ "$license_set" = false ]; then
|
|
164
|
-
# Existing projects keep their authored license unless the user explicitly
|
|
165
|
-
# selects a replacement in the generated configuration.
|
|
166
|
-
license=preserve
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
case "$package_manager" in
|
|
170
|
-
auto|none|bun|pnpm|yarn|npm) ;;
|
|
171
|
-
*) printf 'Unsupported package manager: %s\n' "$package_manager" >&2; exit 2 ;;
|
|
172
|
-
esac
|
|
173
|
-
case "$release_type" in
|
|
174
|
-
auto|node|python|rust|simple|none) ;;
|
|
175
|
-
*) printf 'Unsupported release type: %s\n' "$release_type" >&2; exit 2 ;;
|
|
176
|
-
esac
|
|
177
|
-
|
|
178
|
-
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || true)"
|
|
179
|
-
if [ -f "$script_dir/sync-template.sh" ]; then
|
|
180
|
-
sync_script="$script_dir/sync-template.sh"
|
|
181
|
-
elif [ -d "$source" ] && [ -f "$source/.github/scripts/sync-template.sh" ]; then
|
|
182
|
-
sync_script="$source/.github/scripts/sync-template.sh"
|
|
183
|
-
else
|
|
184
|
-
command -v git >/dev/null 2>&1 || { echo "git is required" >&2; exit 1; }
|
|
185
|
-
tool_dir="$(mktemp -d)"
|
|
186
|
-
git clone --quiet --depth 1 --branch "$ref" "$source" "$tool_dir/template-repo"
|
|
187
|
-
sync_script="$tool_dir/template-repo/.github/scripts/sync-template.sh"
|
|
188
|
-
fi
|
|
189
|
-
|
|
190
|
-
sync_args=(
|
|
191
|
-
--source "$source"
|
|
192
|
-
--ref "$ref"
|
|
193
|
-
--profile "$profile"
|
|
194
|
-
--languages "$languages"
|
|
195
|
-
--features "$features"
|
|
196
|
-
--package-manager "$package_manager"
|
|
197
|
-
--license "$license"
|
|
198
|
-
)
|
|
199
|
-
if [ -n "$runtime_repository" ]; then sync_args+=(--runtime-repository "$runtime_repository"); fi
|
|
200
|
-
if [ -n "$runtime_ref" ]; then sync_args+=(--runtime-ref "$runtime_ref"); fi
|
|
201
|
-
if [ -n "$license_file" ]; then sync_args+=(--license-file "$license_file"); fi
|
|
202
|
-
if [ "$dry_run" = true ]; then sync_args+=(--check); else sync_args+=(--apply); fi
|
|
203
|
-
if [ "$prune" = true ]; then sync_args+=(--prune); fi
|
|
204
|
-
if [ "$force" = true ]; then sync_args+=(--force); fi
|
|
205
|
-
REPO_FOUNDRY_INIT=true bash "$sync_script" "${sync_args[@]}"
|
|
206
|
-
|
|
207
|
-
if [ "$dry_run" = true ]; then
|
|
208
|
-
printf '%s\n' 'Dry run complete; no files were changed.'
|
|
209
|
-
exit 0
|
|
210
|
-
fi
|
|
211
|
-
|
|
212
|
-
mkdir -p .github
|
|
213
|
-
config_value() {
|
|
214
|
-
awk -F': ' -v key="$1" '$1 == key { value=$2; sub(/[[:space:]]+#.*/, "", value); gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); print value; exit }' .github/code-foundry.yml
|
|
215
|
-
}
|
|
216
|
-
profile="$(config_value profile 2>/dev/null || true)"
|
|
217
|
-
languages="$(config_value languages 2>/dev/null || true)"
|
|
218
|
-
features="$(config_value features 2>/dev/null || true)"
|
|
219
|
-
package_manager="$(config_value package_manager 2>/dev/null || true)"
|
|
220
|
-
runtime_repository="$(config_value runtime_repository 2>/dev/null || true)"
|
|
221
|
-
runtime_ref="$(config_value runtime_ref 2>/dev/null || true)"
|
|
222
|
-
release_type="$(config_value release_type 2>/dev/null || true)"
|
|
223
|
-
npm_publish="$(config_value npm_publish 2>/dev/null || true)"
|
|
224
|
-
license="$(config_value license 2>/dev/null || true)"
|
|
225
|
-
{
|
|
226
|
-
printf 'version: 1\n'
|
|
227
|
-
printf 'profile: %s\n' "$profile"
|
|
228
|
-
printf 'languages: %s\n' "$languages"
|
|
229
|
-
printf 'features: %s\n' "$features"
|
|
230
|
-
printf 'package_manager: %s\n' "$package_manager"
|
|
231
|
-
printf 'runtime_repository: %s\n' "$runtime_repository"
|
|
232
|
-
printf 'runtime_ref: %s\n' "$runtime_ref"
|
|
233
|
-
printf 'runner: %s\n' "$runner"
|
|
234
|
-
printf 'unit_runner: %s\n' "$unit_runner"
|
|
235
|
-
printf 'ci_runner: %s\n' "$ci_runner"
|
|
236
|
-
printf 'test_runner: %s\n' "$test_runner"
|
|
237
|
-
printf 'security_runner: %s\n' "$security_runner"
|
|
238
|
-
printf 'codeql_runner: %s\n' "$codeql_runner"
|
|
239
|
-
printf 'pr_runner: %s\n' "$pr_runner"
|
|
240
|
-
printf 'release_runner: %s\n' "$release_runner"
|
|
241
|
-
printf 'release_type: %s\n' "$release_type"
|
|
242
|
-
printf 'npm_publish: %s\n' "$npm_publish"
|
|
243
|
-
printf 'license: %s\n' "$license"
|
|
244
|
-
if [ -n "$license_file" ]; then
|
|
245
|
-
printf 'license_file: %s\n' "$license_file"
|
|
246
|
-
fi
|
|
247
|
-
printf 'prune_standard: %s\n' "$prune_standard"
|
|
248
|
-
printf 'cache_packages: %s\n' "$cache_packages"
|
|
249
|
-
printf 'cache_build: %s\n' "$cache_build"
|
|
250
|
-
printf 'coverage_minimum: %s\n' "$coverage_minimum"
|
|
251
|
-
printf 'turbo_remote: %s\n' "$turbo_remote"
|
|
252
|
-
} > .github/code-foundry.yml
|
|
253
|
-
|
|
254
|
-
if [ "$bootstrap" = false ]; then
|
|
255
|
-
printf '%s\n' 'Bootstrap skipped.'
|
|
256
|
-
exit 0
|
|
257
|
-
fi
|
|
258
|
-
|
|
259
|
-
bootstrap_script="$script_dir/bootstrap.sh"
|
|
260
|
-
[ -x "$bootstrap_script" ] || { echo "Package is missing bootstrap.sh" >&2; exit 1; }
|
|
261
|
-
bash "$bootstrap_script"
|
|
262
|
-
|
|
263
|
-
if [ "$protection" = true ]; then
|
|
264
|
-
remote="$(git remote get-url origin 2>/dev/null || true)"
|
|
265
|
-
repo="$(printf '%s\n' "$remote" | sed -E 's#.*github.com[:/]([^/]+/[^/.]+)(\.git)?$#\1#')"
|
|
266
|
-
[ -n "$repo" ] || { echo 'Could not determine GitHub repository from origin' >&2; exit 1; }
|
|
267
|
-
bash .github/scripts/sync-protection.sh --repo "$repo" --apply
|
|
268
|
-
fi
|
|
@@ -1,67 +0,0 @@
|
|
|
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
|