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 .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.19
_commit: v0.0.31
_src_path: gh:LabAutomationAndScreening/copier-base-template.git
description: Copier template for creating Python libraries and executables
python_ci_versions:
Expand Down
9 changes: 4 additions & 5 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,17 @@
"eamodio.gitlens@15.5.1",
"ms-vscode.live-server@0.5.2024091601",
"MS-vsliveshare.vsliveshare@1.0.5905",
"github.copilot@1.234.0",
"github.copilot-chat@0.20.2",
"github.copilot@1.304.1523",
"github.copilot-chat@0.27.2025042301",

// Python
"ms-python.python@2024.14.1",
"ms-python.vscode-pylance@2024.9.2",
"ms-vscode-remote.remote-containers@0.383.0",
"charliermarsh.ruff@2024.54.0",



// Misc file formats
"bierner.markdown-mermaid@1.25.0",
"bierner.markdown-mermaid@1.28.0",
"samuelcolvin.jinjahtml@0.20.0",
"tamasfe.even-better-toml@0.19.2",
"emilast.LogFileHighlighter@3.3.3",
Expand All @@ -61,4 +59,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): bbebc7c9
}
6 changes: 3 additions & 3 deletions .devcontainer/install-ci-tooling.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -ex



curl -LsSf https://astral.sh/uv/0.6.6/install.sh | sh
curl -LsSf https://astral.sh/uv/0.6.17/install.sh | sh
uv --version
# TODO: add uv autocompletion to the shell https://docs.astral.sh/uv/getting-started/installation/#shell-autocompletion

Expand All @@ -19,8 +19,8 @@ input="${1:-$default_version}"
export UV_PYTHON="$input"
export UV_PYTHON_PREFERENCE=only-system

uv tool install 'copier==9.5.0' --with 'copier-templates-extensions==0.3.0'
uv tool install 'copier==9.6.0' --with 'copier-templates-extensions==0.3.0'

uv tool install 'pre-commit==4.1.0'
uv tool install 'pre-commit==4.2.0'

uv tool list
27 changes: 20 additions & 7 deletions .devcontainer/windows-host-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# If you're still having issues, make sure in Windows Developer Settings that you enabled Developer Mode, and also that you set your git config to have `core.autocrlf=false` and `core.symlinks=true` globally

set -e # Exit immediately on error
set -euo pipefail # Exit immediately on error

if [ -z "$BASH_VERSION" ]; then
echo "Error: This script must be run with bash (e.g., 'bash windows-host-helper.sh')." >&2
Expand Down Expand Up @@ -39,13 +39,26 @@ tmpdir=$(mktemp -d)
# This creates "$tmpdir/$repoName" with the repository's contents.
git clone "$gitUrl" "$tmpdir/$repoName"

# Enable dotglob so that '*' includes hidden files
shopt -s dotglob

