Skip to content
Draft
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
Expand Up @@ -15,7 +15,7 @@ ssh_port_number: 55874
template_might_want_to_install_aws_ssm_port_forwarding_plugin: true
template_might_want_to_use_python_asyncio: true
template_might_want_to_use_vcrpy: true
template_publishes_releases: false
template_publishes_releases: true
template_uses_pulumi: false
template_uses_python: true
template_uses_typescript: false
Expand Down
1 change: 1 addition & 0 deletions .config/.copier-managed-files.json
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@
"template/.github/actions/update-devcontainer-hash/action.yml",
"template/.github/pull_request_template.md",
"template/.github/workflows/confirm-on-tagged-copier-template.yaml",
"template/.github/workflows/extract_project_version.py",
"template/.github/workflows/get-values.yaml",
"template/.github/workflows/git_tag.py",
"template/.github/workflows/hash_git_files.py",
Expand Down
102 changes: 0 additions & 102 deletions copier_template_resources/git_tag.py

This file was deleted.

50 changes: 48 additions & 2 deletions template/.github/workflows/ci.yaml.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,50 @@ jobs:
token: ${{ secrets.CODECOV_TOKEN }}
slug: {% endraw %}{{ full_repo_url | replace("https://github.com/", "") }}{% raw %}{% endraw %}{% endif %}{% raw %}

{% endraw %}{% if not deploy_as_executable %}{% raw %}
build:
needs:
- test
- check-skip-duplicate
if: needs.check-skip-duplicate.outputs.should-run == 'true'
timeout-minutes: {% endraw %}{{ gha_medium_timeout_minutes }}{% raw %}
runs-on: {% endraw %}{{ gha_linux_runner }}{% raw %}

steps:
- name: Checkout code
uses: actions/checkout@{% endraw %}{{ gha_checkout }}{% raw %}

- name: Install python tooling
uses: ./.github/actions/install_deps
with:
skip-installing-ssm-plugin-manager: true
skip-installing-pulumi-cli: true
python-version: {% endraw %}{{ python_version }}{% if python_package_registry == "AWS CodeArtifact" %}{% raw %}
code-artifact-auth-role-name: CoreInfraBaseAccess
code-artifact-auth-role-account-id: {% endraw %}{{ aws_central_infrastructure_account_id }}{% raw %}
code-artifact-auth-region: {% endraw %}{{ aws_org_home_region }}{% endif %}{% raw %}

