diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..64cf9a1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +patreon: bretfisher diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f9ecf57 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + # Maintain dependencies for GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/.github/linters/.hadolint.yaml b/.github/linters/.hadolint.yaml new file mode 100644 index 0000000..362b1aa --- /dev/null +++ b/.github/linters/.hadolint.yaml @@ -0,0 +1,10 @@ +ignored: + - DL3003 #ignore that we use cd sometimes + - DL3006 #image pin versions + - DL3008 #apt pin versions + - DL3018 #apk add pin versions + - DL3022 #bad rule for COPY --from + - DL3028 #gem install pin versions + - DL3059 #multiple consecutive runs + - DL4006 #we don't need pipefail in this + - SC2016 #we want single quotes sometimes \ No newline at end of file diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml new file mode 100644 index 0000000..aef4e33 --- /dev/null +++ b/.github/linters/.markdown-lint.yml @@ -0,0 +1,7 @@ +# MD013/line-length - Line length +MD013: + # Number of characters + line_length: 150 + # Number of characters for headings + heading_line_length: 100 + code_blocks: false \ No newline at end of file diff --git a/.github/workflows/docker-build-and-push.yaml b/.github/workflows/docker-build-and-push.yaml new file mode 100644 index 0000000..bab1085 --- /dev/null +++ b/.github/workflows/docker-build-and-push.yaml @@ -0,0 +1,147 @@ +--- +name: Docker Build and Push Image + +on: + # we want pull requests so we can build(test) but not push to image registry + pull_request: + branches: + - 'main' + # only build when important files change + paths-ignore: + - 'README.md' + - '.github/workflows/linter.yml' + - '.github/linters/**' + push: + branches: + - 'main' + # only build when important files change + paths-ignore: + - 'README.md' + - '.github/workflows/linter.yml' + - '.github/linters/**' + schedule: + # re-run monthly to keep image fresh with upstream base images + # NOTE: GH will stop cron jobs in a stale repo (60 days) + # https://docs.github.com/en/actions/managing-workflow-runs/disabling-and-enabling-a-workflow + - cron: '0 12 15 * *' + # run whenever we want! + # workflow_dispatch: + # REUSABLE WORKFLOW with INPUTS + # to keep this workflow simple, assumptions are made: + # - only able to push to Docker Hub and or GHCR + # - Image name is name of GitHub repo + # - Dockerfile is in root of repo, named 'Dockerfile' + # - Builds on PR with tag of `prNUMBER` (same tag each PR push) + # - Builds on push to main branch with tag of `latest` + # ???? what else + workflow_call: + # allow reuse of this workflow in other repos + inputs: + # TODO: allow dynamic docker hub and ghcr + dockerhub-enabled: + description: Push images to Docker Hub + + type: boolean + ghcr-enabled: + dockerhub-username: + description: Docker Hub username + required: false + type: string + context: + description: Docker context (path) to start build from + required: false + type: string + default: . + target: + description: Build stage to target + required: false + type: string + platforms: + description: Platforms to build for + required: false + type: string + # common ones: linux/amd64,linux/arm64,linux/arm/v7 + default: linux/amd64 + # TODO: does this work in calling repos? + image-names: + description: A list of the account/repo names for docker build + required: false + type: string + # the default will tag it same as repo name (for hub.docker.com) and + # ghcr.io/ + default: | + ${{ github.repository }} + ghcr.io/${{ github.repository }} + secrets: + dockerhub-token: + description: Docker Hub token + required: false + + +jobs: + build-and-push-image: + name: Build+Push + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2.4.0 + - + # we need qemu and buildx so we can build multiple platforms later + name: Set up QEMU + id: qemu + uses: docker/setup-qemu-action@v1.2.0 + - + # BuildKit (used with `docker buildx`) is the best way to build images + name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + if: ${{ inputs.dockerhub-enabled }} + uses: docker/login-action@v1 + with: + username: ${{ inputs.dockerhub-username }} + password: ${{ secrets.dockerhub-token }} + - + name: Login to GHCR + if: ${{ inputs.ghcr-enabled }} + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Docker meta + id: docker_meta + uses: docker/metadata-action@v3.6.2 + with: + # list of Docker images to use as base name for tags + images: ${{ inputs.image-names }} + flavor: | + latest=false + tags: | + type=raw,value=latest,enable=${{ endsWith(github.ref, github.event.repository.default_branch) }} + type=ref,event=pr,prefix=pr + - + # this will build the images, once per platform, + # then push to both Docker Hub and GHCR + name: Docker Build and Push + id: docker_build_and_push + uses: docker/build-push-action@v2 + with: + platforms: ${{ inputs.platforms }} + context: ${{ inputs.context }} + target: ${{ inputs.target }} + builder: ${{ steps.buildx.outputs.name }} + # it uses github cache API for faster builds: + # https://github.com/crazy-max/docker-build-push-action/blob/master/docs/advanced/cache.md#cache-backend-api + cache-from: type=gha + cache-to: type=gha,mode=max + # for an approved pull_request, only push pr-specific tags + push: true + tags: ${{ steps.docker_meta.outputs.tags }} + labels: ${{ steps.docker_meta.outputs.labels }} + - + name: Show image digest + run: echo ${{ steps.docker_build_and_push.outputs.digest }} \ No newline at end of file diff --git a/.github/workflows/linter.yaml b/.github/workflows/linter.yaml new file mode 100644 index 0000000..cd04c15 --- /dev/null +++ b/.github/workflows/linter.yaml @@ -0,0 +1,19 @@ +--- +name: Lint Code Base + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + call-super-linter: + # use Reusable Workflows to call my linter config remotely + # https://docs.github.com/en/actions/learn-github-actions/reusing-workflows + uses: bretfisher/super-linter-workflow/.github/workflows/super-linter.yaml@main + with: + # disable common code linters + devops-only: true + # prevent scanning a comma separated regex list + #filter-regex-exclude: .*compose-sample-3/html/.* diff --git a/README.md b/README.md index b423719..0ad7e65 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,19 @@ -# docker-build-workflow -A Reusable Workflow of the Docker GitHub Actions +# Docker Build and Push GitHub Actions Workflow + +A Reusable Workflow of the Docker GitHub Actions steps. Enhanced with learnings from production use. + +## Reasons to use this workflow + +1. Easier to start with than hand-building all the Docker Actions into a single workflow. +2. Provides inline docs based on real-world usage of this workflow. +3. Gives you inputs so you can reuse this workflow across many repositories +and only needing the full workflow stored in a central repository. + +## Basic workflow steps + +1. Clone the repository +2. Setup QEMU for multi-platform building (buildx) via docker/setup-qemu-action +3. Setup buildx (the best image builder) via docker/setup-buildx-action +4. Log into Docker Hub and/or GHCR +5. Add labels and tags via docker/metadata-action +6. Build and push image via docker/build-push-action