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
60 changes: 60 additions & 0 deletions .github/workflows/build-docker-ccbr-actions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: build-docker-ccbr-actions

on:
release:
types:
- published
workflow_dispatch:
inputs:
push:
description: "Push image to DockerHub"
type: boolean
default: false
ccbr-actions-version:
description: "Version/tag of ccbr_actions to install in the image (defaults to the release tag or 'main')"
required: false
default: "main"

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Determine version and push flag
id: setup
shell: bash
run: |
if [ "${{ github.event_name }}" = "release" ]; then
version="${{ github.ref_name }}"
push="true"
else
version="${{ inputs.ccbr-actions-version }}"
push="${{ inputs.push }}"
fi
echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "push=${push}" >> "$GITHUB_OUTPUT"

- name: Login to DockerHub
if: ${{ steps.setup.outputs.push == 'true' }}
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME_VK }}
password: ${{ secrets.DOCKERHUBRW_TOKEN_VK }}

- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: .
file: Dockerfile
push: ${{ steps.setup.outputs.push == 'true' }}
tags: nciccbr/ccbr_actions:${{ steps.setup.outputs.version }}
build-args: |
CCBR_ACTIONS_VERSION=${{ steps.setup.outputs.version }}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## actions development version

- Add `setup-ccbr-actions` composite action that checks whether a Docker image exists at `nciccbr/ccbr_actions:{version}` on DockerHub and uses it if available, otherwise installs `ccbr_actions` via pip. (#186, @copilot)
- Add `Dockerfile` and `.github/workflows/build-docker-ccbr-actions.yml` to build and push `nciccbr/ccbr_actions` Docker images on release. (#186, @copilot)
- Update `build-docker` to use `setup-ccbr-actions` for installing `ccbr_actions`. (#186, @copilot)

## actions 0.7.1

- Fix `draft-release` to preserve custom keys in `CITATION.cff` in R packages. (#180, @kelly-sovacool)
Expand Down
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM python:3.11-slim

ARG CCBR_ACTIONS_VERSION=main

RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir "git+https://github.com/CCBR/actions@${CCBR_ACTIONS_VERSION}"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ Custom actions used in our github workflows.
using mkdocs + mike
- [post-release](post-release) - Post-release cleanup chores, intended
to be triggered by publishing a release
- [setup-ccbr-actions](setup-ccbr-actions) - Set up ccbr_actions by
pulling a Docker image if available on DockerHub, otherwise installing
via pip
- [sync-copilot-instructions](sync-copilot-instructions) - Sync Copilot
instructions from a source repository to a target repository and open
a PR
Expand Down
103 changes: 85 additions & 18 deletions build-docker/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,12 @@ outputs:
runs:
using: "composite"
steps:
- uses: actions/setup-python@v6
- name: Set up ccbr_actions
id: setup-ccbr-actions
uses: ./setup-ccbr-actions
with:
python-version: "${{ inputs.python-version }}"

- name: Install CCBR/actions
shell: bash
run: pip install --upgrade pip git+https://github.com/CCBR/actions.git@${{ inputs.ccbr-actions-version }}
ccbr-actions-version: ${{ inputs.ccbr-actions-version }}
python-version: ${{ inputs.python-version }}

- name: Resolve push mode
id: resolve_push
Expand Down Expand Up @@ -110,15 +109,35 @@ runs:
password: ${{ inputs.dockerhub-token }}

- name: Prepare build-time variables
shell: python
shell: bash
id: prepare_vars
env:
CCBR_INSTALL_METHOD: ${{ steps.setup-ccbr-actions.outputs.install-method }}
CCBR_DOCKER_IMAGE: ${{ steps.setup-ccbr-actions.outputs.docker-image }}
run: |
script=$(mktemp /tmp/ccbr_XXXXXXXXXX.py)
cat > "$script" << 'PYEOF'
from ccbr_actions.docker import prepare_docker_build_variables
prepare_docker_build_variables(
dockerfile="${{ inputs.dockerfile }}",
suffix="${{ inputs.suffix }}",
dockerhub_account="${{ inputs.dockerhub-namespace }}"
)
PYEOF
if [ "${CCBR_INSTALL_METHOD}" = "docker" ]; then
docker run --rm \
-v "${GITHUB_ENV}:${GITHUB_ENV}" \
-e GITHUB_ENV \
-v "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \
-e GITHUB_WORKSPACE \
-w "${GITHUB_WORKSPACE}" \
-v "${script}:${script}:ro" \
"${CCBR_DOCKER_IMAGE}" \
python "${script}"
else
python "$script"
fi
rm -f "$script"

- name: Check variables and create README
shell: bash
Expand All @@ -142,23 +161,46 @@ runs:

- name: Check whether image tag is stale
id: check_tag_staleness
shell: python
shell: bash
env:
CCBR_INSTALL_METHOD: ${{ steps.setup-ccbr-actions.outputs.install-method }}
CCBR_DOCKER_IMAGE: ${{ steps.setup-ccbr-actions.outputs.docker-image }}
INPUT_FORCE_BUILD: ${{ inputs.force_build }}
run: |
script=$(mktemp /tmp/ccbr_XXXXXXXXXX.py)
cat > "$script" << 'PYEOF'
import os
from ccbr_actions.actions import set_output
from ccbr_actions.docker import evaluate_docker_build_staleness_and_set_outputs

force_build = "${{ inputs.force_build }}".lower() == "true"
force_build = os.environ.get("INPUT_FORCE_BUILD", "").lower() == "true"
if force_build:
set_output("should_build", "true")
set_output("reason", "force_build_requested")
print("::notice::Force build requested. Skipping Docker Hub staleness check.")
else:
evaluate_docker_build_staleness_and_set_outputs(
dockerfile_path="${{ env.DOCKERFILE_PATH }}",
image_name="${{ env.IMAGENAME }}",
dockerfile_path=os.environ.get("DOCKERFILE_PATH", ""),
image_name=os.environ.get("IMAGENAME", ""),
dockerhub_namespace="${{ inputs.dockerhub-namespace }}",
repo_name="${{ env.REPONAME }}",
repo_name=os.environ.get("REPONAME", ""),
)
PYEOF
if [ "${CCBR_INSTALL_METHOD}" = "docker" ]; then
docker run --rm \
-v "${GITHUB_OUTPUT}:${GITHUB_OUTPUT}" \
-e GITHUB_OUTPUT \
-e INPUT_FORCE_BUILD \
-e DOCKERFILE_PATH \
-e IMAGENAME \
-e REPONAME \
-v "${script}:${script}:ro" \
"${CCBR_DOCKER_IMAGE}" \
python "${script}"
Comment on lines +190 to +199
else
python "$script"
fi
rm -f "$script"

- name: Build and push Docker image
id: build_and_push
Expand All @@ -181,15 +223,40 @@ runs:
shell: bash
id: run_script_in_container
if: ${{ (inputs.print-versions == 'true') && (steps.check_tag_staleness.outputs.should_build == 'true') }}
env:
CCBR_INSTALL_METHOD: ${{ steps.setup-ccbr-actions.outputs.install-method }}
CCBR_DOCKER_IMAGE: ${{ steps.setup-ccbr-actions.outputs.docker-image }}
run: |
script_src=$(command -v print_versions.sh)
EXTRA_MOUNTS=""
if [ -f "${{ inputs.config-file }}" ]; then
CONFIG_FILE="/ws/${{ inputs.config-file }}"
if [ "${CCBR_INSTALL_METHOD}" = "docker" ]; then
# Extract print_versions.sh from the ccbr_actions Docker image
docker run --rm --entrypoint sh \
-v /tmp:/tmp \
"${CCBR_DOCKER_IMAGE}" \
-c "cp \$(command -v print_versions.sh) /tmp/print_versions.sh"
script_src=/tmp/print_versions.sh
if [ -f "${{ inputs.config-file }}" ]; then
CONFIG_FILE="/ws/${{ inputs.config-file }}"
else
config_src=$(docker run --rm --entrypoint python \
"${CCBR_DOCKER_IMAGE}" \
-c 'import ccbr_actions.data; print(ccbr_actions.data.get_file_path("tool_version_commands.txt"))')
docker run --rm --entrypoint sh \
-v /tmp:/tmp \
"${CCBR_DOCKER_IMAGE}" \
-c "cp ${config_src} /tmp/tool_version_commands.txt"
CONFIG_FILE="/ws/tool_version_commands.txt"
EXTRA_MOUNTS="-v /tmp/tool_version_commands.txt:/ws/tool_version_commands.txt:ro"
fi
else
config_src=$(python -c 'import ccbr_actions.data; print(ccbr_actions.data.get_file_path("tool_version_commands.txt"))')
CONFIG_FILE="/ws/tool_version_commands.txt"
EXTRA_MOUNTS="-v ${config_src}:/ws/tool_version_commands.txt:ro"
script_src=$(command -v print_versions.sh)
if [ -f "${{ inputs.config-file }}" ]; then
CONFIG_FILE="/ws/${{ inputs.config-file }}"
else
config_src=$(python -c 'import ccbr_actions.data; print(ccbr_actions.data.get_file_path("tool_version_commands.txt"))')
CONFIG_FILE="/ws/tool_version_commands.txt"
EXTRA_MOUNTS="-v ${config_src}:/ws/tool_version_commands.txt:ro"
fi
fi
docker run --rm --entrypoint /bin/bash \
-v "${{ github.workspace }}:/ws" \
Expand Down
72 changes: 72 additions & 0 deletions setup-ccbr-actions/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: setup-ccbr-actions
description: "Set up ccbr_actions by pulling a Docker image if available on DockerHub, otherwise installing via pip"
author: "Kelly Sovacool"

inputs:
ccbr-actions-version:
description: |
Version/tag of ccbr_actions to use (e.g. 'v0.7.0', 'main', 'latest').
When a matching Docker image exists at nciccbr/ccbr_actions:{version}, it is
pulled instead of running pip install. Otherwise ccbr_actions is installed via pip.
required: true
default: "main"
Comment on lines +11 to +12
python-version:
description: |
The version of Python to install when falling back to pip install.
required: true
default: "3.11"
dockerhub-namespace:
description: |
DockerHub namespace to check for the ccbr_actions Docker image.
required: false
default: "nciccbr"

outputs:
install-method:
description: "Method used to make ccbr_actions available: 'docker' or 'pip'."
value: ${{ steps.check-docker.outputs.install-method }}
docker-image:
description: "Full Docker image reference used (e.g. nciccbr/ccbr_actions:v0.7.0), or empty when pip was used."
value: ${{ steps.check-docker.outputs.docker-image }}

runs:
using: "composite"
steps:
- name: Check if Docker image exists on DockerHub
id: check-docker
shell: bash
run: |
version="${{ inputs.ccbr-actions-version }}"
namespace="${{ inputs.dockerhub-namespace }}"
repo_name="ccbr_actions"

http_code=$(curl -s -o /dev/null -w "%{http_code}" \
"https://hub.docker.com/v2/namespaces/${namespace}/repositories/${repo_name}/tags/${version}" \
--max-time 20)

if [ "${http_code}" = "200" ]; then
docker_image="${namespace}/${repo_name}:${version}"
echo "docker-image=${docker_image}" >> "$GITHUB_OUTPUT"
echo "install-method=docker" >> "$GITHUB_OUTPUT"
echo "::notice::ccbr_actions Docker image found: ${docker_image}"
else
echo "docker-image=" >> "$GITHUB_OUTPUT"
echo "install-method=pip" >> "$GITHUB_OUTPUT"
echo "::notice::No Docker image found for '${namespace}/${repo_name}:${version}'. Installing ccbr_actions via pip."
fi
Comment on lines +43 to +56

- name: Pull Docker image
if: ${{ steps.check-docker.outputs.install-method == 'docker' }}
shell: bash
run: docker pull ${{ steps.check-docker.outputs.docker-image }}

- name: Set up Python
if: ${{ steps.check-docker.outputs.install-method == 'pip' }}
uses: actions/setup-python@v6
with:
python-version: "${{ inputs.python-version }}"

- name: Install ccbr_actions via pip
if: ${{ steps.check-docker.outputs.install-method == 'pip' }}
shell: bash
run: pip install --upgrade pip git+https://github.com/CCBR/actions.git@${{ inputs.ccbr-actions-version }}