{% endraw %}{% if python_package_registry == "AWS CodeArtifact" %}{% raw %}
- name: OIDC Auth for Installing any dependencies that uv may need for build (sometimes it likes to install setuptools...even if it's already in the package dependencies)
uses: aws-actions/configure-aws-credentials@{% endraw %}{{ gha_configure_aws_credentials }}{% raw %}
with:
role-to-assume: arn:aws:iam::{% endraw %}{{ aws_central_infrastructure_account_id }}{% raw %}:role/CoreInfraBaseAccess
aws-region: {% endraw %}{{ aws_org_home_region }}{% raw %}

{% endraw %}{% endif %}{% raw %}
- name: Build package
run: |
{% endraw %}{% if python_package_registry == "AWS CodeArtifact" %}{% raw %} . .devcontainer/code-artifact-auth.sh{% endraw %}{% endif %}{% raw %}
uv build --no-sources

- name: Upload build package
uses: actions/upload-artifact@{% endraw %}{{ gha_upload_artifact }}{% raw %}
with:
name: python-package-distributions
path: dist/
if-no-files-found: error
{% endraw %}{% endif %}{% raw %}

{% endraw %}{% if deploy_as_executable %}{% raw %} executable:
needs:
- test
Expand Down Expand Up @@ -193,7 +237,8 @@ jobs:
- get-values
- check-skip-duplicate
- lint
- test{% endraw %}{% if create_docs %}
- test{% endraw %}{% if not deploy_as_executable %}
- build{% endif %}{% if create_docs %}
- build-docs{% endif %}{% if deploy_as_executable %}
- executable{% endif %}{% raw %}
- confirm-on-tagged-copier-template
Expand All @@ -207,7 +252,8 @@ jobs:

if [[ ! "${{ needs.get-values.result }}" =~ $success_pattern ]] ||
[[ ! "${{ needs.check-skip-duplicate.result }}" =~ $success_pattern ]] ||
[[ ! "${{ needs.lint.result }}" =~ $success_pattern ]] ||{% endraw %}{% if create_docs %}{% raw %}
[[ ! "${{ needs.lint.result }}" =~ $success_pattern ]] ||{% endraw %}{% if not deploy_as_executable %}{% raw %}
[[ ! "${{ needs.build.result }}" =~ $success_pattern ]] ||{% endraw %}{% endif %}{% if create_docs %}{% raw %}
[[ ! "${{ needs.build-docs.result }}" =~ $success_pattern ]] ||{% endraw %}{% endif %}{% raw %}{% endraw %}{% if deploy_as_executable %}{% raw %}
[[ ! "${{ needs.executable.result }}" =~ $success_pattern ]] ||{% endraw %}{% endif %}{% raw %}
[[ ! "${{ needs.test.result }}" =~ $success_pattern ]] ||
Expand Down
53 changes: 53 additions & 0 deletions template/.github/workflows/extract_project_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# ============== WARNING ==============================================================================
# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git
# See .config/.copier-managed-files.json for details.
#
# You are welcome to make changes to this file in your repo if they are custom to your project,
# but if the change should be shared with other projects, please backport it to the template repo.
# =====================================================================================================
import argparse
import json
import tomllib
from pathlib import Path


def extract_version(file_path: Path | str) -> str:
path = Path(file_path)

if path.name == "package.json":
data = json.loads(path.read_text())
if version := data.get("version"):
return version
raise KeyError(f"No version field found in {path!r}")

if path.name == "pyproject.toml":
with path.open("rb") as f:
data = tomllib.load(f)
project = data.get("project", {})
if version := project.get("version"):
return version
tool = data.get("tool", {})
if version := tool.get("poetry", {}).get("version"):
return version
raise KeyError(f"No version field found in {path!r}")

raise ValueError(f"Unsupported file type {path.name!r}; expected pyproject.toml or package.json")


def main() -> None:
parser = argparse.ArgumentParser(
description="Extract the version from a pyproject.toml or package.json file and print it."
)
_ = parser.add_argument(
"file",
nargs="?",
default="pyproject.toml",
help="Path to pyproject.toml or package.json (default: pyproject.toml)",
)
args = parser.parse_args()

print(extract_version(args.file)) # noqa: T201 # specifically printing this out so CI pipelines can read the value from stdout


if __name__ == "__main__":
main()
1 change: 0 additions & 1 deletion template/.github/workflows/git_tag.py

This file was deleted.

67 changes: 67 additions & 0 deletions template/.github/workflows/git_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# ============== WARNING ==============================================================================
# File is managed by copier template: gh:LabAutomationAndScreening/copier-base-template.git
# See .config/.copier-managed-files.json for details.
#
# You are welcome to make changes to this file in your repo if they are custom to your project,
# but if the change should be shared with other projects, please backport it to the template repo.
# =====================================================================================================
import argparse
import subprocess


def ensure_tag_not_present(tag: str, remote: str) -> None:
no_matching_refs_return_code = 2
result = subprocess.run( # noqa: S603 # this is trusted input, it's our own arguments being passed in
["git", "ls-remote", "--exit-code", "--tags", remote, f"refs/tags/{tag}"], # noqa: S607 # if `git` isn't in PATH already, then there are bigger problems to solve
stdout=subprocess.DEVNULL,
check=False,
)
if result.returncode == 0:
raise Exception(f"Error: tag '{tag}' exists on remote '{remote}'") # noqa: TRY002 # not worth a custom exception
if (
result.returncode != no_matching_refs_return_code
): # anything else is a real error (bad remote, auth failure, network)
raise Exception(f"git ls-remote exited with code {result.returncode} (remote={remote!r})") # noqa: TRY002 # not worth a custom exception


def main() -> None:
parser = argparse.ArgumentParser(
description=("Confirm that git tag v<version> is not present on a remote, or create and push the tag.")
)
_ = parser.add_argument(
"--version",
required=True,
help="Version string (e.g. 1.0.6 or v1.0.6); the tag will always be v<version>",
)
mode = parser.add_mutually_exclusive_group(required=True)
_ = mode.add_argument(
"--confirm-tag-not-present",
action="store_true",
help="Check that git tag v<version> is NOT present on the remote. If the tag exists, exit with an error.",
)
_ = mode.add_argument(
"--push-tag-to-remote",
action="store_true",
help="Create git tag v<version> locally and push it to the remote. Internally confirms the tag is not already present.",
)
_ = parser.add_argument(
"--remote",
default="origin",
help="Name of git remote to query/push (default: origin)",
)
args = parser.parse_args()

tag = args.version if args.version.startswith("v") else f"v{args.version}"

if args.push_tag_to_remote:
ensure_tag_not_present(tag, args.remote)
_ = subprocess.run(["git", "tag", tag], check=True) # noqa: S603,S607 # this is trusted input, it's our own version string. and if `git` isn't in PATH, then there are larger problems anyway
_ = subprocess.run(["git", "push", args.remote, tag], check=True) # noqa: S603,S607 # this is trusted input, it's our own version string. and if `git` isn't in PATH, then there are larger problems anyway
return

if args.confirm_tag_not_present:
ensure_tag_not_present(tag, args.remote)


if __name__ == "__main__":
main()
Loading