-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
630 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# https://editorconfig.org/ | ||
|
||
# https://manpages.debian.org/testing/shfmt/shfmt.1.en.html#EXAMPLES | ||
[*.sh] | ||
indent_style = space | ||
indent_size = 4 | ||
shell_variant = bash # --language-variant | ||
binary_next_line = false | ||
switch_case_indent = true # --case-indent | ||
space_redirects = false | ||
keep_padding = false | ||
function_next_line = false # --func-next-line |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
--- | ||
name: GitHub Actions | ||
on: | ||
pull_request: | ||
paths: | ||
- ".github/workflows/*" | ||
|
||
jobs: | ||
lint: | ||
name: Lint | ||
# These permissions are needed to: | ||
# - Checkout the Git repo (`contents: read`) | ||
permissions: | ||
contents: read | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
# https://github.com/rhysd/actionlint/blob/v1.7.6/docs/usage.md#use-actionlint-on-github-actions | ||
# https://github.com/rhysd/actionlint/blob/v1.7.6/docs/usage.md#reviewdog | ||
# https://github.com/reviewdog/reviewdog#filter-mode | ||
# No support for non-workflows yet: https://github.com/rhysd/actionlint/issues/46 | ||
- uses: reviewdog/action-actionlint@a1b7ce56be870acfe94b83ce5f6da076aecc6d8c # v1.62.0 | ||
with: | ||
fail_level: error | ||
filter_mode: nofilter # Post results on all results and not just changed files |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
--- | ||
name: Integration Tests | ||
on: | ||
pull_request: | ||
paths: | ||
- "action.yaml" | ||
- ".github/workflows/integration-tests.yaml" | ||
push: | ||
paths: | ||
- "action.yaml" | ||
- ".github/workflows/integration-tests.yaml" | ||
|
||
concurrency: | ||
group: integration-tests-${{ github.event_name == 'pull_request' && 'pr' || 'push' }}-${{ github.event.pull_request.head.sha || github.sha }} | ||
cancel-in-progress: true | ||
|
||
jobs: | ||
filter-matrix: | ||
name: Filter Matrix | ||
runs-on: ubuntu-latest | ||
outputs: | ||
test-json: ${{ steps.filter.outputs.test-json }} | ||
cleanup-json: ${{ steps.filter.outputs.cleanup-json }} | ||
steps: | ||
- name: Filter Matrix | ||
id: filter | ||
shell: bash | ||
run: | | ||
# Remove any entries with keys containing `null` values. | ||
test_yaml="$(yq 'map(select(to_entries | map(.value != null) | all))' <<<"${matrix:?}")" | ||
# Validate we do not accidentally test against the same package and commit SHA. | ||
yq -o=json <<<"$test_yaml" | jq -e '(map({package, "commit-sha"}) | unique | length) == length' || exit 1 | ||
# Automatically cleanup the `cache-sha-*` tags for the specific test commits. | ||
cleanup_yaml="$(yq 'group_by(.package) | map({"package": .[0].package, "tags" : map(.commit-sha) | unique | map("cache-sha-" + .) | join(",")})' <<<"$test_yaml")" | ||
# Output our multiline YAML document using GH action flavored heredoc | ||
# https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings | ||
{ | ||
echo "test-json<<EOF" | ||
yq -o json <<<"$test_yaml" | ||
echo "EOF" | ||
echo "cleanup-json<<EOF" | ||
yq -o json <<<"$cleanup_yaml" | ||
echo "EOF" | ||
} | tee -a "$GITHUB_OUTPUT" | ||
env: | ||
# We need to avoid running concurrent tests using the same commit SHA and | ||
# writing to the same image-repository. If we do not then we could see false | ||
# positive test results if one of them doesn't actually push cache layers. We | ||
# address this problem by: | ||
# | ||
# 1. Ensuring tests which run in parallel either use separate image repositories | ||
# or different Git commit SHAs. | ||
# 2. Utilizing concurrency groups to avoid having multiple instances of this | ||
# workflow run in parallel when triggered on the same commit SHA. | ||
# 3. Deleting the `cache-sha-*` tags to ensure our running workflow produced | ||
# those images. Ideally, we'd delete these before the tests run but attempting | ||
# to delete images from non-existing packages causes failures so this works | ||
# well enough. | ||
# | ||
# I also considered revising the action to avoid pushing images entirely. | ||
# Doing this may be challenging in otherways as pushing the image is a | ||
# requirement for getting the digests in some contexts: | ||
# https://github.com/docker/build-push-action/issues/906#issuecomment-1674567311 | ||
matrix: | | ||
- title: ${{ github.event_name == 'pull_request' && 'Merge Commit' || '' }} | ||
package : temporary/whalesay-pr | ||
commit-sha: ${{ github.sha }} | ||
from-scratch: false | ||
- title: Head Commit | ||
package: temporary/whalesay-${{ github.event_name == 'pull_request' && 'pr' || 'push' }} | ||
commit-sha: ${{ github.event.pull_request.head.sha || github.sha }} | ||
from-scratch: false | ||
- title: ${{ github.event_name == 'pull_request' && 'Merge Commit From Scratch' || '' }} | ||
package: temporary/whalesay-pr-from-scratch | ||
commit-sha: ${{ github.sha }} | ||
from-scratch: true | ||
- title: Head Commit From Scratch | ||
package: temporary/whalesay-${{ github.event_name == 'pull_request' && 'pr' || 'push' }}-from-scratch | ||
commit-sha: ${{ github.event.pull_request.head.sha || github.sha }} | ||
from-scratch: true | ||
test: | ||
name: Test ${{ matrix.test.title }} | ||
needs: filter-matrix | ||
# These permissions are needed to: | ||
# - Checkout the repo | ||
permissions: | ||
contents: read | ||
packages: write | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
test: ${{ fromJSON(needs.filter-matrix.outputs.test-json) }} | ||
steps: | ||
- name: Job started at | ||
id: job-started | ||
run: | | ||
job_started_at="$(date --utc --iso-8601=seconds)" | ||
echo "at=$job_started_at" | tee -a "$GITHUB_OUTPUT" | ||
- uses: actions/checkout@v4 | ||
with: | ||
ref: ${{ matrix.test.commit-sha }} | ||
- name: Log in to the Container registry | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ github.token }} | ||
- uses: ./ | ||
id: build | ||
with: | ||
image-repository: ghcr.io/beacon-biosignals/${{ matrix.test.package }} | ||
context: test | ||
build-args: | | ||
DEBIAN_VERSION=12.9 | ||
from-scratch: ${{ matrix.test.from-scratch || 'false' }} | ||
- name: Validate image works | ||
run: | | ||
docker pull "${{ steps.build.outputs.image }}" | ||
output="$(docker run "${{ steps.build.outputs.image }}")" | ||
if [[ "$(wc -l <<<"$output")" -lt 14 ]]; then | ||
echo "$output" | ||
exit 1 | ||
fi | ||
debian_version="$(docker run --entrypoint=/bin/cat "${{ steps.build.outputs.image }}" /etc/debian_version)" | ||
[[ "$debian_version" == "12.9" ]] || exit 2 | ||
- name: Layer created at | ||
id: layer-created | ||
run: | | ||
layer_created_at="$(docker run --entrypoint=/bin/cat "${{ steps.build.outputs.image }}" /etc/layer-created-at)" | ||
echo "at=$layer_created_at" | tee -a "$GITHUB_OUTPUT" | ||
# Test will fail if this is the first time the image was build in the image-repository | ||
- name: Validate layer caching | ||
if: ${{ matrix.test.from-scratch == false }} | ||
run: | | ||
[[ "$(date -d "$layer_created_at" +%s)" -lt "$(date -d "$job_started_at" +%s)" ]] || exit 1 | ||
env: | ||
job_started_at: ${{ steps.job-started.outputs.at }} | ||
layer_created_at: ${{ steps.layer-created.outputs.at }} | ||
- name: Validate no layer caching | ||
if: ${{ matrix.test.from-scratch == true }} | ||
run: | | ||
[[ "$(date -d "$layer_created_at" +%s)" -gt "$(date -d "$job_started_at" +%s)" ]] || exit 1 | ||
env: | ||
job_started_at: ${{ steps.job-started.outputs.at }} | ||
layer_created_at: ${{ steps.layer-created.outputs.at }} | ||
- name: Validate cache images | ||
run: | | ||
docker manifest inspect "${{ steps.build.outputs.image-repository }}:cache-sha-${{ matrix.test.commit-sha }}" | ||
# Should only be skipped when workflow is triggered by a tag push | ||
if [[ -n "$branch" ]]; then | ||
docker manifest inspect "${{ steps.build.outputs.image-repository }}:cache-branch-${branch//[^[:alnum:]]/-}" | ||
fi | ||
env: | ||
branch: ${{ github.head_ref || (github.ref_type == 'branch' && github.ref_name) }} | ||
- name: Validate annotations | ||
run: | | ||
set -x | ||
json="$(docker manifest inspect "${{ steps.build.outputs.image }}")" | ||
[[ "$(jq -r '.annotations."org.opencontainers.image.revision"' <<<"$json")" == "${{ matrix.test.commit-sha }}" ]] || exit 1 | ||
- name: Validate docker/metadata-output environment variables are overwritten | ||
shell: bash | ||
run: | | ||
if [[ "$(printenv | grep '^DOCKER_METADATA_OUTPUT_' | grep -c '[^=]$')" -ne 0 ]]; then | ||
printenv | grep '^DOCKER_METADATA_OUTPUT_' | ||
exit 1 | ||
fi | ||
cleanup: | ||
name: Cleanup (${{ matrix.cleanup.package }}) | ||
needs: | ||
- filter-matrix | ||
- test | ||
if: ${{ !cancelled() }} | ||
runs-on: ubuntu-latest | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
cleanup: ${{ fromJSON(needs.filter-matrix.outputs.cleanup-json || '[]') }} | ||
steps: | ||
- uses: dataaxiom/ghcr-cleanup-action@cd0cdb900b5dbf3a6f2cc869f0dbb0b8211f50c4 # v1.0.16 | ||
with: | ||
package: ${{ matrix.cleanup.package }} | ||
delete-tags: ${{ matrix.cleanup.tags }} | ||
- uses: dataaxiom/ghcr-cleanup-action@cd0cdb900b5dbf3a6f2cc869f0dbb0b8211f50c4 # v1.0.16 | ||
with: | ||
package: ${{ matrix.cleanup.package }} | ||
older-than: 1 day | ||
keep-n-tagged: 0 | ||
exclude-tags: branch-main,cache-branch-main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
--- | ||
name: Shell | ||
on: | ||
pull_request: | ||
paths: | ||
- "**.sh" | ||
- ".github/workflows/*" | ||
|
||
jobs: | ||
lint-format: | ||
name: Lint & Format | ||
# These permissions are needed to: | ||
# - Checkout the Git repo (`contents: read`) | ||
# - Post a comments on PRs: https://github.com/luizm/action-sh-checker#secrets | ||
permissions: | ||
contents: read | ||
pull-requests: write | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Extract workflow shell scripts | ||
id: extract | ||
uses: beacon-biosignals/gha-extract-shell-scripts@v1 | ||
- uses: luizm/action-sh-checker@c6edb3de93e904488b413636d96c6a56e3ad671a # v0.8.0 | ||
env: | ||
GITHUB_TOKEN: ${{ github.token }} | ||
with: | ||
sh_checker_comment: true | ||
# Support investigating linting/formatting errors | ||
- uses: actions/upload-artifact@v4 | ||
if: ${{ failure() }} | ||
with: | ||
name: workflow-scripts | ||
path: ${{ steps.extract.outputs.output-dir }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
--- | ||
# https://yamllint.readthedocs.io/en/stable/integration.html#integration-with-github-actions | ||
name: YAML | ||
on: | ||
pull_request: | ||
paths: | ||
- "**/*.yaml" | ||
- "**/*.yml" | ||
jobs: | ||
lint: | ||
name: Lint | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- name: Install yamllint | ||
run: pip install yamllint | ||
- name: Lint YAML files | ||
run: yamllint . --format=github |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
rules: | ||
indentation: | ||
spaces: 2 | ||
indent-sequences: true | ||
document-start: | ||
present: true | ||
new-line-at-end-of-file: enable |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2025 Beacon Biosignals | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,69 @@ | ||
# docker-build | ||
# Docker Build | ||
|
||
Build a Docker image while utilizing [layer caching](https://docs.docker.com/build/cache/) backed from the image repository. Image tags will be automatically created based upon the relevant PR, branch name, and commit SHA. | ||
|
||
When using this action we recommend utilizing a separate image repositories for development and production (e.g.`temporary/my-image` and `permanent/my-image`) to make it easier to separate temporary images from permanent images meant for end users. The `beacon-biosignals/docker-build` action is used to build temporary images under development. Once a temporary image is ready for production it can be promoted to be permanent by using `docker tag`/`docker push` or [`regctl image copy --digest-tags`](https://github.com/regclient/regclient/blob/main/docs/regctl.md#registry-commands) (if you want the digest to be identical across registries) to transfer the image. | ||
|
||
Note that although [Docker does support using GitHub Actions cache](https://docs.docker.com/build/cache/backends/gha/) as a layer cache backend the GHA cache limit for a repository is 10 GB which is quite limiting for larger Docker images. | ||
|
||
## Example | ||
|
||
```yaml | ||
--- | ||
on: | ||
pull_request: {} | ||
# Trigger this build workflow on "main". See `from-scratch` | ||
push: | ||
branches: | ||
- main | ||
jobs: | ||
example: | ||
# These permissions are needed to: | ||
# - Get the workflow run: https://github.com/beacon-biosignals/docker-build#permissions | ||
permissions: {} | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Build image | ||
uses: beacon-biosignals/docker-build@v1 | ||
with: | ||
image-repository: temporary/my-image | ||
context: . | ||
# Example of passing in Docker `--build-arg` | ||
build-args: | | ||
JULIA_VERSION=1.10 | ||
PYTHON_VERSION=3.10 | ||
# Example of passing in Docker `--secret` | ||
build-secrets: | | ||
github-token=${{ secrets.token || github.token }} | ||
# Build images from scratch on "main". Ensures that caching doesn't result in using insecure system packages. | ||
from-scratch: ${{ github.ref == 'refs/heads/main' }} | ||
``` | ||
## Inputs | ||
| Name | Description | Required | Example | | ||
|:---------------------|:------------|:---------|:--------| | ||
| `image-repository` | The Docker image repository to push the build image and cached layers. | Yes | `temporary/my-image` | | ||
| `context` | The Docker build context directory. Defaults to `.`. | No | `./my-image` | | ||
| `build-args` | List of [build-time variables](https://docs.docker.com/reference/cli/docker/buildx/build/#build-arg). | No | <pre><code>HTTP_PROXY=http://10.20.30.2:1234 FTP_PROXY=http://40.50.60.5:4567</code></pre> | | ||
| `build-secrets` | List of [secrets](https://docs.docker.com/engine/reference/commandline/buildx_build/#secret) to expose to the build. | No | `GIT_AUTH_TOKEN=mytoken` | | ||
| `from-scratch` | Do not use cache when building the image. Defaults to `false`. | No | `false` | | ||
|
||
## Outputs | ||
|
||
| Name | Description | Example | | ||
|:-------------------|:------------|:--------| | ||
| `image` | Reference to the build image including the digest. | `temporary/my-image@sha256:37782d4e1c24d8f12047039a0d3512d1b6059e306a80d5b66a1d9ff60247a8cb` | | ||
| `image-repository` | The Docker image repository where the image was pushed to. | `temporary/my-image` | | ||
| `digest` | The built Docker image digest. | `sha256:37782d4e1c24d8f12047039a0d3512d1b6059e306a80d5b66a1d9ff60247a8cb` | | ||
| `tags` | JSON list of tags associated with the built Docker image. | `branch-main`, `sha-152cb14` | | ||
| `commit-sha` | The Git commit SHA used to build the image. | `152cb14643b50529b229930d6124e6bbef48668d` | | ||
|
||
## Permissions | ||
|
||
The follow [job permissions](https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs) are required to run this action: | ||
|
||
```yaml | ||
permissions: | ||
packages: write # Only required when using the GitHub Container registry: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry | ||
``` |
Oops, something went wrong.