From 1c6f4ac1712985253efd1efb6702ac7e9caf1293 Mon Sep 17 00:00:00 2001 From: NiftyAndy Date: Tue, 28 Jul 2026 12:42:34 -0400 Subject: [PATCH 1/3] feat: centralize repository configuration --- .github/CONTRIBUTING.md | 2 +- .github/actions/setup/action.yml | 22 +-- .github/{template.yml => code-foundry.yml} | 8 ++ .github/code-foundry.yml.example | 41 ++++++ .github/scripts/changed-files.sh | 2 +- .github/scripts/ci.sh | 8 +- .github/scripts/codeql-languages.sh | 8 +- .github/scripts/doctor.sh | 6 +- .github/scripts/init-repo.sh | 145 ++++++++++++++++--- .github/scripts/profile.sh | 15 +- .github/scripts/security.sh | 8 +- .github/scripts/sync-protection.sh | 8 +- .github/scripts/sync-template.sh | 155 ++++++++++++++++++--- .github/template.yml.example | 18 --- .github/workflows/draft-pr.yml | 1 + .github/workflows/release-pr.yml | 1 + .github/workflows/release.yml | 1 + .github/workflows/reusable-ci.yml | 15 +- .github/workflows/reusable-codeql.yml | 17 ++- .github/workflows/reusable-draft-pr.yml | 8 +- .github/workflows/reusable-release-pr.yml | 8 +- .github/workflows/reusable-release.yml | 16 ++- .github/workflows/reusable-security.yml | 19 ++- .github/workflows/reusable-test.yml | 20 ++- README.md | 11 +- docs/CONFIGURATION.md | 87 ++++++++++++ docs/INITIALIZATION.md | 15 +- docs/README.md | 1 + docs/RELEASES.md | 2 +- src/cli.mjs | 53 +++++-- 30 files changed, 588 insertions(+), 133 deletions(-) rename .github/{template.yml => code-foundry.yml} (61%) create mode 100644 .github/code-foundry.yml.example delete mode 100644 .github/template.yml.example create mode 100644 docs/CONFIGURATION.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f4114f1b..c0bc85ee 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -170,7 +170,7 @@ Required checks are enforced by branch protection. Do not duplicate their checkl ### Release conventions -Use Conventional Commits so the release automation can determine the next version: `fix:` produces a patch release, `feat:` produces a minor release, and `!` or `BREAKING CHANGE:` produces a major release. Add `Release-As: x.y.z` only when a deliberate version override is needed. The release workflow maintains the changelog and GitHub release after changes land on `main`; npm publication is opt-in through `.github/template.yml`. +Use Conventional Commits so the release automation can determine the next version: `fix:` produces a patch release, `feat:` produces a minor release, and `!` or `BREAKING CHANGE:` produces a major release. Add `Release-As: x.y.z` only when a deliberate version override is needed. The release workflow maintains the changelog and GitHub release after changes land on `main`; npm publication is opt-in through `.github/code-foundry.yml`. Security checks can be skipped when repository visibility or the GitHub plan does not support a feature. A skipped optional check must not be configured as a required status check. diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 021a014e..c8a35ed6 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -205,8 +205,10 @@ runs: javascript_package_manager=none configured_package_manager="" - if [ -f .github/template.yml ]; then - configured_package_manager="$(awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml)" + config_file=.github/code-foundry.yml + [ -f "$config_file" ] || config_file=.github/template.yml + if [ -f "$config_file" ]; then + configured_package_manager="$(awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file")" fi case "$configured_package_manager" in bun|pnpm|yarn|npm) javascript_package_manager="$configured_package_manager" ;; @@ -387,7 +389,7 @@ runs: if [ "$javascript" = true ] && task_language_needed javascript; then add_configured_tool node if { [ -f bun.lock ] || [ -f bun.lockb ] || - awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml 2>/dev/null | grep -qx bun; }; then + config_file=.github/code-foundry.yml; [ -f "$config_file" ] || config_file=.github/template.yml; awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file" 2>/dev/null | grep -qx bun; }; then add_configured_tool bun fi fi @@ -401,7 +403,7 @@ runs: javascript) configured_tool node && mise_tools+=(node) if { [ -f bun.lock ] || [ -f bun.lockb ] || - awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml 2>/dev/null | grep -qx bun; } && configured_tool bun; then + config_file=.github/code-foundry.yml; [ -f "$config_file" ] || config_file=.github/template.yml; awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file" 2>/dev/null | grep -qx bun; } && configured_tool bun; then mise_tools+=(bun) fi ;; @@ -449,12 +451,12 @@ runs: (inputs.cache-installed == 'true' || steps.profile.outputs.javascript_package_manager == 'pnpm' || steps.profile.outputs.javascript_package_manager == 'yarn') && - hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/template.yml', '.mise.toml', 'mise.lock') != '' + hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/code-foundry.yml', '.github/template.yml', '.mise.toml', 'mise.lock') != '' uses: actions/cache/restore@v5 with: path: | **/node_modules - key: ${{ runner.os }}-javascript-installed-v3-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/template.yml', '.mise.toml', 'mise.lock') }} + key: ${{ runner.os }}-javascript-installed-v3-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/code-foundry.yml', '.github/template.yml', '.mise.toml', 'mise.lock') }} - name: Restore Bun dependencies id: javascript-bun-cache-restore if: >- @@ -463,12 +465,12 @@ runs: inputs.cache-installed != 'false' && steps.profile.outputs.javascript_dependencies == 'true' && steps.profile.outputs.javascript_package_manager == 'bun' && - hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/template.yml', '.mise.toml', 'mise.lock') != '' + hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/code-foundry.yml', '.github/template.yml', '.mise.toml', 'mise.lock') != '' uses: actions/cache/restore@v5 with: path: | **/node_modules - key: ${{ runner.os }}-javascript-installed-v4-bun-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/template.yml', '.mise.toml', 'mise.lock') }} + key: ${{ runner.os }}-javascript-installed-v4-bun-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/code-foundry.yml', '.github/template.yml', '.mise.toml', 'mise.lock') }} - name: Detect Bun dependencies id: javascript-bun-dependencies if: >- @@ -735,7 +737,7 @@ runs: with: path: | **/node_modules - key: ${{ runner.os }}-javascript-installed-v4-bun-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/template.yml', '.mise.toml', 'mise.lock') }} + key: ${{ runner.os }}-javascript-installed-v4-bun-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/.npmrc', '.github/code-foundry.yml', '.github/template.yml', '.mise.toml', 'mise.lock') }} - name: Save installed JavaScript dependencies if: >- inputs.cache-save == 'true' && @@ -751,7 +753,7 @@ runs: with: path: | **/node_modules - key: ${{ runner.os }}-javascript-installed-v3-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/template.yml', '.mise.toml', 'mise.lock') }} + key: ${{ runner.os }}-javascript-installed-v3-${{ hashFiles('**/package.json', '**/bun.lock', '**/bun.lockb', '**/pnpm-lock.yaml', '**/yarn.lock', '**/package-lock.json', '**/.npmrc', '**/.yarnrc*', '**/.pnpmfile.cjs', '.github/code-foundry.yml', '.github/template.yml', '.mise.toml', 'mise.lock') }} - name: Save JavaScript packages if: >- inputs.cache-save == 'true' && diff --git a/.github/template.yml b/.github/code-foundry.yml similarity index 61% rename from .github/template.yml rename to .github/code-foundry.yml index 9a3a0ce0..ffa3d43d 100644 --- a/.github/template.yml +++ b/.github/code-foundry.yml @@ -1,3 +1,4 @@ +# Canonical Code Foundry repository configuration. version: 1 template: code-foundry@latest profile: auto @@ -15,3 +16,10 @@ cache_packages: auto cache_build: auto coverage_minimum: 80 turbo_remote: auto +ci_runner: ubuntu-latest +test_runner: ubuntu-latest +security_runner: ubuntu-slim +codeql_runner: ubuntu-latest +pr_runner: ubuntu-slim +release_runner: ubuntu-slim +prune_standard: false diff --git a/.github/code-foundry.yml.example b/.github/code-foundry.yml.example new file mode 100644 index 00000000..9f3d197f --- /dev/null +++ b/.github/code-foundry.yml.example @@ -0,0 +1,41 @@ +# Copy this file to .github/code-foundry.yml, edit the values you need, and run: +# npx code-foundry init --config .github/code-foundry.yml +# Omitted keys use automatic detection and the standard defaults. See +# docs/CONFIGURATION.md for the complete visual reference. + +version: 1 + +# Repository shape and supported languages. +profile: auto # auto, application, monorepo, minimal +languages: auto # auto or typescript,rust,python,solidity +package_manager: auto # auto, bun, pnpm, yarn, npm + +# Standard callers to install. Use `all` or a comma-separated selection. +features: all # ci, codeql, security, test, draft-pr, release-pr, release, dependabot + +# Runtime source. Leave blank to infer it from the template source. +runtime_repository: # OWNER/REPO, or leave blank to infer +runtime_ref: # tag or branch, or leave blank for default + +# Release and licensing behavior. +release_type: auto # auto, node, python, rust, simple, none +npm_publish: false +license: preserve # agpl-3.0-or-later, mit, preserve, none +# license_file: ./legal/LICENSE.txt + +# Runner policy. Each workflow caller receives its selected runner. +runner: ubuntu-latest +unit_runner: ubuntu-slim +ci_runner: ubuntu-latest +test_runner: ubuntu-latest +security_runner: ubuntu-slim +codeql_runner: ubuntu-latest +pr_runner: ubuntu-slim +release_runner: ubuntu-slim +prune_standard: false # remove disabled standard workflows during sync + +# Cache and quality policy. +cache_packages: auto # auto, true, false +cache_build: auto # auto, true, false +coverage_minimum: 80 +turbo_remote: auto # auto, true, false diff --git a/.github/scripts/changed-files.sh b/.github/scripts/changed-files.sh index bb6921ce..3e6a8f2c 100755 --- a/.github/scripts/changed-files.sh +++ b/.github/scripts/changed-files.sh @@ -72,7 +72,7 @@ repo_foundry_pr_dependencies_unchanged() { while IFS= read -r file; do case "$file" in .github/workflows/security.yml|.github/scripts/security.sh|.github/scripts/changed-files.sh|\ - .github/actions/setup/action.yml|.github/template.yml|.mise.toml|mise.lock|\ + .github/actions/setup/action.yml|.github/code-foundry.yml|.github/template.yml|.mise.toml|mise.lock|\ .github/security-audit-allowlist.txt) return 1 ;; diff --git a/.github/scripts/ci.sh b/.github/scripts/ci.sh index faf6e1f3..f4bcc184 100755 --- a/.github/scripts/ci.sh +++ b/.github/scripts/ci.sh @@ -93,8 +93,12 @@ package_manager() { local configured="" if [ -x .github/scripts/profile.sh ]; then configured="$(bash .github/scripts/profile.sh get package_manager 2>/dev/null || true)" - elif [ -f .github/template.yml ]; then - configured="$(awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml)" + else + config_file=.github/code-foundry.yml + [ -f "$config_file" ] || config_file=.github/template.yml + if [ -f "$config_file" ]; then + configured="$(awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file")" + fi fi case "$configured" in bun|pnpm|yarn|npm) echo "$configured"; return ;; diff --git a/.github/scripts/codeql-languages.sh b/.github/scripts/codeql-languages.sh index 6db797e5..ab565afc 100755 --- a/.github/scripts/codeql-languages.sh +++ b/.github/scripts/codeql-languages.sh @@ -25,8 +25,12 @@ if find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) -print - configured="" if [ -x .github/scripts/profile.sh ]; then configured="$(bash .github/scripts/profile.sh get languages 2>/dev/null || true)" -elif [ -f .github/template.yml ]; then - configured="$(awk -F': ' '/^languages:/ {print $2; exit}' .github/template.yml)" +else + config_file=.github/code-foundry.yml + [ -f "$config_file" ] || config_file=.github/template.yml + if [ -f "$config_file" ]; then + configured="$(awk -F': ' '/^languages:/ {print $2; exit}' "$config_file")" + fi fi if [ "$configured" = auto ] || [ "$configured" = all ] || [ -z "$configured" ]; then diff --git a/.github/scripts/doctor.sh b/.github/scripts/doctor.sh index ba4b47d8..78570e63 100755 --- a/.github/scripts/doctor.sh +++ b/.github/scripts/doctor.sh @@ -4,8 +4,10 @@ set -euo pipefail errors=0 configured_features="all" -if [ -f .github/template.yml ]; then - configured_features="$(awk -F': ' '/^features:/ {print $2; exit}' .github/template.yml)" +config_file=.github/code-foundry.yml +[ -f "$config_file" ] || config_file=.github/template.yml +if [ -f "$config_file" ]; then + configured_features="$(awk -F': ' '/^features:/ {print $2; exit}' "$config_file")" [ -n "$configured_features" ] || configured_features="all" fi diff --git a/.github/scripts/init-repo.sh b/.github/scripts/init-repo.sh index 032f8827..99f2bcf2 100755 --- a/.github/scripts/init-repo.sh +++ b/.github/scripts/init-repo.sh @@ -3,6 +3,7 @@ set -euo pipefail source="${REPO_FOUNDRY_SOURCE:-https://github.com/${GITHUB_REPOSITORY_OWNER:-OWNER}/code-foundry.git}" ref="main" +config_file="${REPO_FOUNDRY_CONFIG:-}" profile="${REPO_FOUNDRY_PROFILE:-auto}" protection=false dry_run=false @@ -16,8 +17,40 @@ runtime_ref="${REPO_FOUNDRY_RUNTIME_REF:-}" bootstrap=true release_type="${REPO_FOUNDRY_RELEASE_TYPE:-auto}" npm_publish="${REPO_FOUNDRY_NPM_PUBLISH:-false}" +prune_standard="${REPO_FOUNDRY_PRUNE_STANDARD:-false}" license="${REPO_FOUNDRY_LICENSE:-agpl-3.0-or-later}" license_file="${REPO_FOUNDRY_LICENSE_FILE:-}" +runner="${REPO_FOUNDRY_RUNNER:-ubuntu-latest}" +unit_runner="${REPO_FOUNDRY_UNIT_RUNNER:-ubuntu-slim}" +ci_runner="${REPO_FOUNDRY_CI_RUNNER:-ubuntu-latest}" +test_runner="${REPO_FOUNDRY_TEST_RUNNER:-ubuntu-latest}" +security_runner="${REPO_FOUNDRY_SECURITY_RUNNER:-ubuntu-slim}" +codeql_runner="${REPO_FOUNDRY_CODEQL_RUNNER:-ubuntu-latest}" +pr_runner="${REPO_FOUNDRY_PR_RUNNER:-ubuntu-slim}" +release_runner="${REPO_FOUNDRY_RELEASE_RUNNER:-ubuntu-slim}" +cache_packages="${REPO_FOUNDRY_CACHE_PACKAGES:-auto}" +cache_build="${REPO_FOUNDRY_CACHE_BUILD:-auto}" +coverage_minimum="${REPO_FOUNDRY_COVERAGE_MINIMUM:-80}" +turbo_remote="${REPO_FOUNDRY_TURBO_REMOTE:-auto}" + +profile_set=false +languages_set=false +features_set=false +package_manager_set=false +runtime_repository_set=false +runtime_ref_set=false +release_type_set=false +npm_publish_set=false +license_set=false +[ -n "${REPO_FOUNDRY_PROFILE:-}" ] && profile_set=true +[ -n "${REPO_FOUNDRY_LANGUAGES:-}" ] && languages_set=true +[ -n "${REPO_FOUNDRY_FEATURES:-}" ] && features_set=true +[ -n "${REPO_FOUNDRY_PACKAGE_MANAGER:-}" ] && package_manager_set=true +[ -n "${REPO_FOUNDRY_RUNTIME_REPOSITORY:-}" ] && runtime_repository_set=true +[ -n "${REPO_FOUNDRY_RUNTIME_REF:-}" ] && runtime_ref_set=true +[ -n "${REPO_FOUNDRY_RELEASE_TYPE:-}" ] && release_type_set=true +[ -n "${REPO_FOUNDRY_NPM_PUBLISH:-}" ] && npm_publish_set=true +[ -n "${REPO_FOUNDRY_LICENSE:-}" ] && license_set=true tool_dir="" cleanup() { @@ -34,6 +67,7 @@ Initialize or synchronize a repository from the shared baseline. Options: --source PATH_OR_URL Template source (default: REPO_FOUNDRY_SOURCE or GitHub owner) --ref REF Template branch or tag (default: main) + --config PATH Use a .github/code-foundry.yml configuration file --profile NAME auto, application, monorepo, or minimal --languages LIST auto or comma-separated: typescript,rust,python,solidity --features LIST all or comma-separated optional features: @@ -63,14 +97,15 @@ while [ "$#" -gt 0 ]; do case "$1" in --source) source="${2:?missing source path or URL}"; shift 2 ;; --ref) ref="${2:?missing ref}"; shift 2 ;; - --profile) profile="${2:?missing profile}"; shift 2 ;; - --languages) languages="${2:?missing language list}"; shift 2 ;; - --features) features="${2:?missing feature list}"; shift 2 ;; - --package-manager) package_manager="${2:?missing package manager}"; shift 2 ;; - --runtime-repository) runtime_repository="${2:?missing runtime repository}"; shift 2 ;; - --runtime-ref) runtime_ref="${2:?missing runtime ref}"; shift 2 ;; - --release-type) release_type="${2:?missing release type}"; shift 2 ;; - --license) license="${2:?missing license}"; shift 2 ;; + --config) config_file="${2:?missing config path}"; shift 2 ;; + --profile) profile="${2:?missing profile}"; profile_set=true; shift 2 ;; + --languages) languages="${2:?missing language list}"; languages_set=true; shift 2 ;; + --features) features="${2:?missing feature list}"; features_set=true; shift 2 ;; + --package-manager) package_manager="${2:?missing package manager}"; package_manager_set=true; shift 2 ;; + --runtime-repository) runtime_repository="${2:?missing runtime repository}"; runtime_repository_set=true; shift 2 ;; + --runtime-ref) runtime_ref="${2:?missing runtime ref}"; runtime_ref_set=true; shift 2 ;; + --release-type) release_type="${2:?missing release type}"; release_type_set=true; shift 2 ;; + --license) license="${2:?missing license}"; license_set=true; shift 2 ;; --license-file) license_file="${2:?missing license file}"; shift 2 ;; --npm-publish) npm_publish=true; shift ;; --dry-run) dry_run=true; shift ;; @@ -86,6 +121,59 @@ while [ "$#" -gt 0 ]; do esac done +if [ -n "$config_file" ]; then + [ -f "$config_file" ] || { printf 'Configuration file not found: %s\n' "$config_file" >&2; exit 1; } + mkdir -p .github + if [ "$(cd -- "$(dirname -- "$config_file")" && pwd)/$(basename -- "$config_file")" != "$(pwd)/.github/code-foundry.yml" ]; then + cp "$config_file" .github/code-foundry.yml + fi +fi + +config_path=.github/code-foundry.yml +[ -f "$config_path" ] || config_path=.github/template.yml +if [ -f "$config_path" ]; then + config_value() { + awk -F': ' -v key="$1" '$1 == key { value=$2; sub(/[[:space:]]+#.*/, "", value); gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); print value; exit }' "$config_path" + } + if [ "$profile_set" = false ]; then profile="$(config_value profile)"; fi + if [ "$languages_set" = false ]; then languages="$(config_value languages)"; fi + if [ "$features_set" = false ]; then features="$(config_value features)"; fi + if [ "$package_manager_set" = false ]; then package_manager="$(config_value package_manager)"; fi + if [ "$runtime_repository_set" = false ]; then runtime_repository="$(config_value runtime_repository)"; fi + if [ "$runtime_ref_set" = false ]; then runtime_ref="$(config_value runtime_ref)"; fi + if [ "$release_type_set" = false ]; then release_type="$(config_value release_type)"; fi + if [ "$npm_publish_set" = false ]; then npm_publish="$(config_value npm_publish)"; fi + if [ "$license_set" = false ]; then license="$(config_value license)"; fi + [ -n "$license_file" ] || license_file="$(config_value license_file)" + prune_standard="$(config_value prune_standard)" + [ -n "${REPO_FOUNDRY_RUNNER:-}" ] || runner="$(config_value runner)" + [ -n "${REPO_FOUNDRY_UNIT_RUNNER:-}" ] || unit_runner="$(config_value unit_runner)" + [ -n "${REPO_FOUNDRY_CI_RUNNER:-}" ] || ci_runner="$(config_value ci_runner)" + [ -n "${REPO_FOUNDRY_TEST_RUNNER:-}" ] || test_runner="$(config_value test_runner)" + [ -n "${REPO_FOUNDRY_SECURITY_RUNNER:-}" ] || security_runner="$(config_value security_runner)" + [ -n "${REPO_FOUNDRY_CODEQL_RUNNER:-}" ] || codeql_runner="$(config_value codeql_runner)" + [ -n "${REPO_FOUNDRY_PR_RUNNER:-}" ] || pr_runner="$(config_value pr_runner)" + [ -n "${REPO_FOUNDRY_RELEASE_RUNNER:-}" ] || release_runner="$(config_value release_runner)" + [ -n "${REPO_FOUNDRY_CACHE_PACKAGES:-}" ] || cache_packages="$(config_value cache_packages)" + [ -n "${REPO_FOUNDRY_CACHE_BUILD:-}" ] || cache_build="$(config_value cache_build)" + [ -n "${REPO_FOUNDRY_COVERAGE_MINIMUM:-}" ] || coverage_minimum="$(config_value coverage_minimum)" + [ -n "${REPO_FOUNDRY_TURBO_REMOTE:-}" ] || turbo_remote="$(config_value turbo_remote)" + [ -n "$runner" ] || runner=ubuntu-latest + [ -n "$unit_runner" ] || unit_runner=ubuntu-slim + [ -n "$ci_runner" ] || ci_runner="$runner" + [ -n "$test_runner" ] || test_runner="$runner" + [ -n "$security_runner" ] || security_runner=ubuntu-slim + [ -n "$codeql_runner" ] || codeql_runner="$runner" + [ -n "$pr_runner" ] || pr_runner=ubuntu-slim + [ -n "$release_runner" ] || release_runner=ubuntu-slim + [ -n "$cache_packages" ] || cache_packages=auto + [ -n "$cache_build" ] || cache_build=auto + [ -n "$coverage_minimum" ] || coverage_minimum=80 + [ -n "$turbo_remote" ] || turbo_remote=auto + [ -n "$prune_standard" ] || prune_standard=false + [ "$prune_standard" = true ] && prune=true +fi + case "$package_manager" in auto|bun|pnpm|yarn|npm) ;; *) printf 'Unsupported package manager: %s\n' "$package_manager" >&2; exit 2 ;; @@ -130,16 +218,19 @@ if [ "$dry_run" = true ]; then fi mkdir -p .github -template_ref="$(awk -F': ' '/^template:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -profile="$(awk -F': ' '/^profile:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -languages="$(awk -F': ' '/^languages:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -features="$(awk -F': ' '/^features:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -package_manager="$(awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -runtime_repository="$(awk -F': ' '/^runtime_repository:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -runtime_ref="$(awk -F': ' '/^runtime_ref:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -release_type="$(awk -F': ' '/^release_type:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -npm_publish="$(awk -F': ' '/^npm_publish:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" -license="$(awk -F': ' '/^license:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" +config_value() { + awk -F': ' -v key="$1" '$1 == key { value=$2; sub(/[[:space:]]+#.*/, "", value); gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); print value; exit }' .github/code-foundry.yml +} +template_ref="$(config_value template 2>/dev/null || true)" +profile="$(config_value profile 2>/dev/null || true)" +languages="$(config_value languages 2>/dev/null || true)" +features="$(config_value features 2>/dev/null || true)" +package_manager="$(config_value package_manager 2>/dev/null || true)" +runtime_repository="$(config_value runtime_repository 2>/dev/null || true)" +runtime_ref="$(config_value runtime_ref 2>/dev/null || true)" +release_type="$(config_value release_type 2>/dev/null || true)" +npm_publish="$(config_value npm_publish 2>/dev/null || true)" +license="$(config_value license 2>/dev/null || true)" { printf 'version: 1\n' if [ -n "$template_ref" ]; then @@ -151,10 +242,26 @@ license="$(awk -F': ' '/^license:/ {print $2; exit}' .github/template.yml 2>/dev printf 'package_manager: %s\n' "$package_manager" printf 'runtime_repository: %s\n' "$runtime_repository" printf 'runtime_ref: %s\n' "$runtime_ref" + printf 'runner: %s\n' "$runner" + printf 'unit_runner: %s\n' "$unit_runner" + printf 'ci_runner: %s\n' "$ci_runner" + printf 'test_runner: %s\n' "$test_runner" + printf 'security_runner: %s\n' "$security_runner" + printf 'codeql_runner: %s\n' "$codeql_runner" + printf 'pr_runner: %s\n' "$pr_runner" + printf 'release_runner: %s\n' "$release_runner" printf 'release_type: %s\n' "$release_type" printf 'npm_publish: %s\n' "$npm_publish" printf 'license: %s\n' "$license" -} > .github/template.yml + if [ -n "$license_file" ]; then + printf 'license_file: %s\n' "$license_file" + fi + printf 'prune_standard: %s\n' "$prune_standard" + printf 'cache_packages: %s\n' "$cache_packages" + printf 'cache_build: %s\n' "$cache_build" + printf 'coverage_minimum: %s\n' "$coverage_minimum" + printf 'turbo_remote: %s\n' "$turbo_remote" +} > .github/code-foundry.yml if [ "$bootstrap" = false ]; then printf '%s\n' 'Bootstrap skipped.' diff --git a/.github/scripts/profile.sh b/.github/scripts/profile.sh index b9832f03..12e60a50 100755 --- a/.github/scripts/profile.sh +++ b/.github/scripts/profile.sh @@ -3,7 +3,8 @@ set -euo pipefail # Resolve repository settings with this precedence: # explicit REPO_FOUNDRY_* values (CLI callers can export them), then -# .github/template.yml, then detected defaults. +# .github/code-foundry.yml, then detected defaults. A legacy +# .github/template.yml is accepted during migration. root="${REPO_FOUNDRY_ROOT:-$PWD}" command="detect" @@ -31,12 +32,13 @@ while [ "$#" -gt 0 ]; do done cd "$root" -template_file=.github/template.yml +template_file=.github/code-foundry.yml +[ -f "$template_file" ] || template_file=.github/template.yml config_value() { local key="$1" [ -f "$template_file" ] || return 0 - awk -F': *' -v key="$key" '$1 == key { print substr($0, index($0, ":") + 1); exit }' "$template_file" | + awk -F': *' -v key="$key" '$1 == key { value=substr($0, index($0, ":") + 1); sub(/[[:space:]]+#.*/, "", value); print value; exit }' "$template_file" | sed -e 's/^ *//' -e 's/ *$//' -e 's/^['"'"'"]//' -e 's/['"'"'"]$//' } @@ -159,10 +161,17 @@ detect() { printf 'npm_publish=%s\n' "$npm_publish" printf 'runner=%s\n' "$runner" printf 'unit_runner=%s\n' "$unit_runner" + printf 'ci_runner=%s\n' "$(raw_value ci_runner "$runner")" + printf 'test_runner=%s\n' "$(raw_value test_runner "$runner")" + printf 'security_runner=%s\n' "$(raw_value security_runner ubuntu-slim)" + printf 'codeql_runner=%s\n' "$(raw_value codeql_runner "$runner")" + printf 'pr_runner=%s\n' "$(raw_value pr_runner ubuntu-slim)" + printf 'release_runner=%s\n' "$(raw_value release_runner ubuntu-slim)" printf 'cache_packages=%s\n' "$(raw_value cache_packages auto)" printf 'cache_build=%s\n' "$(raw_value cache_build auto)" printf 'coverage_minimum=%s\n' "$(raw_value coverage_minimum 80)" printf 'turbo_remote=%s\n' "$(raw_value turbo_remote auto)" + printf 'prune_standard=%s\n' "$(raw_value prune_standard false)" } case "$command" in diff --git a/.github/scripts/security.sh b/.github/scripts/security.sh index be81b7d7..d4840e2e 100755 --- a/.github/scripts/security.sh +++ b/.github/scripts/security.sh @@ -105,8 +105,12 @@ package_manager() { local configured="" if [ -x .github/scripts/profile.sh ]; then configured="$(bash .github/scripts/profile.sh get package_manager 2>/dev/null || true)" - elif [ -f .github/template.yml ]; then - configured="$(awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml)" + else + config_file=.github/code-foundry.yml + [ -f "$config_file" ] || config_file=.github/template.yml + if [ -f "$config_file" ]; then + configured="$(awk -F': ' '/^package_manager:/ {print $2; exit}' "$config_file")" + fi fi case "$configured" in bun|pnpm|yarn|npm) echo "$configured"; return ;; diff --git a/.github/scripts/sync-protection.sh b/.github/scripts/sync-protection.sh index e90e717c..fb32db99 100755 --- a/.github/scripts/sync-protection.sh +++ b/.github/scripts/sync-protection.sh @@ -7,9 +7,11 @@ mode="check" configured_features="all" configured_languages="auto" -if [ -f .github/template.yml ]; then - configured_features="$(awk -F': ' '/^features:/ {print $2; exit}' .github/template.yml)" - configured_languages="$(awk -F': ' '/^languages:/ {print $2; exit}' .github/template.yml)" +config_file=.github/code-foundry.yml +[ -f "$config_file" ] || config_file=.github/template.yml +if [ -f "$config_file" ]; then + configured_features="$(awk -F': ' '/^features:/ {print $2; exit}' "$config_file")" + configured_languages="$(awk -F': ' '/^languages:/ {print $2; exit}' "$config_file")" [ -n "$configured_features" ] || configured_features="all" [ -n "$configured_languages" ] || configured_languages="auto" fi diff --git a/.github/scripts/sync-template.sh b/.github/scripts/sync-template.sh index 086831a4..73382801 100755 --- a/.github/scripts/sync-template.sh +++ b/.github/scripts/sync-template.sh @@ -13,6 +13,7 @@ Options: --languages LIST auto or comma-separated: typescript,rust,python,solidity --features LIST all or comma-separated standard features --package-manager NAME auto, bun, pnpm, yarn, or npm + --config PATH Use a .github/code-foundry.yml configuration file --runtime-repository OWNER/REPO Reusable workflow runtime repository --runtime-ref REF Reusable workflow runtime tag or branch --license NAME preserve, agpl-3.0-or-later, mit, or none @@ -27,11 +28,13 @@ EOF source_ref="main" mode="check" source="" +config_file="${REPO_FOUNDRY_CONFIG:-}" temp_dir="" profile="${REPO_FOUNDRY_PROFILE:-auto}" languages="${REPO_FOUNDRY_LANGUAGES:-auto}" features="${REPO_FOUNDRY_FEATURES:-all}" prune=false +prune_set=false force=false languages_set=false profile_set=false @@ -51,14 +54,30 @@ runtime_ref_set=false template_ref="" release_type="${REPO_FOUNDRY_RELEASE_TYPE:-auto}" npm_publish="${REPO_FOUNDRY_NPM_PUBLISH:-false}" +prune_standard="${REPO_FOUNDRY_PRUNE_STANDARD:-false}" +runner="${REPO_FOUNDRY_RUNNER:-}" +unit_runner="${REPO_FOUNDRY_UNIT_RUNNER:-}" +ci_runner="${REPO_FOUNDRY_CI_RUNNER:-}" +test_runner="${REPO_FOUNDRY_TEST_RUNNER:-}" +security_runner="${REPO_FOUNDRY_SECURITY_RUNNER:-}" +codeql_runner="${REPO_FOUNDRY_CODEQL_RUNNER:-}" +pr_runner="${REPO_FOUNDRY_PR_RUNNER:-}" +release_runner="${REPO_FOUNDRY_RELEASE_RUNNER:-}" +cache_packages="${REPO_FOUNDRY_CACHE_PACKAGES:-}" +cache_build="${REPO_FOUNDRY_CACHE_BUILD:-}" +coverage_minimum="${REPO_FOUNDRY_COVERAGE_MINIMUM:-}" +turbo_remote="${REPO_FOUNDRY_TURBO_REMOTE:-}" license="${REPO_FOUNDRY_LICENSE:-preserve}" license_file="${REPO_FOUNDRY_LICENSE_FILE:-}" release_type_set=false npm_publish_set=false license_set=false +license_file_set=false [ -n "${REPO_FOUNDRY_RELEASE_TYPE:-}" ] && release_type_set=true [ -n "${REPO_FOUNDRY_NPM_PUBLISH:-}" ] && npm_publish_set=true +[ -n "${REPO_FOUNDRY_PRUNE_STANDARD:-}" ] && prune_set=true [ -n "${REPO_FOUNDRY_LICENSE:-}" ] && license_set=true +[ -n "${REPO_FOUNDRY_LICENSE_FILE:-}" ] && license_file_set=true node_version="24.18.0" bun_version="1.3.14" python_version="3.13" @@ -108,6 +127,7 @@ while [ "$#" -gt 0 ]; do case "$1" in --source) source="${2:?missing source path or URL}"; shift 2 ;; --ref) source_ref="${2:?missing ref}"; shift 2 ;; + --config) config_file="${2:?missing config path}"; shift 2 ;; --profile) profile="${2:?missing profile}"; profile_set=true; shift 2 ;; --languages) languages="${2:?missing language list}"; languages_set=true; shift 2 ;; --features) features="${2:?missing feature list}"; features_set=true; shift 2 ;; @@ -115,10 +135,10 @@ while [ "$#" -gt 0 ]; do --runtime-repository) runtime_repository="${2:?missing runtime repository}"; runtime_repository_set=true; shift 2 ;; --runtime-ref) runtime_ref="${2:?missing runtime ref}"; runtime_ref_set=true; shift 2 ;; --license) license="${2:?missing license}"; license_set=true; shift 2 ;; - --license-file) license_file="${2:?missing license file}"; shift 2 ;; + --license-file) license_file="${2:?missing license file}"; license_file_set=true; shift 2 ;; --check) mode="check"; shift ;; --apply) mode="apply"; shift ;; - --prune) prune=true; shift ;; + --prune) prune=true; prune_set=true; shift ;; --force) force=true; shift ;; -h|--help) usage; exit 0 ;; *) usage >&2; exit 2 ;; @@ -126,43 +146,91 @@ while [ "$#" -gt 0 ]; do done [ -n "$source" ] || { usage >&2; exit 2; } -if [ -f .github/template.yml ]; then +if [ -n "$config_file" ]; then + [ -f "$config_file" ] || { printf 'Configuration file not found: %s\n' "$config_file" >&2; exit 1; } + mkdir -p .github + if [ "$(cd -- "$(dirname -- "$config_file")" && pwd)/$(basename -- "$config_file")" != "$(pwd)/.github/code-foundry.yml" ]; then + cp "$config_file" .github/code-foundry.yml + fi +fi +config_path=.github/code-foundry.yml +[ -f "$config_path" ] || config_path=.github/template.yml +if [ -f "$config_path" ]; then + config_value() { + awk -F': ' -v key="$1" '$1 == key { value=$2; sub(/[[:space:]]+#.*/, "", value); gsub(/^[[:space:]]+|[[:space:]]+$/, "", value); print value; exit }' "$config_path" + } if [ "$profile_set" = false ]; then - configured_profile="$(awk -F': ' '/^profile:/ {print $2; exit}' .github/template.yml)" + configured_profile="$(config_value profile)" [ -n "$configured_profile" ] && profile="$configured_profile" fi if [ "$languages_set" = false ]; then - configured_languages="$(awk -F': ' '/^languages:/ {print $2; exit}' .github/template.yml)" + configured_languages="$(config_value languages)" [ -n "$configured_languages" ] && languages="$configured_languages" fi if [ "$features_set" = false ]; then - configured_features="$(awk -F': ' '/^features:/ {print $2; exit}' .github/template.yml)" + configured_features="$(config_value features)" [ -n "$configured_features" ] && features="$configured_features" fi if [ "$package_manager_set" = false ]; then - package_manager="$(awk -F': ' '/^package_manager:/ {print $2; exit}' .github/template.yml)" + package_manager="$(config_value package_manager)" fi if [ "$runtime_repository_set" = false ]; then - runtime_repository="$(awk -F': ' '/^runtime_repository:/ {print $2; exit}' .github/template.yml)" + runtime_repository="$(config_value runtime_repository)" fi if [ "$runtime_ref_set" = false ]; then - runtime_ref="$(awk -F': ' '/^runtime_ref:/ {print $2; exit}' .github/template.yml)" + runtime_ref="$(config_value runtime_ref)" fi - template_ref="$(awk -F': ' '/^template:/ {print $2; exit}' .github/template.yml)" + template_ref="$(config_value template)" if [ "$release_type_set" != true ]; then - configured_release_type="$(awk -F': ' '/^release_type:/ {print $2; exit}' .github/template.yml)" + configured_release_type="$(config_value release_type)" [ -n "$configured_release_type" ] && release_type="$configured_release_type" fi if [ "$npm_publish_set" != true ]; then - configured_npm_publish="$(awk -F': ' '/^npm_publish:/ {print $2; exit}' .github/template.yml)" + configured_npm_publish="$(config_value npm_publish)" [ -n "$configured_npm_publish" ] && npm_publish="$configured_npm_publish" fi if [ "$license_set" != true ]; then - configured_license="$(awk -F': ' '/^license:/ {print $2; exit}' .github/template.yml)" + configured_license="$(config_value license)" [ -n "$configured_license" ] && license="$configured_license" fi + if [ "$license_file_set" = false ]; then + license_file="$(config_value license_file)" + fi + if [ "$prune_set" = false ]; then + configured_prune="$(config_value prune_standard)" + [ -n "$configured_prune" ] && prune_standard="$configured_prune" + fi + [ -n "$runner" ] || runner="$(config_value runner)" + [ -n "$unit_runner" ] || unit_runner="$(config_value unit_runner)" + [ -n "$ci_runner" ] || ci_runner="$(config_value ci_runner)" + [ -n "$test_runner" ] || test_runner="$(config_value test_runner)" + [ -n "$security_runner" ] || security_runner="$(config_value security_runner)" + [ -n "$codeql_runner" ] || codeql_runner="$(config_value codeql_runner)" + [ -n "$pr_runner" ] || pr_runner="$(config_value pr_runner)" + [ -n "$release_runner" ] || release_runner="$(config_value release_runner)" + [ -n "$cache_packages" ] || cache_packages="$(config_value cache_packages)" + [ -n "$cache_build" ] || cache_build="$(config_value cache_build)" + [ -n "$coverage_minimum" ] || coverage_minimum="$(config_value coverage_minimum)" + [ -n "$turbo_remote" ] || turbo_remote="$(config_value turbo_remote)" fi [ -n "$package_manager" ] || package_manager=auto +[ -n "$runner" ] || runner=ubuntu-latest +[ -n "$unit_runner" ] || unit_runner=ubuntu-slim +[ -n "$ci_runner" ] || ci_runner="$runner" +[ -n "$test_runner" ] || test_runner="$runner" +[ -n "$security_runner" ] || security_runner=ubuntu-slim +[ -n "$codeql_runner" ] || codeql_runner="$runner" +[ -n "$pr_runner" ] || pr_runner=ubuntu-slim +[ -n "$release_runner" ] || release_runner=ubuntu-slim +[ -n "$cache_packages" ] || cache_packages=auto +[ -n "$cache_build" ] || cache_build=auto +[ -n "$coverage_minimum" ] || coverage_minimum=80 +[ -n "$turbo_remote" ] || turbo_remote=auto +[ "$prune_standard" = true ] || [ "$prune_standard" = false ] || { + printf 'prune_standard must be true or false: %s\n' "$prune_standard" >&2 + exit 2 +} +[ "$prune_standard" = true ] && prune=true if [ -z "$runtime_repository" ]; then source_repository="$source" if [ -d "$source" ]; then @@ -194,6 +262,11 @@ case "$license" in *) printf 'Unsupported license: %s\n' "$license" >&2; exit 2 ;; esac [ -z "$license_file" ] || license=custom +for configured_runner in "$runner" "$unit_runner" "$ci_runner" "$test_runner" "$security_runner" "$codeql_runner" "$pr_runner" "$release_runner"; do + case "$configured_runner" in + ''|*[!A-Za-z0-9._/-]*) printf 'Runner contains unsupported characters: %s\n' "$configured_runner" >&2; exit 2 ;; + esac +done if [ -d "$source" ] && [ -f "$source/.github/scripts/sync-template.sh" ]; then template_root="$source" @@ -206,8 +279,10 @@ fi # Prefer an explicit CLI/environment value, then the target's saved contract, # then the source template's contract, and finally the stable public runtime. -if [ -z "$runtime_ref" ] && [ -f "$template_root/.github/template.yml" ]; then - runtime_ref="$(awk -F': ' '/^runtime_ref:/ {print $2; exit}' "$template_root/.github/template.yml")" +source_config="$template_root/.github/code-foundry.yml" +[ -f "$source_config" ] || source_config="$template_root/.github/template.yml" +if [ -z "$runtime_ref" ] && [ -f "$source_config" ]; then + runtime_ref="$(awk -F': ' '/^runtime_ref:/ {print $2; exit}' "$source_config")" fi [ -n "$runtime_ref" ] || runtime_ref="v0.20.2" case "$runtime_ref" in @@ -253,7 +328,7 @@ files=( ruff.toml .prettierrc .github/CODEOWNERS - .github/template.yml.example + .github/code-foundry.yml.example .github/CODE_OF_CONDUCT.md .github/CONTRIBUTING.md .github/PULL_REQUEST_TEMPLATE.md @@ -264,6 +339,8 @@ files=( .github/ISSUE_TEMPLATE/feature_request.yml .github/scripts/profile.sh .github/scripts/bootstrap.sh + # Keep the small local hook runner and its changed-file helper; the full + # CI/security implementations used by Actions are loaded from the runtime. .github/scripts/changed-files.sh .github/scripts/ci.sh .github/scripts/doctor.sh @@ -394,9 +471,11 @@ done # the source's configured runtime first so forks remain self-contained. source_runtime_repository="" source_runtime_ref="" -if [ -f "$template_root/.github/template.yml" ]; then - source_runtime_repository="$(awk -F': ' '/^runtime_repository:/ {print $2; exit}' "$template_root/.github/template.yml")" - source_runtime_ref="$(awk -F': ' '/^runtime_ref:/ {print $2; exit}' "$template_root/.github/template.yml")" +source_config="$template_root/.github/code-foundry.yml" +[ -f "$source_config" ] || source_config="$template_root/.github/template.yml" +if [ -f "$source_config" ]; then + source_runtime_repository="$(awk -F': ' '/^runtime_repository:/ {print $2; exit}' "$source_config")" + source_runtime_ref="$(awk -F': ' '/^runtime_ref:/ {print $2; exit}' "$source_config")" fi [ -n "$source_runtime_repository" ] || source_runtime_repository="0xPlayerOne/code-foundry" [ -n "$source_runtime_ref" ] || source_runtime_ref="$runtime_ref" @@ -407,10 +486,21 @@ for file in .github/workflows/ci.yml .github/workflows/test.yml .github/workflow if [ "$mode" = "check" ]; then printf 'Would render runtime repository in %s\n' "$file" else + runner_value="$runner" + case "$file" in + .github/workflows/ci.yml) runner_value="$ci_runner" ;; + .github/workflows/test.yml) runner_value="$test_runner" ;; + .github/workflows/security.yml) runner_value="$security_runner" ;; + .github/workflows/codeql.yml) runner_value="$codeql_runner" ;; + .github/workflows/draft-pr.yml|.github/workflows/release-pr.yml) runner_value="$pr_runner" ;; + .github/workflows/release.yml) runner_value="$release_runner" ;; + esac rendered_workflow="$(mktemp)" sed -E \ -e "s#${source_runtime_repository}@[^[:space:]]+#${runtime_repository}@${runtime_ref}#g" \ -e "s#runtime-ref: [^[:space:]]+#runtime-ref: ${runtime_ref}#g" \ + -e "s#^ runner: [^[:space:]]+# runner: ${runner_value}#" \ + -e "s#^ unit-runner: [^[:space:]]+# unit-runner: ${unit_runner}#" \ "$file" > "$rendered_workflow" mv "$rendered_workflow" "$file" printf 'Rendered runtime repository in %s\n' "$file" @@ -592,10 +682,26 @@ if [ "$mode" = "apply" ]; then fi printf 'runtime_repository: %s\n' "$runtime_repository" printf 'runtime_ref: %s\n' "$runtime_ref" + printf 'runner: %s\n' "$runner" + printf 'unit_runner: %s\n' "$unit_runner" + printf 'ci_runner: %s\n' "$ci_runner" + printf 'test_runner: %s\n' "$test_runner" + printf 'security_runner: %s\n' "$security_runner" + printf 'codeql_runner: %s\n' "$codeql_runner" + printf 'pr_runner: %s\n' "$pr_runner" + printf 'release_runner: %s\n' "$release_runner" + printf 'prune_standard: %s\n' "$prune_standard" + printf 'cache_packages: %s\n' "$cache_packages" + printf 'cache_build: %s\n' "$cache_build" + printf 'coverage_minimum: %s\n' "$coverage_minimum" + printf 'turbo_remote: %s\n' "$turbo_remote" printf 'release_type: %s\n' "$release_type" printf 'npm_publish: %s\n' "$npm_publish" printf 'license: %s\n' "$license" - } > .github/template.yml + if [ -n "$license_file" ]; then + printf 'license_file: %s\n' "$license_file" + fi + } > .github/code-foundry.yml fi if [ "$prune" = true ]; then @@ -611,6 +717,15 @@ if [ "$prune" = true ]; then fi fi done + if [ -f .github/dependabot.yml ] && ! workflow_enabled dependabot; then + changed=$((changed + 1)) + if [ "$mode" = "check" ]; then + printf 'Would remove disabled standard configuration .github/dependabot.yml\n' + else + rm .github/dependabot.yml + printf '%s\n' 'Removed disabled standard configuration .github/dependabot.yml' + fi + fi fi printf '%s\n' "$changed baseline file(s) differ." diff --git a/.github/template.yml.example b/.github/template.yml.example deleted file mode 100644 index c22d5156..00000000 --- a/.github/template.yml.example +++ /dev/null @@ -1,18 +0,0 @@ -# Repository-specific settings for the shared initializer and sync scripts. -version: 1 -template: code-foundry@latest -profile: auto -languages: auto -features: all -package_manager: auto -runtime_repository: 0xPlayerOne/code-foundry -runtime_ref: v0.20.2 -release_type: auto -npm_publish: false -license: preserve -runner: ubuntu-latest -unit_runner: ubuntu-slim -cache_packages: auto -cache_build: auto -coverage_minimum: 80 -turbo_remote: auto diff --git a/.github/workflows/draft-pr.yml b/.github/workflows/draft-pr.yml index af04e13c..1a884be7 100644 --- a/.github/workflows/draft-pr.yml +++ b/.github/workflows/draft-pr.yml @@ -19,4 +19,5 @@ jobs: draft-pr: name: Draft PR uses: 0xPlayerOne/code-foundry/.github/workflows/reusable-draft-pr.yml@v0.20.2 + with: secrets: inherit diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 1f6c0a2a..69c2f9f8 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -12,4 +12,5 @@ jobs: release-pr: name: Release PR uses: 0xPlayerOne/code-foundry/.github/workflows/reusable-release-pr.yml@v0.20.2 + with: secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a99b01cd..9ba1e72a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,4 +15,5 @@ jobs: release: name: Release uses: 0xPlayerOne/code-foundry/.github/workflows/reusable-release.yml@v0.20.2 + with: secrets: inherit diff --git a/.github/workflows/reusable-ci.yml b/.github/workflows/reusable-ci.yml index cf4b962f..24d073c0 100644 --- a/.github/workflows/reusable-ci.yml +++ b/.github/workflows/reusable-ci.yml @@ -12,7 +12,12 @@ on: description: Code Foundry runtime tag or ref. required: false type: string - default: v0.20.0 + default: v0.20.2 + runner: + description: Runner used by CI jobs. + required: false + type: string + default: ubuntu-latest permissions: contents: read @@ -36,7 +41,7 @@ concurrency: jobs: format: name: Format - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} steps: - name: Checkout uses: actions/checkout@v7 @@ -78,7 +83,7 @@ jobs: lint: name: Lint - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} steps: - name: Checkout uses: actions/checkout@v7 @@ -112,7 +117,7 @@ jobs: type-check: name: Type-Check - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} steps: - name: Checkout uses: actions/checkout@v7 @@ -146,7 +151,7 @@ jobs: build: name: Build - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} steps: - name: Checkout uses: actions/checkout@v7 diff --git a/.github/workflows/reusable-codeql.yml b/.github/workflows/reusable-codeql.yml index df105697..fd754172 100644 --- a/.github/workflows/reusable-codeql.yml +++ b/.github/workflows/reusable-codeql.yml @@ -12,7 +12,12 @@ on: description: Code Foundry runtime tag or ref. required: false type: string - default: v0.20.0 + default: v0.20.2 + runner: + description: Runner used by CodeQL jobs. + required: false + type: string + default: ubuntu-latest permissions: actions: read @@ -32,7 +37,7 @@ concurrency: jobs: detect: name: Detect - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 10 outputs: languages: ${{ steps.languages.outputs.languages }} @@ -115,7 +120,7 @@ jobs: ( !github.event.repository.private || needs.detect.outputs.code_security == 'enabled' ) && needs.detect.outputs.actions_available == 'true' && needs.detect.outputs.actions_changed == 'true' - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} timeout-minutes: 30 steps: - name: Checkout @@ -148,7 +153,7 @@ jobs: ( !github.event.repository.private || needs.detect.outputs.code_security == 'enabled' ) && needs.detect.outputs.javascript_available == 'true' && needs.detect.outputs.javascript_changed == 'true' - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} timeout-minutes: 30 steps: - name: Checkout @@ -181,7 +186,7 @@ jobs: ( !github.event.repository.private || needs.detect.outputs.code_security == 'enabled' ) && needs.detect.outputs.python_available == 'true' && needs.detect.outputs.python_changed == 'true' - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} timeout-minutes: 30 steps: - name: Checkout @@ -214,7 +219,7 @@ jobs: ( !github.event.repository.private || needs.detect.outputs.code_security == 'enabled' ) && needs.detect.outputs.rust_available == 'true' && needs.detect.outputs.rust_changed == 'true' - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} timeout-minutes: 30 steps: - name: Checkout diff --git a/.github/workflows/reusable-draft-pr.yml b/.github/workflows/reusable-draft-pr.yml index 8c8c7fef..5bb28809 100644 --- a/.github/workflows/reusable-draft-pr.yml +++ b/.github/workflows/reusable-draft-pr.yml @@ -2,6 +2,12 @@ name: Code Foundry Draft PR on: workflow_call: + inputs: + runner: + description: Runner used to create the draft PR. + required: false + type: string + default: ubuntu-slim permissions: contents: read @@ -13,7 +19,7 @@ env: jobs: create-draft-pr: name: Create - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 10 concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/reusable-release-pr.yml b/.github/workflows/reusable-release-pr.yml index 8ddd3efe..b5eabb83 100644 --- a/.github/workflows/reusable-release-pr.yml +++ b/.github/workflows/reusable-release-pr.yml @@ -2,6 +2,12 @@ name: Code Foundry Release PR on: workflow_call: + inputs: + runner: + description: Runner used to create the release PR. + required: false + type: string + default: ubuntu-slim permissions: contents: read @@ -13,7 +19,7 @@ env: jobs: create-release-pr: name: Create - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 10 concurrency: group: ${{ github.workflow }}-${{ github.ref }} diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index 90577c4f..1862f2c4 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -2,6 +2,12 @@ name: Code Foundry Release on: workflow_call: + inputs: + runner: + description: Runner used by release jobs. + required: false + type: string + default: ubuntu-slim permissions: contents: write @@ -19,7 +25,7 @@ env: jobs: release: name: Release / Version - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 20 concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -41,8 +47,10 @@ jobs: release_type="$(bash .github/scripts/profile.sh get release_type)" npm_publish="$(bash .github/scripts/profile.sh get npm_publish)" else - release_type="$(awk -F': ' '/^release_type:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" - npm_publish="$(awk -F': ' '/^npm_publish:/ {print $2; exit}' .github/template.yml 2>/dev/null || true)" + config_file=.github/code-foundry.yml + [ -f "$config_file" ] || config_file=.github/template.yml + release_type="$(awk -F': ' '/^release_type:/ {print $2; exit}' "$config_file" 2>/dev/null || true)" + npm_publish="$(awk -F': ' '/^npm_publish:/ {print $2; exit}' "$config_file" 2>/dev/null || true)" fi [ -n "$release_type" ] || release_type=auto [ -n "$npm_publish" ] || npm_publish=false @@ -77,7 +85,7 @@ jobs: name: Release / Publish npm needs: release if: needs.release.outputs.release_created == 'true' && needs.release.outputs.npm_publish == 'true' - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 15 permissions: contents: read diff --git a/.github/workflows/reusable-security.yml b/.github/workflows/reusable-security.yml index 726be636..c162048e 100644 --- a/.github/workflows/reusable-security.yml +++ b/.github/workflows/reusable-security.yml @@ -12,7 +12,12 @@ on: description: Code Foundry runtime tag or ref. required: false type: string - default: v0.20.0 + default: v0.20.2 + runner: + description: Runner used by Security jobs. + required: false + type: string + default: ubuntu-slim permissions: contents: read @@ -32,7 +37,7 @@ concurrency: jobs: profile: name: Profile - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 10 outputs: javascript: ${{ steps.profile.outputs.javascript }} @@ -90,7 +95,7 @@ jobs: dependency-audit-javascript: name: Dependency Audit (JavaScript) - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 20 steps: - name: Checkout @@ -140,7 +145,7 @@ jobs: dependency-audit-rust: name: Dependency Audit (Rust) - runs-on: ${{ vars.REPO_FOUNDRY_RUST_AUDIT_RUNNER || 'ubuntu-latest' }} + runs-on: ${{ inputs.runner }} timeout-minutes: 20 steps: - name: Checkout @@ -213,7 +218,7 @@ jobs: max-parallel: 8 matrix: requirement: ${{ fromJSON(needs.profile.outputs.python_requirements) }} - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 20 env: REPO_FOUNDRY_PYTHON_REQUIREMENT: ${{ matrix.requirement }} @@ -261,7 +266,7 @@ jobs: name: Dependency Audit (Python) needs: [profile, dependency-audit-python] if: always() - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 5 steps: - name: Gate @@ -281,7 +286,7 @@ jobs: dependency-review: name: Dependency Review if: ${{ github.event_name == 'pull_request' && !github.event.repository.private }} - runs-on: ubuntu-slim + runs-on: ${{ inputs.runner }} timeout-minutes: 10 steps: - name: Review diff --git a/.github/workflows/reusable-test.yml b/.github/workflows/reusable-test.yml index c05818b5..5aa65e54 100644 --- a/.github/workflows/reusable-test.yml +++ b/.github/workflows/reusable-test.yml @@ -12,7 +12,17 @@ on: description: Code Foundry runtime tag or ref. required: false type: string - default: v0.20.0 + default: v0.20.2 + runner: + description: Runner used by non-unit test jobs. + required: false + type: string + default: ubuntu-latest + unit-runner: + description: Runner used by unit tests. + required: false + type: string + default: ubuntu-slim permissions: contents: read @@ -36,7 +46,7 @@ concurrency: jobs: unit: name: Unit - runs-on: ${{ vars.REPO_FOUNDRY_UNIT_RUNNER || 'ubuntu-slim' }} + runs-on: ${{ inputs.unit-runner }} timeout-minutes: 30 steps: - name: Checkout @@ -97,7 +107,7 @@ jobs: integration: name: Integration - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} timeout-minutes: 30 steps: - name: Checkout @@ -142,7 +152,7 @@ jobs: e2e: name: E2E - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} timeout-minutes: 45 steps: - name: Checkout @@ -199,7 +209,7 @@ jobs: smoke: name: Smoke - runs-on: ubuntu-latest + runs-on: ${{ inputs.runner }} timeout-minutes: 15 steps: - name: Checkout diff --git a/README.md b/README.md index 0fbbea2b..06ee9ea2 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,10 @@ npx code-foundry init --dry-run For an existing installation, use `npx code-foundry sync`. Run `npx code-foundry doctor` to inspect the resulting repository configuration. +If `.github/code-foundry.yml` already exists, initialization uses it automatically. +You can also supply a different file with `--config PATH`; after changing the +file, run `npx code-foundry sync` to render the selected configuration. + ## What it installs - Short workflow callers for CI, Test, Security, CodeQL, Draft PR, Release PR, @@ -66,11 +70,12 @@ Initialization is flag-driven. The most important options are: --license-file PATH ``` -The selected profile is saved in `.github/template.yml`. Explicit flags take +The selected profile is saved in `.github/code-foundry.yml`. Explicit flags take precedence over `REPO_FOUNDRY_*` environment/repository variables, which take precedence over that file and automatic detection. See -[Initialization and synchronization](docs/INITIALIZATION.md) for the full -configuration contract. +[Configuration reference](docs/CONFIGURATION.md) for the visual configuration +guide and [Initialization and synchronization](docs/INITIALIZATION.md) for +the safety and precedence rules. New repositories default to AGPL-3.0-or-later. Synchronization preserves an existing license unless a replacement is explicitly selected. Authored diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 00000000..f444009f --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,87 @@ +# Configuration reference + +`.github/code-foundry.yml` is the repository's single Code Foundry control plane. +Initialize from it with: + +```bash +npx code-foundry init --config .github/code-foundry.yml +``` + +After initialization, edit the file and run `npx code-foundry sync` to render +the selected callers and refresh the local baseline. Existing authored files +and custom workflows remain protected. + +## Configuration flow + +```text +code-foundry.yml + | + +--> profile and language detection + +--> enabled standard workflow callers + +--> runtime repository and ref + +--> workflow runner selection + +--> release, license, cache, and coverage policy +``` + +## Core settings + +| Key | Values | Purpose | +| --- | --- | --- | +| `profile` | `auto`, `application`, `monorepo`, `minimal` | Repository shape | +| `languages` | `auto` or comma-separated languages | Toolchain and CodeQL scope | +| `package_manager` | `auto`, `bun`, `pnpm`, `yarn`, `npm` | Locked JavaScript setup | +| `features` | `all` or comma-separated names | Standard callers to install | +| `prune_standard` | `true` or `false` | Remove disabled standard callers on sync | +| `runtime_repository` | `OWNER/REPO` or blank | Reusable workflow source | +| `runtime_ref` | tag or branch | Reusable workflow version | +| `release_type` | `auto`, `node`, `python`, `rust`, `simple`, `none` | Release Please strategy | +| `npm_publish` | `true` or `false` | Opt into npm publication | +| `license` | license name, `preserve`, `none` | License generation policy | +| `license_file` | path | Optional exact license source | + +Supported languages are TypeScript, Rust, Python, and Solidity. Supported +features are `ci`, `codeql`, `security`, `test`, `draft-pr`, `release-pr`, +`release`, and `dependabot`. + +## Workflow runners + +Each caller can select its default runner without editing workflow YAML: + +| Key | Default | Controls | +| --- | --- | --- | +| `runner` | `ubuntu-latest` | Shared fallback | +| `unit_runner` | `ubuntu-slim` | Unit tests | +| `ci_runner` | `ubuntu-latest` | Format, lint, type-check, build | +| `test_runner` | `ubuntu-latest` | Integration, E2E, smoke | +| `security_runner` | `ubuntu-slim` | Profile and dependency security jobs | +| `codeql_runner` | `ubuntu-latest` | CodeQL detection and analyzers | +| `pr_runner` | `ubuntu-slim` | Draft and Release PR automation | +| `release_runner` | `ubuntu-slim` | Release and package publication | + +Use the full runner for native toolchains, browsers, or measured dependency +workloads. Use `ubuntu-slim` for lightweight orchestration and small projects. + +## Performance and quality + +| Key | Default | Purpose | +| --- | --- | --- | +| `cache_packages` | `auto` | Package-store caching policy | +| `cache_build` | `auto` | Build-cache policy | +| `coverage_minimum` | `80` | Shared Bun/Python coverage target | +| `turbo_remote` | `auto` | Turborepo remote-cache policy | + +Turborepo remote caching still requires the repository secret `TURBO_TOKEN` and +the variable `TURBO_TEAM`; the file controls policy, not credentials. + +## Precedence + +The resolved value order is: + +```text +explicit CLI flag -> REPO_FOUNDRY_* variable -> code-foundry.yml -> detection/default +``` + +Use variables for temporary CI overrides. Keep durable repository policy in +`.github/code-foundry.yml` so humans and agents have one discoverable source of +truth. The example file is a commented starter guide; it is intentionally not +a second generated configuration. diff --git a/docs/INITIALIZATION.md b/docs/INITIALIZATION.md index 12c85f4a..6cb20fea 100644 --- a/docs/INITIALIZATION.md +++ b/docs/INITIALIZATION.md @@ -10,6 +10,17 @@ npx code-foundry sync [options] npx code-foundry doctor ``` +When `.github/code-foundry.yml` already exists, `init` treats it as the primary +configuration and does not replace its values with CLI defaults. To use another +file, pass it explicitly: + +```bash +npx code-foundry init --config ./configs/code-foundry.yml +``` + +The file is copied to `.github/code-foundry.yml`, normalized, and used to render +the standard callers. Later edits can be applied with `npx code-foundry sync`. + Use `--dry-run` to preview changes. Use `--no-bootstrap` when the repository is being initialized in CI or on a machine without mise. Run `bash .github/scripts/bootstrap.sh` later to install tools, enable hooks, and @@ -38,7 +49,7 @@ npx code-foundry init \ --runtime-ref v1.2.3 ``` -Both values are persisted in `.github/template.yml` and rendered into the +Both values are persisted in `.github/code-foundry.yml` and rendered into the standard callers. `REPO_FOUNDRY_RUNTIME_REPOSITORY` and `REPO_FOUNDRY_RUNTIME_REF` provide equivalent environment/repository-variable overrides. @@ -51,6 +62,6 @@ documentation. Existing licenses are preserved unless a license or license file is explicitly selected. Use `--force` only when intentionally refreshing protected standard documents. -The initializer generates `.github/template.yml` as the repository-owned +The initializer generates `.github/code-foundry.yml` as the repository-owned configuration contract. Keep project-specific settings there rather than editing generated workflow callers by hand. diff --git a/docs/README.md b/docs/README.md index a2acd6b6..d7a5ca3b 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ its own names, environments, and deployment details. ## Guides - [Initialization and synchronization](INITIALIZATION.md) +- [Configuration reference](CONFIGURATION.md) - [Workflow and CI conventions](WORKFLOWS.md) - [Release management](RELEASES.md) - [Publishing packages](PUBLISHING.md) diff --git a/docs/RELEASES.md b/docs/RELEASES.md index 03913e99..14ef5538 100644 --- a/docs/RELEASES.md +++ b/docs/RELEASES.md @@ -21,7 +21,7 @@ body. Existing `CHANGELOG.md` history remains repository-owned. ## Configuration -Set these values in `.github/template.yml`: +Set these values in `.github/code-foundry.yml`: ```yaml release_type: auto # auto, node, python, rust, simple, or none diff --git a/src/cli.mjs b/src/cli.mjs index ef929bf1..61a4cc1f 100644 --- a/src/cli.mjs +++ b/src/cli.mjs @@ -18,6 +18,7 @@ Init/sync options: --target PATH Repository directory (default: current directory) --source PATH_OR_URL Template source override --ref REF Template branch or tag (default: main) + --config PATH Use a .github/code-foundry.yml configuration file --profile NAME auto, application, monorepo, or minimal --languages LIST auto or typescript,rust,python,solidity --features LIST all or ci,codeql,security,test,draft-pr,release-pr,release,dependabot @@ -47,6 +48,7 @@ function parseArgs(argv) { target: process.cwd(), source: packageRoot, ref: 'main', + config: process.env.REPO_FOUNDRY_CONFIG || '', profile: process.env.REPO_FOUNDRY_PROFILE || 'auto', languages: process.env.REPO_FOUNDRY_LANGUAGES || 'auto', features: process.env.REPO_FOUNDRY_FEATURES || 'all', @@ -62,6 +64,14 @@ function parseArgs(argv) { npmPublish: process.env.REPO_FOUNDRY_NPM_PUBLISH === 'true', languagesSet: false, featuresSet: false, + profileSet: Boolean(process.env.REPO_FOUNDRY_PROFILE), + packageManagerSet: Boolean(process.env.REPO_FOUNDRY_PACKAGE_MANAGER), + runtimeRepositorySet: Boolean(process.env.REPO_FOUNDRY_RUNTIME_REPOSITORY), + runtimeRefSet: Boolean(process.env.REPO_FOUNDRY_RUNTIME_REF), + releaseTypeSet: Boolean(process.env.REPO_FOUNDRY_RELEASE_TYPE), + licenseSet: Boolean(process.env.REPO_FOUNDRY_LICENSE), + licenseFileSet: Boolean(process.env.REPO_FOUNDRY_LICENSE_FILE), + npmPublishSet: Boolean(process.env.REPO_FOUNDRY_NPM_PUBLISH), dryRun: false, prune: false, force: false, @@ -72,6 +82,7 @@ function parseArgs(argv) { ['--target', 'target'], ['--source', 'source'], ['--ref', 'ref'], + ['--config', 'config'], ['--profile', 'profile'], ['--languages', 'languages'], ['--features', 'features'], @@ -95,6 +106,13 @@ function parseArgs(argv) { options[values.get(arg)] = value if (arg === '--languages') options.languagesSet = true if (arg === '--features') options.featuresSet = true + if (arg === '--profile') options.profileSet = true + if (arg === '--package-manager') options.packageManagerSet = true + if (arg === '--runtime-repository') options.runtimeRepositorySet = true + if (arg === '--runtime-ref') options.runtimeRefSet = true + if (arg === '--release-type') options.releaseTypeSet = true + if (arg === '--license') options.licenseSet = true + if (arg === '--license-file') options.licenseFileSet = true continue } if (arg === '--dry-run') options.dryRun = true @@ -102,7 +120,7 @@ function parseArgs(argv) { else if (arg === '--prune') options.prune = true else if (arg === '--protection') options.protection = true else if (arg === '--no-bootstrap') options.bootstrap = false - else if (arg === '--npm-publish') options.npmPublish = true + else if (arg === '--npm-publish') { options.npmPublish = true; options.npmPublishSet = true } else fail(`unknown option: ${arg}`) } @@ -125,12 +143,16 @@ function main() { const { command, options } = parseArgs(process.argv.slice(2)) const target = resolve(options.target) const common = ['--source', options.source, '--ref', options.ref] - common.push('--license', options.license) - if (options.licenseFile) common.push('--license-file', options.licenseFile) + if (options.config) common.push('--config', options.config) + if (options.profileSet) common.push('--profile', options.profile) if (options.languagesSet) common.push('--languages', options.languages) if (options.featuresSet) common.push('--features', options.features) - if (options.runtimeRepository) common.push('--runtime-repository', options.runtimeRepository) - if (options.runtimeRef) common.push('--runtime-ref', options.runtimeRef) + if (options.packageManagerSet) common.push('--package-manager', options.packageManager) + if (options.runtimeRepositorySet) common.push('--runtime-repository', options.runtimeRepository) + if (options.runtimeRefSet) common.push('--runtime-ref', options.runtimeRef) + if (options.releaseTypeSet) common.push('--release-type', options.releaseType) + if (options.licenseSet) common.push('--license', options.license) + if (options.licenseFileSet && options.licenseFile) common.push('--license-file', options.licenseFile) if (options.prune) common.push('--prune') if (options.force) common.push('--force') if (options.dryRun) common.push('--check') @@ -140,21 +162,22 @@ function main() { const initArgs = [ '--source', options.source, '--ref', options.ref, - '--profile', options.profile, - '--languages', options.languages, - '--features', options.features, - '--package-manager', options.packageManager, - '--release-type', options.releaseType, - '--license', options.license, ] - if (options.runtimeRepository) initArgs.push('--runtime-repository', options.runtimeRepository) - if (options.runtimeRef) initArgs.push('--runtime-ref', options.runtimeRef) - if (options.licenseFile) initArgs.push('--license-file', options.licenseFile) + if (options.profileSet) initArgs.push('--profile', options.profile) + if (options.languagesSet) initArgs.push('--languages', options.languages) + if (options.featuresSet) initArgs.push('--features', options.features) + if (options.packageManagerSet) initArgs.push('--package-manager', options.packageManager) + if (options.releaseTypeSet) initArgs.push('--release-type', options.releaseType) + if (options.licenseSet) initArgs.push('--license', options.license) + if (options.config) initArgs.push('--config', options.config) + if (options.runtimeRepositorySet) initArgs.push('--runtime-repository', options.runtimeRepository) + if (options.runtimeRefSet) initArgs.push('--runtime-ref', options.runtimeRef) + if (options.licenseFileSet && options.licenseFile) initArgs.push('--license-file', options.licenseFile) if (options.protection) initArgs.push('--protection') if (options.dryRun) initArgs.push('--dry-run') if (options.prune) initArgs.push('--prune') if (options.force) initArgs.push('--force') - if (options.npmPublish) initArgs.push('--npm-publish') + if (options.npmPublishSet && options.npmPublish) initArgs.push('--npm-publish') if (!options.bootstrap) initArgs.push('--no-bootstrap') run('init-repo.sh', initArgs, target) } else if (command === 'sync') { From e21fcabcb7b70df92c0b7b9604e68ece9b99be70 Mon Sep 17 00:00:00 2001 From: NiftyAndy Date: Tue, 28 Jul 2026 12:43:11 -0400 Subject: [PATCH 2/3] fix: keep legacy runtime callers valid --- .github/workflows/draft-pr.yml | 1 - .github/workflows/release-pr.yml | 1 - .github/workflows/release.yml | 1 - 3 files changed, 3 deletions(-) diff --git a/.github/workflows/draft-pr.yml b/.github/workflows/draft-pr.yml index 1a884be7..af04e13c 100644 --- a/.github/workflows/draft-pr.yml +++ b/.github/workflows/draft-pr.yml @@ -19,5 +19,4 @@ jobs: draft-pr: name: Draft PR uses: 0xPlayerOne/code-foundry/.github/workflows/reusable-draft-pr.yml@v0.20.2 - with: secrets: inherit diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml index 69c2f9f8..1f6c0a2a 100644 --- a/.github/workflows/release-pr.yml +++ b/.github/workflows/release-pr.yml @@ -12,5 +12,4 @@ jobs: release-pr: name: Release PR uses: 0xPlayerOne/code-foundry/.github/workflows/reusable-release-pr.yml@v0.20.2 - with: secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9ba1e72a..a99b01cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,5 +15,4 @@ jobs: release: name: Release uses: 0xPlayerOne/code-foundry/.github/workflows/reusable-release.yml@v0.20.2 - with: secrets: inherit From 87c40bf399f9fa3402528ed92b5beac8aacff105 Mon Sep 17 00:00:00 2001 From: NiftyAndy Date: Tue, 28 Jul 2026 12:44:12 -0400 Subject: [PATCH 3/3] fix: bridge legacy runtime configuration --- .github/template.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/template.yml diff --git a/.github/template.yml b/.github/template.yml new file mode 100644 index 00000000..952d6484 --- /dev/null +++ b/.github/template.yml @@ -0,0 +1,4 @@ +# Temporary compatibility shim for reusable workflow runtime v0.20.2. +# Removed after the runtime tag that understands .github/code-foundry.yml is published. +package_manager: bun +languages: typescript