Skip to content
Open
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
56 changes: 54 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
name: Build and push image

# Builds insectai/minio for linux/amd64 and linux/arm64 from the sources pinned in versions.env.
# Every run first builds linux/amd64 into the runner's Docker daemon and runs test/smoke.sh against it.
# Pull requests only build (no push). Pushes to main and manual runs build and push.
#
# Each published build gets three tags:
# <MINIO_TAG>-r<IMAGE_REVISION> immutable; never overwritten (a run whose revision is already published
# pushes nothing; raise IMAGE_REVISION in versions.env to publish a rebuild)
# <MINIO_TAG> moves to the newest build of that release
# latest moves to the newest build (skipped when tag_latest is false)

on:
push:
branches: [main]
paths:
- Dockerfile
- versions.env
- docker-entrypoint.sh
- minio-healthcheck
- test/**
- .github/workflows/build.yml
pull_request:
paths:
- Dockerfile
- versions.env
- docker-entrypoint.sh
- minio-healthcheck
- test/**
- .github/workflows/build.yml
workflow_dispatch:
inputs:
Expand Down Expand Up @@ -49,13 +62,50 @@ jobs:
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
push="${{ inputs.push }}"; latest="${{ inputs.tag_latest }}"
fi
revision_tag="$IMAGE:$MINIO_TAG-r$IMAGE_REVISION"
# The -rN tag is immutable: if it already exists, do not publish again.
if [ "$push" = "true" ] && docker buildx imagetools inspect "$revision_tag" >/dev/null 2>&1; then
push=false
{
echo "## Not published"
echo
echo "\`$revision_tag\` already exists and is never overwritten."
echo "To publish a rebuild, raise \`IMAGE_REVISION\` in versions.env (or run the update workflow with force_rebuild)."
} >> "$GITHUB_STEP_SUMMARY"
fi
echo "push=$push" >> "$GITHUB_OUTPUT"
tags="$IMAGE:$MINIO_TAG"
echo "revision_tag=$revision_tag" >> "$GITHUB_OUTPUT"
tags="$revision_tag,$IMAGE:$MINIO_TAG"
if [ "$latest" = "true" ]; then tags="$tags,$IMAGE:latest"; fi
echo "tags=$tags" >> "$GITHUB_OUTPUT"

- uses: docker/setup-buildx-action@v3

- name: Build for the smoke test (linux/amd64, loaded locally)
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
load: true
push: false
tags: insectai/minio:smoke
build-args: |
GO_IMAGE=${{ env.GO_IMAGE }}
RUNTIME_IMAGE=${{ env.RUNTIME_IMAGE }}
MINIO_REPO=${{ env.MINIO_REPO }}
MINIO_TAG=${{ env.MINIO_TAG }}
MINIO_COMMIT=${{ env.MINIO_COMMIT }}
MC_REPO=${{ env.MC_REPO }}
MC_TAG=${{ env.MC_TAG }}
MC_COMMIT=${{ env.MC_COMMIT }}
SOURCE_REVISION=${{ github.sha }}
provenance: false
sbom: false
cache-from: type=gha

- name: Smoke test
run: test/smoke.sh insectai/minio:smoke

- name: Log in to Docker Hub
if: steps.mode.outputs.push == 'true'
uses: docker/login-action@v3
Expand Down Expand Up @@ -96,9 +146,11 @@ jobs:
echo "Copy this into docker-compose files to pin the image:"
echo
echo '```'
echo "image: $IMAGE:$MINIO_TAG@${{ steps.build.outputs.digest }}"
echo "image: ${{ steps.mode.outputs.revision_tag }}@${{ steps.build.outputs.digest }}"
echo '```'
echo
echo "Tags pushed: \`${{ steps.mode.outputs.tags }}\`"
echo
echo "Server: \`$MINIO_REPO\` @ \`$MINIO_TAG\` (\`$MINIO_COMMIT\`)"
echo
echo "Client: \`$MC_REPO\` @ \`$MC_TAG\` (\`$MC_COMMIT\`)"
Expand Down
81 changes: 81 additions & 0 deletions .github/workflows/scan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Scan published image

# Scans the published insectai/minio:latest for known vulnerabilities with Trivy: weekly, on demand, and after
# every successful publish from main. Findings rated CRITICAL or HIGH that have a fix available fail the run
# and appear under the repository's Security tab (code scanning).
#
# Trivy reads the Go module list embedded in the compiled minio and mc binaries, so it reports vulnerable Go
# dependencies and Go standard library versions, not only Alpine packages. For this image that is the main
# signal: most fixes arrive as a new upstream release or a newer GO_IMAGE, which the "Check for updates"
# workflow proposes.

on:
schedule:
- cron: "0 6 * * 2" # Tuesdays 06:00 UTC
workflow_dispatch:
workflow_run:
workflows: ["Build and push image"]
types: [completed]
branches: [main]

permissions:
contents: read
security-events: write

env:
IMAGE_REF: insectai/minio:latest

jobs:
scan:
# After a publish, only scan when the build succeeded.
if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
steps:
# Pinned by commit: v0.36.0.
- name: Scan for the Security tab (SARIF)
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
with:
scan-type: image
image-ref: ${{ env.IMAGE_REF }}
format: sarif
output: trivy-results.sarif
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: "0"

- name: Upload to code scanning
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: trivy-results.sarif
category: trivy-published-image

# Every input is repeated here because trivy-action carries some settings over between calls in one job.
- name: Scan and fail on CRITICAL or HIGH findings
id: table
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25
with:
scan-type: image
image-ref: ${{ env.IMAGE_REF }}
format: table
output: trivy-results.txt
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: "1"

- name: Write the findings to the run summary
if: always()
run: |
{
echo "## Trivy: \`$IMAGE_REF\` (CRITICAL and HIGH, fixable only)"
echo
if [ "${{ steps.table.outcome }}" = "success" ]; then
echo "No CRITICAL or HIGH findings with a fix available."
else
echo "Findings below. Fixes usually arrive as an upstream release or a newer GO_IMAGE/RUNTIME_IMAGE,"
echo "which the \"Check for updates\" workflow proposes; it can also be run by hand."
fi
echo
echo '```'
cat trivy-results.txt 2>/dev/null || echo "(no table output; see the step log)"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
133 changes: 133 additions & 0 deletions .github/workflows/update.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
name: Check for updates

# Weekly: asks scripts/check_upstream.py whether versions.env needs a change. A change is either a new
# upstream release (new pins, IMAGE_REVISION back to 1) or a rebuild of the current release (IMAGE_REVISION + 1)
# because a base image has a newer patch release (or, if rebuild_if_older_than_days is set, because the published
# image is older than that). The age rule is off by default: the runtime stage only copies files onto the pinned
# Alpine image, so a rebuild with unchanged pins produces the same image; newer Go or Alpine patch tags are what
# matter, and the script detects those on its own.
#
# When something changed, the new pins are built for linux/amd64 and smoke-tested here, and only then is a
# pull request opened on the update/versions branch. Merging that pull request publishes the image through
# the "Build and push image" workflow. Nothing is published from this workflow.
#
# Pull requests opened with the default GITHUB_TOKEN do not trigger other workflows, which is why the build
# and smoke test run inline. If the UPDATE_PR_TOKEN secret (a fine-grained token with contents and pull
# requests write access to this repository) is set, it is used instead and the normal PR checks run too.

on:
schedule:
- cron: "0 6 * * 1" # Mondays 06:00 UTC
workflow_dispatch:
inputs:
force_rebuild:
description: Rebuild the current release with the next IMAGE_REVISION even if nothing changed
type: boolean
default: false
rebuild_if_older_than_days:
description: Also rebuild when the published image is older than this many days (0 = off)
type: number
default: 0

permissions:
contents: write
pull-requests: write

concurrency:
group: update-versions
cancel-in-progress: false

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Check upstream
id: check
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Scheduled runs have no inputs; fall back to the defaults above.
OLDER_THAN: ${{ inputs.rebuild_if_older_than_days || 0 }}
FORCE: ${{ inputs.force_rebuild && 'true' || 'false' }}
run: |
args=(--write --github-output "$GITHUB_OUTPUT")
if [ "${OLDER_THAN:-0}" -gt 0 ]; then args+=(--rebuild-if-older-than "$OLDER_THAN"); fi
if [ "$FORCE" = "true" ]; then args+=(--force-rebuild); fi
python3 scripts/check_upstream.py "${args[@]}"

- name: Summary (no change)
if: steps.check.outputs.changed != 'true'
run: |
echo "No update needed: pins are current and the published image is recent." >> "$GITHUB_STEP_SUMMARY"

- name: Load the new pins
if: steps.check.outputs.changed == 'true'
run: grep -v '^#' versions.env | grep . >> "$GITHUB_ENV"

- uses: docker/setup-buildx-action@v3
if: steps.check.outputs.changed == 'true'

- name: Build the new pins (linux/amd64, loaded locally)
if: steps.check.outputs.changed == 'true'
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64
load: true
push: false
tags: insectai/minio:update-check
build-args: |
GO_IMAGE=${{ env.GO_IMAGE }}
RUNTIME_IMAGE=${{ env.RUNTIME_IMAGE }}
MINIO_REPO=${{ env.MINIO_REPO }}
MINIO_TAG=${{ env.MINIO_TAG }}
MINIO_COMMIT=${{ env.MINIO_COMMIT }}
MC_REPO=${{ env.MC_REPO }}
MC_TAG=${{ env.MC_TAG }}
MC_COMMIT=${{ env.MC_COMMIT }}
SOURCE_REVISION=${{ github.sha }}
provenance: false
sbom: false
cache-from: type=gha

- name: Smoke test
if: steps.check.outputs.changed == 'true'
run: test/smoke.sh insectai/minio:update-check

- name: Open or update the pull request
if: steps.check.outputs.changed == 'true'
id: pr
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.UPDATE_PR_TOKEN || secrets.GITHUB_TOKEN }}
branch: update/versions
delete-branch: true
add-paths: versions.env
commit-message: |
chore: update pinned versions

${{ steps.check.outputs.title }}
title: ${{ steps.check.outputs.title }}
body: |
${{ steps.check.outputs.body }}

Built for linux/amd64 and smoke-tested in run ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} before this PR was opened.

Merging this pull request publishes the image to Docker Hub (the "Build and push image" workflow pushes the revision tag, the release tag and `latest`). After that, update the digest pins in the repositories that use the image.

- name: Summary (change)
if: steps.check.outputs.changed == 'true'
env:
TITLE: ${{ steps.check.outputs.title }}
PR_URL: ${{ steps.pr.outputs.pull-request-url }}
PR_OP: ${{ steps.pr.outputs.pull-request-operation }}
BODY: ${{ steps.check.outputs.body }}
run: |
{
echo "## $TITLE"
echo
echo "Built and smoke-tested. Pull request ($PR_OP): $PR_URL"
echo
echo "$BODY"
} >> "$GITHUB_STEP_SUMMARY"
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ LABEL org.opencontainers.image.title="MinIO server and mc client (community buil
org.insectai.mc.commit="${MC_COMMIT}"
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /out/ /
# The entrypoint creates MINIO_DEFAULT_BUCKETS at startup; the health check reports healthy only once the
# server is ready and those buckets exist. See README "Creating buckets at startup".
COPY --chmod=0755 docker-entrypoint.sh /usr/bin/docker-entrypoint.sh
COPY --chmod=0755 minio-healthcheck /usr/bin/minio-healthcheck
EXPOSE 9000 9001
VOLUME ["/data"]
ENTRYPOINT ["/usr/bin/minio"]
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=12 CMD ["/usr/bin/minio-healthcheck"]
ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"]
CMD ["server", "/data", "--console-address", ":9001"]
Loading
Loading