From 0ace3861eb58fdace5bfadc95bb6bd60c5784116 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Tue, 13 Oct 2026 11:15:46 +0000 Subject: [PATCH 1/3] build: install pre-commit tooling and copier only in the CI jobs that use them --- .devcontainer/devcontainer.json | 2 +- .devcontainer/install-ci-tooling.py | 91 ++++++++++++------- .github/workflows/ci.yaml | 4 +- .../install-ci-tooling.py.jinja-base | 91 ++++++++++++------- .../install_deps/action.yml.jinja-base | 10 +- template/.github/workflows/ci.yaml.jinja-base | 2 + .../workflows/pre-commit.yaml.jinja-base | 1 + 7 files changed, 129 insertions(+), 72 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9d12583a..c57fbdc9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -71,5 +71,5 @@ "initializeCommand": "sh .devcontainer/initialize-command.sh", "onCreateCommand": "sh .devcontainer/on-create-command.sh", "postStartCommand": "sh .devcontainer/post-start-command.sh" - // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): e3cf4594 # spellchecker:disable-line + // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): f5f64ab5 # spellchecker:disable-line } diff --git a/.devcontainer/install-ci-tooling.py b/.devcontainer/install-ci-tooling.py index 30e47f60..8a96f8a4 100644 --- a/.devcontainer/install-ci-tooling.py +++ b/.devcontainer/install-ci-tooling.py @@ -50,6 +50,27 @@ type=str, help="What version to install.", ) +_ = parser.add_argument( + "--install-pre-commit-tooling", + default=False, + action="store_true", + help="Install pre-commit, identify and prek. Always installed outside CI.", +) +_ = parser.add_argument( + "--install-copier", + default=False, + action="store_true", + help="Install copier. Always installed outside CI.", +) + + +def should_install(*, requested: bool) -> bool: + if requested: + return True + # GitHub Actions always sets CI, so its absence means a developer's machine or devcontainer, which gets every tool + if "CI" not in os.environ: + return True + return False def run_with_retries( @@ -176,40 +197,42 @@ def main(): _ = subprocess.run(["pnpm -v"], shell=True, check=True) # noqa: S602,S607 # we need shell=True for pnpm commands, and this is all our own input install_uv(uv_path=uv_path, uv_env=uv_env) if not args.no_python: - run_with_retries( - [ - uv_path, - "tool", - "install", - f"copier{COPIER_VERSION}", - "--with", - f"copier-template-extensions{COPIER_TEMPLATE_EXTENSIONS_VERSION}", - ], - description="Installing copier", - env=uv_env, - ) - run_with_retries( - [ - uv_path, - "tool", - "install", - f"pre-commit{PRE_COMMIT_VERSION}", - "--with", - f"identify{IDENTIFY_VERSION}", - ], - description="Installing pre-commit", - env=uv_env, - ) - run_with_retries( - [ - uv_path, - "tool", - "install", - f"prek{PREK_VERSION}", - ], - description="Installing prek", - env=uv_env, - ) + if should_install(requested=args.install_copier): + run_with_retries( + [ + uv_path, + "tool", + "install", + f"copier{COPIER_VERSION}", + "--with", + f"copier-template-extensions{COPIER_TEMPLATE_EXTENSIONS_VERSION}", + ], + description="Installing copier", + env=uv_env, + ) + if should_install(requested=args.install_pre_commit_tooling): + run_with_retries( + [ + uv_path, + "tool", + "install", + f"pre-commit{PRE_COMMIT_VERSION}", + "--with", + f"identify{IDENTIFY_VERSION}", + ], + description="Installing pre-commit", + env=uv_env, + ) + run_with_retries( + [ + uv_path, + "tool", + "install", + f"prek{PREK_VERSION}", + ], + description="Installing prek", + env=uv_env, + ) install_task(uv_path=uv_path, uv_env=uv_env) _ = subprocess.run( # noqa: S603 # this is all our own input [ diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d68f7c14..ada1a75d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -35,7 +35,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install Tooling - run: python .devcontainer/install-ci-tooling.py + run: python .devcontainer/install-ci-tooling.py --install-pre-commit-tooling - name: Install Dependencies run: | @@ -157,7 +157,7 @@ jobs: node-version: 24.11.1 - name: Install Tooling - run: python .devcontainer/install-ci-tooling.py + run: python .devcontainer/install-ci-tooling.py --install-copier --install-pre-commit-tooling - name: Instantiate copier template run: | diff --git a/template/.devcontainer/install-ci-tooling.py.jinja-base b/template/.devcontainer/install-ci-tooling.py.jinja-base index 544c4f89..2a9bfa9f 100644 --- a/template/.devcontainer/install-ci-tooling.py.jinja-base +++ b/template/.devcontainer/install-ci-tooling.py.jinja-base @@ -72,6 +72,27 @@ _ = parser.add_argument( default=False, help="Skip installing the SSM plugin for AWS CLI", ) +_ = parser.add_argument( + "--install-pre-commit-tooling", + action="store_true", + default=False, + help="Install pre-commit, identify and prek. Always installed outside CI.", +) +_ = parser.add_argument( + "--install-copier", + action="store_true", + default=False, + help="Install copier. Always installed outside CI.", +) + + +def should_install(*, requested: bool) -> bool: + if requested: + return True + # GitHub Actions always sets CI, so its absence means a developer's machine or devcontainer, which gets every tool + if "CI" not in os.environ: + return True + return False def pwsh_cmd(cmd: str) -> list[str]: @@ -352,40 +373,42 @@ def main(): uv_path = "uv" install_uv(uv_path=uv_path, uv_env=uv_env, is_windows=is_windows) if not args.no_python: - run_with_retries( - [ - uv_path, - "tool", - "install", - f"copier{COPIER_VERSION}", - "--with", - f"copier-template-extensions{COPIER_TEMPLATE_EXTENSIONS_VERSION}", - ], - description="Installing copier", - env=uv_env, - ) - run_with_retries( - [ - uv_path, - "tool", - "install", - f"pre-commit{PRE_COMMIT_VERSION}", - "--with", - f"identify{IDENTIFY_VERSION}", - ], - description="Installing pre-commit", - env=uv_env, - ) - run_with_retries( - [ - uv_path, - "tool", - "install", - f"prek{PREK_VERSION}", - ], - description="Installing prek", - env=uv_env, - ) + if should_install(requested=args.install_copier): + run_with_retries( + [ + uv_path, + "tool", + "install", + f"copier{COPIER_VERSION}", + "--with", + f"copier-template-extensions{COPIER_TEMPLATE_EXTENSIONS_VERSION}", + ], + description="Installing copier", + env=uv_env, + ) + if should_install(requested=args.install_pre_commit_tooling): + run_with_retries( + [ + uv_path, + "tool", + "install", + f"pre-commit{PRE_COMMIT_VERSION}", + "--with", + f"identify{IDENTIFY_VERSION}", + ], + description="Installing pre-commit", + env=uv_env, + ) + run_with_retries( + [ + uv_path, + "tool", + "install", + f"prek{PREK_VERSION}", + ], + description="Installing prek", + env=uv_env, + ) if not args.no_node: install_pnpm(is_windows=is_windows) # Task is installed outside the --no-node branch because CI always passes --no-node (pnpm/setup handles pnpm there), and every job still needs the task runner diff --git a/template/.github/actions/install_deps/action.yml.jinja-base b/template/.github/actions/install_deps/action.yml.jinja-base index 7529c662..bbf89560 100644 --- a/template/.github/actions/install_deps/action.yml.jinja-base +++ b/template/.github/actions/install_deps/action.yml.jinja-base @@ -21,6 +21,14 @@ inputs: required: false default: false description: Whether to explicitly skip installing the SSM Plugin manager when setting up basic CI tooling + install-pre-commit-tooling: + required: false + default: false + description: Whether to install the tooling the pre-commit hooks need (pre-commit, identify and prek), for jobs that run pre-commit + install-copier: + required: false + default: false + description: Whether to install copier, for jobs that instantiate a copier template project-dir: description: What's the relative path to the project? required: false @@ -95,7 +103,7 @@ runs: - name: Install tooling # the funky syntax is github action ternary # --no-node is always passed because the only node tooling this script installs is pnpm, which pnpm/setup above already installed - run: python .devcontainer/install-ci-tooling.py ${{ inputs.python-version == 'notUsing' && '--no-python' || '' }} --no-node ${{ inputs.skip-installing-ssm-plugin-manager == 'true' && '--skip-installing-ssm-plugin' || '' }} + run: python .devcontainer/install-ci-tooling.py ${{ inputs.python-version == 'notUsing' && '--no-python' || '' }} --no-node ${{ inputs.skip-installing-ssm-plugin-manager == 'true' && '--skip-installing-ssm-plugin' || '' }} ${{ inputs.install-pre-commit-tooling == 'true' && '--install-pre-commit-tooling' || '' }} ${{ inputs.install-copier == 'true' && '--install-copier' || '' }} shell: pwsh - name: OIDC Auth for CodeArtifact diff --git a/template/.github/workflows/ci.yaml.jinja-base b/template/.github/workflows/ci.yaml.jinja-base index b58e7357..a9c0b2f4 100644 --- a/template/.github/workflows/ci.yaml.jinja-base +++ b/template/.github/workflows/ci.yaml.jinja-base @@ -124,6 +124,8 @@ jobs: python-version: ${{ matrix.python-version }}{% endraw %}{% if template_uses_typescript is defined and template_uses_typescript is sameas(true) %}{% raw %} node-version: {% endraw %}{{ default_node_version }}{% raw %}{% endraw %}{% endif %}{% raw %} install-deps: false + install-copier: true + install-pre-commit-tooling: true - name: Instantiate copier template run: | diff --git a/template/.github/workflows/pre-commit.yaml.jinja-base b/template/.github/workflows/pre-commit.yaml.jinja-base index 3283cc7e..26e3450c 100644 --- a/template/.github/workflows/pre-commit.yaml.jinja-base +++ b/template/.github/workflows/pre-commit.yaml.jinja-base @@ -51,6 +51,7 @@ jobs: node-version: ${{ inputs.node-version }} skip-installing-ssm-plugin-manager: true skip-installing-pulumi-cli: true + install-pre-commit-tooling: true - name: Compute Pre-commit cache key # must run after checkout, since hashFiles needs the file on disk run: | From 39fb0dac24d0257beea2c66d4d6a2f393c86cf03 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Tue, 13 Oct 2026 12:23:54 +0000 Subject: [PATCH 2/3] build: say the install flags only matter in CI, since the script installs everything without the CI variable --- .devcontainer/devcontainer.json | 2 +- .devcontainer/install-ci-tooling.py | 4 ++-- template/.devcontainer/install-ci-tooling.py.jinja-base | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c57fbdc9..5977aec2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -71,5 +71,5 @@ "initializeCommand": "sh .devcontainer/initialize-command.sh", "onCreateCommand": "sh .devcontainer/on-create-command.sh", "postStartCommand": "sh .devcontainer/post-start-command.sh" - // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): f5f64ab5 # spellchecker:disable-line + // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): fe536220 # spellchecker:disable-line } diff --git a/.devcontainer/install-ci-tooling.py b/.devcontainer/install-ci-tooling.py index 8a96f8a4..98748dbc 100644 --- a/.devcontainer/install-ci-tooling.py +++ b/.devcontainer/install-ci-tooling.py @@ -54,13 +54,13 @@ "--install-pre-commit-tooling", default=False, action="store_true", - help="Install pre-commit, identify and prek. Always installed outside CI.", + help="Install pre-commit, identify and prek in CI. Without the CI env var, this script always installs them.", ) _ = parser.add_argument( "--install-copier", default=False, action="store_true", - help="Install copier. Always installed outside CI.", + help="Install copier in CI. Without the CI env var, this script always installs it.", ) diff --git a/template/.devcontainer/install-ci-tooling.py.jinja-base b/template/.devcontainer/install-ci-tooling.py.jinja-base index 2a9bfa9f..7e2bf305 100644 --- a/template/.devcontainer/install-ci-tooling.py.jinja-base +++ b/template/.devcontainer/install-ci-tooling.py.jinja-base @@ -76,13 +76,13 @@ _ = parser.add_argument( "--install-pre-commit-tooling", action="store_true", default=False, - help="Install pre-commit, identify and prek. Always installed outside CI.", + help="Install pre-commit, identify and prek in CI. Without the CI env var, this script always installs them.", ) _ = parser.add_argument( "--install-copier", action="store_true", default=False, - help="Install copier. Always installed outside CI.", + help="Install copier in CI. Without the CI env var, this script always installs it.", ) From cfd4a02b88a807ca423cd6f9753d1429ccdcf3b7 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Tue, 13 Oct 2026 12:24:53 +0000 Subject: [PATCH 3/3] build: describe the install flags without naming the environment variable --- .devcontainer/devcontainer.json | 2 +- .devcontainer/install-ci-tooling.py | 4 ++-- template/.devcontainer/install-ci-tooling.py.jinja-base | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5977aec2..dca586ef 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -71,5 +71,5 @@ "initializeCommand": "sh .devcontainer/initialize-command.sh", "onCreateCommand": "sh .devcontainer/on-create-command.sh", "postStartCommand": "sh .devcontainer/post-start-command.sh" - // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): fe536220 # spellchecker:disable-line + // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): d487598c # spellchecker:disable-line } diff --git a/.devcontainer/install-ci-tooling.py b/.devcontainer/install-ci-tooling.py index 98748dbc..23c5bc44 100644 --- a/.devcontainer/install-ci-tooling.py +++ b/.devcontainer/install-ci-tooling.py @@ -54,13 +54,13 @@ "--install-pre-commit-tooling", default=False, action="store_true", - help="Install pre-commit, identify and prek in CI. Without the CI env var, this script always installs them.", + help="Install pre-commit, identify and prek in CI. Outside CI, this script always installs them.", ) _ = parser.add_argument( "--install-copier", default=False, action="store_true", - help="Install copier in CI. Without the CI env var, this script always installs it.", + help="Install copier in CI. Outside CI, this script always installs it.", ) diff --git a/template/.devcontainer/install-ci-tooling.py.jinja-base b/template/.devcontainer/install-ci-tooling.py.jinja-base index 7e2bf305..26ec9d58 100644 --- a/template/.devcontainer/install-ci-tooling.py.jinja-base +++ b/template/.devcontainer/install-ci-tooling.py.jinja-base @@ -76,13 +76,13 @@ _ = parser.add_argument( "--install-pre-commit-tooling", action="store_true", default=False, - help="Install pre-commit, identify and prek in CI. Without the CI env var, this script always installs them.", + help="Install pre-commit, identify and prek in CI. Outside CI, this script always installs them.", ) _ = parser.add_argument( "--install-copier", action="store_true", default=False, - help="Install copier in CI. Without the CI env var, this script always installs it.", + help="Install copier in CI. Outside CI, this script always installs it.", )