Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .config/.copier-answers.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Changes here will be overwritten by Copier
_commit: v0.0.139
_commit: v0.0.140
_src_path: gh:LabAutomationAndScreening/copier-base-template.git
copier_answers_directory: .config
description: Copier template for creating Python libraries and executables
Expand Down
10 changes: 5 additions & 5 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@
"extensions": [
// basic tooling
// "eamodio.gitlens@15.5.1",
"coderabbit.coderabbit-vscode@0.20.7",
"coderabbit.coderabbit-vscode@0.21.0",
"ms-vscode.live-server@0.5.2025051301",
"MS-vsliveshare.vsliveshare@1.0.5905",
"anthropic.claude-code@2.1.205",
"anthropic.claude-code@2.1.211",

// Python
"ms-python.python@2026.5.2026052901",
"meta.pyrefly@1.1.9001",
"meta.pyrefly@1.1.9002",
"ms-vscode-remote.remote-containers@0.414.0",
"charliermarsh.ruff@2026.56.0",
"charliermarsh.ruff@2026.60.0",

// Misc file formats
"bierner.markdown-mermaid@1.29.0",
Expand Down Expand Up @@ -60,5 +60,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): 2c1bcc28 # spellchecker:disable-line
// Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): d56eca75 # spellchecker:disable-line
}
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ repos:

# Reformatting (should generally come before any file format or other checks, because reformatting can change things)
- repo: https://github.com/crate-ci/typos
rev: 359573a3ff38f71c93e160f2ace96d118f6ff42e # frozen: v1
rev: 96d9af6217dc2855210655af7e1ff19d23d4e593 # frozen: v1
hooks:
- id: typos
args: [--write-changes, --force-exclude, --config, .config/_typos.toml]
Expand Down Expand Up @@ -113,7 +113,7 @@ repos:
)$

- repo: https://github.com/rbubley/mirrors-prettier
rev: 515f543f5718ebfd6ce22e16708bb32c68ff96e1 # frozen: v3.8.3
rev: 9337a74165b178ae2c766f60bee7252a0f06f3e8 # frozen: v3.9.5
hooks:
- id: prettier
args: [--config, .config/.prettierrc]
Expand Down Expand Up @@ -185,7 +185,7 @@ repos:
- id: check-case-conflict

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 943377262562a12b57292fc98fabd7dbf81451fe # frozen: 0.37.2
rev: 1a4bb160cab6417b3045e1b37b6b72449243e658 # frozen: 0.37.4
hooks:
- id: check-github-workflows

Expand Down Expand Up @@ -239,7 +239,7 @@ repos:
description: Runs hadolint to lint Dockerfiles

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 6fec9b7edb08fd9989088709d864a7826dc74e80 # frozen: v0.15.12
rev: 2700fd5671c633760d912769c041bfcde2b9a01b # frozen: v0.15.22
hooks:
- id: ruff
name: ruff-src
Expand Down Expand Up @@ -270,7 +270,7 @@ repos:
)$