# Move all contents (including hidden files) from the cloned repo to the target folder
mv "$tmpdir/$repoName"/* "./$repoName/"
SRC="$(realpath "$tmpdir/$repoName")"
DST="$(realpath "./$repoName")"

# Clean up: remove the temporary directory
# 1) Recreate directory tree under $DST
while IFS= read -r -d '' dir; do
rel="${dir#$SRC/}" # strip leading $SRC/ → e.g. "sub/dir"
mkdir -p "$DST/$rel"
done < <(find "$SRC" -type d -print0)

# 2) Move all files into that mirror
while IFS= read -r -d '' file; do
rel="${file#$SRC/}" # e.g. "sub/dir/file.txt"
# ensure parent exists (though step 1 already did)
mkdir -p "$(dirname "$DST/$rel")"
mv "$file" "$DST/$rel"
done < <(find "$SRC" -type f -print0)

# 3) Clean up now‑empty dirs and the tmp clone
find "$SRC" -depth -type d -empty -delete
rm -rf "$tmpdir"

echo "Repository '$repoName' has been updated."
echo "Repository '$repoName' has been synced into '$DST'."
2 changes: 1 addition & 1 deletion .github/actions/install_deps_uv/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ runs:
shell: bash

- name: Setup python
uses: actions/setup-python@v5.4.0
uses: actions/setup-python@v5.5.0
with:
python-version: ${{ env.PYTHON_VERSION }}

Expand Down
6 changes: 3 additions & 3 deletions .github/actions/install_deps_uv/install-ci-tooling.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

irm https://astral.sh/uv/0.6.6/install.ps1 | iex
irm https://astral.sh/uv/0.6.17/install.ps1 | iex

# Add uv to path (in github runner)
$env:Path = "C:\Users\runneradmin\.local\bin;$env:Path"
Expand All @@ -24,8 +24,8 @@ if ($args.Count -eq 0) {
$env:UV_PYTHON = "$input"
$env:UV_PYTHON_PREFERENCE="only-system"

& uv tool install 'copier==9.5.0' --with 'copier-templates-extensions==0.3.0'
& uv tool install 'copier==9.6.0' --with 'copier-templates-extensions==0.3.0'

& uv tool install 'pre-commit==4.1.0'
& uv tool install 'pre-commit==4.2.0'

& uv tool list
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Link to Issue or Slack thread
## Link to Issue or Message thread



Expand Down
120 changes: 110 additions & 10 deletions .github/reusable_workflows/build-docker-image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,144 @@ on:
description: 'Docker image name'
type: string
required: true
tag:
description: 'Docker image tag'
type: string
required: true
tag-for-production:
description: 'Whether or not to add a tag indicating this is being used in production'
type: boolean
required: false
default: false
context:
description: 'Build context path'
type: string
required: false
default: './'
push-role-name:
type: string
description: What's the IAM role name to use for Pushing to the ECR?
required: false
default: no-push
save-as-artifact:
type: boolean
description: 'Should the image be saved as an artifact?'
required: false
default: false

permissions:
id-token: write
contents: write # needed for mutex

jobs:
build-image:
name: Build Docker Image
runs-on: ubuntu-24.04
steps:
- name: Parse ECR URL
if: ${{ inputs.push-role-name != 'no-push' }}
id: parse_ecr_url
run: |
ECR_URL="${{ inputs.repository}}"

# Extract the AWS Account ID, which is the first field
AWS_ACCOUNT_ID=$(echo "$ECR_URL" | cut -d. -f1)

# Extract the AWS Region, which is the fourth field in the URL structure
AWS_REGION=$(echo "$ECR_URL" | cut -d. -f4)

# Set the outputs for use in later steps
echo "aws_account_id=${AWS_ACCOUNT_ID}" >> "$GITHUB_OUTPUT"
echo "aws_region=${AWS_REGION}" >> "$GITHUB_OUTPUT"
shell: bash

- name: Checkout code
uses: actions/checkout@v4.2.2

- name: OIDC Auth for ECR
if: ${{ inputs.push-role-name != 'no-push' }}
uses: aws-actions/configure-aws-credentials@v4.1.0
with:
role-to-assume: arn:aws:iam::${{ steps.parse_ecr_url.outputs.aws_account_id }}:role/${{ inputs.push-role-name }}
aws-region: ${{ steps.parse_ecr_url.outputs.aws_region }}

- name: Calculate hash of files in build context
id: calculate-build-context-hash
run: |
python3 --version
BUILD_HASH=$(python3 .github/workflows/hash_git_files.py ${{ inputs.context }})
echo "build_context_tag=context-${BUILD_HASH}" >> "$GITHUB_OUTPUT"
echo "Calculated build context tag as: ${BUILD_HASH}"

IMAGE_NAME_WITH_NAMESPACE="${{ inputs.image_name }}"
IMAGE_NAME_NO_SLASHES="${IMAGE_NAME_WITH_NAMESPACE//\//-}"
echo "image_name_no_slashes=${IMAGE_NAME_NO_SLASHES}" >> "$GITHUB_OUTPUT"
echo "Image name without slashes: ${IMAGE_NAME_NO_SLASHES}"

- name: Set up mutex # Github concurrency management is horrible, things get arbitrarily cancelled if queued up. So using mutex until github fixes itself. When multiple jobs are modifying cache at once, weird things can happen. possible issue is https://github.com/actions/toolkit/issues/658
if: ${{ inputs.push-role-name != 'no-push' }}
uses: ben-z/gh-action-mutex@1ebad517141198e08d47cf72f3c0975316620a65 # v1.0.0-alpha.10
with:
branch: mutex-${{ inputs.repository }}-${{ inputs.image_name }}
timeout-minutes: 30 # this is the amount of time this action will wait to attempt to acquire the mutex lock before failing, e.g. if other jobs are queued up in front of it

- name: Test if docker image exists
if: ${{ inputs.push-role-name != 'no-push' }}
id: check-if-exists
run: |
BUILD_HASH=${{ steps.calculate-build-context-hash.outputs.build_context_tag }}
echo Checking for : $BUILD_HASH
if aws ecr describe-images --region ${{ steps.parse_ecr_url.outputs.aws_region }} --registry-id=${{ steps.parse_ecr_url.outputs.aws_account_id }} --repository-name=${{ inputs.image_name }} --image-ids=imageTag=$BUILD_HASH; then \
echo "Image was found in ECR"; \
echo "status=found" >> $GITHUB_OUTPUT
else \
echo "Image was not found in ECR"; \
echo "status=notfound" >> $GITHUB_OUTPUT
fi

- name: Login to Amazon ECR
if: ${{ inputs.push-role-name != 'no-push' && (steps.check-if-exists.outputs.status == 'notfound' || inputs.save-as-artifact ) }}
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2.0.1

- name: Pull existing image to package as artifact
if: ${{ inputs.save-as-artifact && steps.check-if-exists.outputs.status == 'found' }}
run: |
docker pull ${{ inputs.repository }}/${{ inputs.image_name }}:${{ steps.calculate-build-context-hash.outputs.build_context_tag }}

- name: Set up Docker Buildx
if: ${{ (inputs.save-as-artifact && inputs.push-role-name == 'no-push') || steps.check-if-exists.outputs.status == 'notfound' }}
uses: docker/setup-buildx-action@v3.10.0
with:
version: v0.22.0

- name: Build Docker Image
if: ${{ (inputs.save-as-artifact && inputs.push-role-name == 'no-push') || steps.check-if-exists.outputs.status == 'notfound' }}
uses: docker/build-push-action@v6.15.0
with:
context: ${{ inputs.context }}
push: false
load: true # make the image available later for the `docker save` step
tags: ${{ inputs.repository }}/${{ inputs.image_name }}:${{ inputs.tag }}
push: ${{ inputs.push-role-name != 'no-push' && steps.check-if-exists.outputs.status == 'notfound' }}
load: ${{ inputs.save-as-artifact }} # make the image available later for the `docker save` step
tags: ${{ inputs.repository }}/${{ inputs.image_name }}:${{ steps.calculate-build-context-hash.outputs.build_context_tag }}

- name: Add git sha tag
if: ${{ inputs.push-role-name != 'no-push' }}
run: |
aws ecr batch-get-image --registry-id=${{ steps.parse_ecr_url.outputs.aws_account_id }} --repository-name=${{ inputs.image_name }} --image-ids imageTag=${{ steps.calculate-build-context-hash.outputs.build_context_tag }} --query 'images[].imageManifest' --output text > manifest.json
aws ecr put-image --registry-id=${{ steps.parse_ecr_url.outputs.aws_account_id }} --repository-name=${{ inputs.image_name }} --image-tag git-sha-${{ github.sha }} --image-manifest file://manifest.json

- name: Add tag for Production
if: ${{ inputs.push-role-name != 'no-push' && inputs.tag-for-production }}
run: |
aws ecr batch-get-image --registry-id=${{ steps.parse_ecr_url.outputs.aws_account_id }} --repository-name=${{ inputs.image_name }} --image-ids imageTag=${{ steps.calculate-build-context-hash.outputs.build_context_tag }} --query 'images[].imageManifest' --output text > manifest.json
# TODO: figure out some better conditional logic about adding a tag for the context in production, so we don't have to `|| true` at the end
aws ecr put-image --registry-id=${{ steps.parse_ecr_url.outputs.aws_account_id }} --repository-name=${{ inputs.image_name }} --image-tag production--${{ steps.calculate-build-context-hash.outputs.build_context_tag }} --image-manifest file://manifest.json || true
aws ecr put-image --registry-id=${{ steps.parse_ecr_url.outputs.aws_account_id }} --repository-name=${{ inputs.image_name }} --image-tag production--git-sha-${{ github.sha }} --image-manifest file://manifest.json

- name: Save Docker Image as tar
run: docker save -o ${{ inputs.image_name }}.tar ${{ inputs.repository }}/${{ inputs.image_name }}:${{ inputs.tag }}
if: ${{ inputs.save-as-artifact }}
run: docker save -o ${{ steps.calculate-build-context-hash.outputs.image_name_no_slashes }}.tar ${{ inputs.repository }}/${{ inputs.image_name }}:${{ steps.calculate-build-context-hash.outputs.build_context_tag }}

- name: Upload Docker Image Artifact
if: ${{ inputs.save-as-artifact }}
uses: actions/upload-artifact@v4.6.2
with:
name: ${{ inputs.image_name }}
path: ${{ inputs.image_name }}.tar
name: ${{ steps.calculate-build-context-hash.outputs.image_name_no_slashes }}
path: ${{ steps.calculate-build-context-hash.outputs.image_name_no_slashes }}.tar
if-no-files-found: error
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ jobs:

- name: Run pre-commit
run: |
# skip pip-compile because the command line args in the header are different
SKIP=git-dirty pre-commit run -a
# skip devcontainer context hash because the template instantiation may make it different every time
SKIP=git-dirty,compute-devcontainer-context-hash pre-commit run -a

- name: Upload pre-commit log if failure
if: ${{ failure() }}
Expand Down
Loading