- repo: https://github.com/pylint-dev/pylint
rev: 88e1ab7545a4af4aea15c305a154c164a95ab842 # frozen: v4.0.5
rev: 8a396357098337ba8be714fffdf2d00947a9778c # frozen: v4.0.6
hooks:
- id: pylint
name: pylint
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This project is a Copier template used to generate other copier templates. It is
- When disabling a linting rule with an inline directive, provide a comment at the end of the line (or on the line above for tools that don't allow extra text after an inline directive) describing the reasoning for disabling the rule.
- Avoid telling the type checker what a type is rather than letting it prove it. This includes type assertions (`as SomeType` in TypeScript, `cast()` in Python) and variable annotations that override inference. Prefer approaches that let the type checker verify the type itself: `isinstance`/`instanceof` narrowing, restructuring code so the correct type flows naturally, or using discriminated unions. When there is genuinely no alternative, add a comment explaining why the workaround is necessary and why it is safe.
- Avoid `||` (TypeScript) or `or` (Python) in `if`/`elif` conditions, and avoid `x in ['a', 'b']`-style membership tests in implementation code — coverage tools treat these as a single branch, silently masking untested paths and producing false 100% branch coverage. Use separate `if`/`elif` branches instead so each condition is independently covered.
- When filtering logic combines multiple `and`-joined guards (e.g. a null check alongside a value check), prefer a loop with explicit `if`/`continue` branches over a single-line comprehension. A compound boolean filter on one line hides individual branches from line coverage — each guard condition should be its own statement so missing test cases are surfaced.

## Testing

Expand All @@ -24,7 +25,6 @@ This project is a Copier template used to generate other copier templates. It is
- Avoid magic values in comparisons in tests in all languages (like ruff rule PLR2004 specifies). Note: `1` and `0` are not magic numbers (according to PLR2004)
- Prefer using random values in tests rather than arbitrary ones (e.g. the faker library, uuids, random.randint) when possible. For enums, pick randomly rather than hardcoding one value.
- Avoid loops in tests — assert each item explicitly so failures pinpoint the exact element. When verifying a condition across all items in a collection, collect the violations into a list and assert it's empty (e.g., assert [x for x in items if bad_condition(x)] == []).
- When filtering logic combines multiple `and`-joined guards (e.g. a null check alongside a value check), prefer a loop with explicit `if`/`continue` branches over a single-line comprehension. A compound boolean filter on one line hides individual branches from line coverage — each guard condition should be its own statement so missing test cases are surfaced.
- When a test's final assertion is an absence (e.g., element is `null`, list is empty, modal is closed), include a prior presence assertion confirming the expected state existed before the action that removed it. A test whose only assertion is an absence check can pass vacuously if setup silently failed.
- When asserting a mock or spy was called with specific arguments, always constrain as tightly as possible. In order of preference: (1) assert called exactly once with those args (`assert_called_once_with` in Python, `toHaveBeenCalledExactlyOnceWith` in Vitest/Jest); (2) if multiple calls are expected, assert the total call count and use a positional or last-call assertion (`nthCalledWith`, `lastCalledWith` / `assert_has_calls` with `call_args_list[n]`); (3) plain "called with at any point" (`toHaveBeenCalledWith`, `assert_called_with`) is a last resort only when neither the call count nor the call order can reasonably be constrained.
- When asserting an exception is raised, verify the error message includes all key constructor arguments — not just one identifying field. This ensures the error message is fully populated and catches cases where arguments are swapped or missing. In Python: use the `match` parameter in `pytest.raises`. In TypeScript: use a regex or substring in `toThrow`, or catch and assert on error properties individually.
Expand Down
8 changes: 4 additions & 4 deletions template/.devcontainer/devcontainer.json.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@
"-AmazonWebServices.aws-toolkit-vscode", // the AWS CLI feature installs this automatically, but it's causing problems in VS Code{% endraw %}{% endif %}{% raw %}
// basic tooling
// "eamodio.gitlens@15.5.1",
"coderabbit.coderabbit-vscode@0.20.7",
"coderabbit.coderabbit-vscode@0.21.0",
"ms-vscode.live-server@0.5.2025051301",
"MS-vsliveshare.vsliveshare@1.0.5905",{% endraw %}{% if install_claude_cli %}{% raw %}
"anthropic.claude-code@2.1.205",{% endraw %}{% endif %}{% raw %}{% endraw %}{% if (is_child_of_copier_base_template is defined and is_child_of_copier_base_template is sameas(true)) or (is_child_of_copier_base_template is not defined and template_uses_python is defined and template_uses_python is sameas(true)) %}{% raw %}
"anthropic.claude-code@2.1.211",{% endraw %}{% endif %}{% raw %}{% endraw %}{% if (is_child_of_copier_base_template is defined and is_child_of_copier_base_template is sameas(true)) or (is_child_of_copier_base_template is not defined and template_uses_python is defined and template_uses_python is sameas(true)) %}{% raw %}

// Python
"ms-python.python@2026.5.2026052901",
"meta.pyrefly@1.1.9001",
"meta.pyrefly@1.1.9002",
"ms-vscode-remote.remote-containers@0.414.0",
"charliermarsh.ruff@2026.56.0",{% endraw %}{% endif %}{% raw %}
"charliermarsh.ruff@2026.60.0",{% endraw %}{% endif %}{% raw %}
{% endraw %}{% if is_child_of_copier_base_template is not defined and template_uses_vuejs is defined and template_uses_vuejs is sameas(true) %}{% raw %}
// VueJS
"vue.volar@3.2.5",
Expand Down
5 changes: 3 additions & 2 deletions template/.github/workflows/release.yaml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,9 @@ jobs:
runs-on: {% endraw %}{{ gha_linux_runner }}{% raw %}
timeout-minutes: {% endraw %}{{ gha_short_timeout_minutes }}{% raw %}
needs:
{% endraw %}{% if not deploy_as_executable %}{% raw %} - install-from-primary
{% endraw %}{% endif %}{% raw %} - guard
- guard{% endraw %}{% if not deploy_as_executable %}{% raw %}
- create-tag
- install-from-primary{% endraw %}{% endif %}{% raw %}
permissions:
contents: write # needed to create GitHub releases{% endraw %}{% if deploy_as_executable %}{% raw %}
actions: read # needed to download the executable artifacts from the CI run{% endraw %}{% endif %}{% raw %}
Expand Down
10 changes: 5 additions & 5 deletions template/.pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ repos:

# Reformatting (should generally come before any file format or other checks, because reformatting can change things)
- repo: https://github.com/crate-ci/typos
rev: 359573a3ff38f71c93e160f2ace96d118f6ff42e # frozen: v1
rev: 96d9af6217dc2855210655af7e1ff19d23d4e593 # frozen: v1
hooks:
- id: typos
args: [--write-changes, --force-exclude, --config, .config/_typos.toml]
Expand Down Expand Up @@ -113,7 +113,7 @@ repos:
)$

- repo: https://github.com/rbubley/mirrors-prettier
rev: 515f543f5718ebfd6ce22e16708bb32c68ff96e1 # frozen: v3.8.3
rev: 9337a74165b178ae2c766f60bee7252a0f06f3e8 # frozen: v3.9.5
hooks:
- id: prettier
args: [--config, .config/.prettierrc]
Expand Down Expand Up @@ -185,7 +185,7 @@ repos:
- id: check-case-conflict

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 943377262562a12b57292fc98fabd7dbf81451fe # frozen: 0.37.2
rev: 1a4bb160cab6417b3045e1b37b6b72449243e658 # frozen: 0.37.4
hooks:
- id: check-github-workflows

Expand Down Expand Up @@ -239,7 +239,7 @@ repos:
description: Runs hadolint to lint Dockerfiles

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 6fec9b7edb08fd9989088709d864a7826dc74e80 # frozen: v0.15.12
rev: 2700fd5671c633760d912769c041bfcde2b9a01b # frozen: v0.15.22
hooks:
- id: ruff
name: ruff-src
Expand Down Expand Up @@ -270,7 +270,7 @@ repos:
)$

- repo: https://github.com/pylint-dev/pylint
rev: 88e1ab7545a4af4aea15c305a154c164a95ab842 # frozen: v4.0.5
rev: 8a396357098337ba8be714fffdf2d00947a9778c # frozen: v4.0.6
hooks:
- id: pylint
name: pylint
Expand Down
2 changes: 1 addition & 1 deletion template/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This project is a Python library.
- When disabling a linting rule with an inline directive, provide a comment at the end of the line (or on the line above for tools that don't allow extra text after an inline directive) describing the reasoning for disabling the rule.
- Avoid telling the type checker what a type is rather than letting it prove it. This includes type assertions (`as SomeType` in TypeScript, `cast()` in Python) and variable annotations that override inference. Prefer approaches that let the type checker verify the type itself: `isinstance`/`instanceof` narrowing, restructuring code so the correct type flows naturally, or using discriminated unions. When there is genuinely no alternative, add a comment explaining why the workaround is necessary and why it is safe.
- Avoid `||` (TypeScript) or `or` (Python) in `if`/`elif` conditions, and avoid `x in ['a', 'b']`-style membership tests in implementation code — coverage tools treat these as a single branch, silently masking untested paths and producing false 100% branch coverage. Use separate `if`/`elif` branches instead so each condition is independently covered.
- When filtering logic combines multiple `and`-joined guards (e.g. a null check alongside a value check), prefer a loop with explicit `if`/`continue` branches over a single-line comprehension. A compound boolean filter on one line hides individual branches from line coverage — each guard condition should be its own statement so missing test cases are surfaced.

## Testing

Expand All @@ -24,7 +25,6 @@ This project is a Python library.
- Avoid magic values in comparisons in tests in all languages (like ruff rule PLR2004 specifies). Note: `1` and `0` are not magic numbers (according to PLR2004)
- Prefer using random values in tests rather than arbitrary ones (e.g. the faker library, uuids, random.randint) when possible. For enums, pick randomly rather than hardcoding one value.
- Avoid loops in tests — assert each item explicitly so failures pinpoint the exact element. When verifying a condition across all items in a collection, collect the violations into a list and assert it's empty (e.g., assert [x for x in items if bad_condition(x)] == []).
- When filtering logic combines multiple `and`-joined guards (e.g. a null check alongside a value check), prefer a loop with explicit `if`/`continue` branches over a single-line comprehension. A compound boolean filter on one line hides individual branches from line coverage — each guard condition should be its own statement so missing test cases are surfaced.
- When a test's final assertion is an absence (e.g., element is `null`, list is empty, modal is closed), include a prior presence assertion confirming the expected state existed before the action that removed it. A test whose only assertion is an absence check can pass vacuously if setup silently failed.
- When asserting a mock or spy was called with specific arguments, always constrain as tightly as possible. In order of preference: (1) assert called exactly once with those args (`assert_called_once_with` in Python, `toHaveBeenCalledExactlyOnceWith` in Vitest/Jest); (2) if multiple calls are expected, assert the total call count and use a positional or last-call assertion (`nthCalledWith`, `lastCalledWith` / `assert_has_calls` with `call_args_list[n]`); (3) plain "called with at any point" (`toHaveBeenCalledWith`, `assert_called_with`) is a last resort only when neither the call count nor the call order can reasonably be constrained.
- When asserting an exception is raised, verify the error message includes all key constructor arguments — not just one identifying field. This ensures the error message is fully populated and catches cases where arguments are swapped or missing. In Python: use the `match` parameter in `pytest.raises`. In TypeScript: use a regex or substring in `toThrow`, or catch and assert on error properties individually.
Expand Down