Created
November 4, 2023 19:08
-
-
Save Clumsy-Coder/08f5e0f93f38e726c6b371142159b504 to your computer and use it in GitHub Desktop.
github-actions NextJS docker build with semantic-release
This file contains hidden or 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
--- | |
# .github/workflows/build.yaml | |
name: NextJS build and release | |
# description: lint, test, build and release NextJS | |
on: push | |
env: | |
FORCE_COLOR: true # display terminal colors | |
# APP_NAME: app_name | |
# GHCR_IMAGE: ghcr.io/<user>/<repo name> | |
CONTAINER_REGISTRY: ghcr.io | |
IMAGE_NAME: clumsy-coder/pihole-dashboard | |
#################################################################################################### | |
jobs: | |
# install npm packages and store them as cache. | |
install: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: actions/setup-node@v3 | |
with: | |
node-version: latest | |
- name: Cache node modules | |
id: cache-primes | |
uses: actions/cache@v3 | |
with: | |
path: node_modules | |
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | |
restore-keys: ${{ runner.os }}-node- | |
# skip npm ci if `package.json` didn't change | |
# https://github.com/actions/cache#outputs | |
# https://github.com/actions/cache#restoring-and-saving-cache-using-a-single-action | |
- name: Install npm dependencies | |
if: steps.cache-primes.outputs.cache-hit != 'true' | |
run: npm ci --include=dev | |
################################################################################################ | |
# lint source code using ESlint and Typescript | |
lint: | |
needs: install | |
runs-on: ubuntu-latest | |
timeout-minutes: 10 | |
steps: | |
- uses: actions/checkout@v4 | |
- uses: actions/setup-node@v3 | |
with: | |
node-version: latest | |
- name: Cache node modules | |
uses: actions/cache@v3 | |
with: | |
path: node_modules | |
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | |
restore-keys: ${{ runner.os }}-node- | |
- name: Lint project | |
run: npm run lint | |
################################################################################################ | |
# prepare docker built | |
# extract the next version tag | |
prepare-docker-build: | |
needs: install | |
runs-on: ubuntu-latest | |
outputs: | |
NEXT_VERSION: ${{ steps.set-env.outputs.NEXT_VERSION }} | |
PUBLISH: ${{ steps.set-env.outputs.PUBLISH }} | |
BUILD_DATE: ${{ steps.set-env.outputs.BUILD_DATE }} | |
GIT_SHA: ${{ steps.set-env.outputs.GIT_SHA }} | |
GIT_REF: ${{ steps.set-env.outputs.GIT_REF }} | |
new-release-published: ${{ steps.get-next-version.outputs.new-release-published }} | |
new-release-version: ${{ steps.get-next-version.outputs.new-release-version }} | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- uses: actions/setup-node@v3 | |
with: | |
node-version: latest | |
- name: Cache node modules | |
uses: actions/cache@v3 | |
with: | |
path: node_modules | |
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | |
restore-keys: ${{ runner.os }}-node- | |
- name: Extract next semantic-release version | |
# run: npx semantic-release --dry-run --branches="*" | |
run: npx semantic-release --dry-run | |
id: get-next-version | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
# needed in case semantic-release doesn't run on branches other than 'master' or 'development' | |
- name: Set NEXT_VERSION if there's NO new release | |
if: | | |
steps.get-next-version.outputs.new-release-published == '' || | |
steps.get-next-version.outputs.new-release-published == 'false' | |
run: | | |
node -p "require('./package').version" | |
node -p "require('./package').version" | awk '{print "NEXT_VERSION=" $1}' >> "$GITHUB_ENV" | |
echo "PUBLISH=false" >> "$GITHUB_ENV" | |
- name: Set NEXT_VERSION if there's a NEW release | |
if: steps.get-next-version.outputs.new-release-published == 'true' | |
run: | | |
echo ${{ steps.get-next-version.outputs.new-release-version }} | |
echo "NEXT_VERSION=${{ steps.get-next-version.outputs.new-release-version }}" >> "$GITHUB_ENV" | |
echo "PUBLISH=true" >> "$GITHUB_ENV" | |
- name: Set Environment Variables | |
id: set-env | |
run: | | |
{ | |
echo "NEXT_VERSION=$NEXT_VERSION" | |
echo "PUBLISH=$PUBLISH" | |
echo "BUILD_DATE=$(date +'%Y-%m-%d %H:%M:%S')" | |
echo "GIT_SHA=$(echo ${{ github.sha }} | cut -c1-7)" | |
# replace `/` with `-` | |
# mainly used for branches created by dependabot. they contain `/` which docker tagname cannot have | |
# https://docs.docker.com/engine/reference/commandline/tag/#description | |
# https://stackoverflow.com/a/73799519/3053548 | |
# https://stackoverflow.com/a/73467468/3053548 | |
echo "GIT_REF=${GITHUB_REF_NAME//\//-}" | |
# echo "GHCR_IMAGE=$(echo 'console.log("ghcr.io/${{ github.repository }}".toLowerCase())' | node -)" >> $GITHUB_ENV | |
} | tee -a "$GITHUB_ENV" "$GITHUB_OUTPUT" | |
- run: echo "$GITHUB_ENV" | |
################################################################################################ | |
# build docker images using reusable workflow and github action matrix | |
docker-build-image: | |
needs: prepare-docker-build | |
strategy: | |
matrix: | |
# using paired matrix values | |
# obtained from | |
# https://stackoverflow.com/a/76547617/3053548 | |
include: | |
- version: latest | |
publish: ${{ needs.prepare-docker-build.outputs.publish == 'true' }} | |
# next version | |
- version: ${{ needs.prepare-docker-build.outputs.next_version }} | |
publish: ${{ needs.prepare-docker-build.outputs.publish == 'true' }} | |
# nightly | |
- version: nightly | |
publish: ${{ github.ref == 'refs/heads/development' }} | |
# branches that are not `development` or `master` | |
# used for testing code of development branches | |
- version: ${{ needs.prepare-docker-build.outputs.git_ref }} | |
publish: ${{ github.ref != 'refs/heads/development' && github.ref != 'refs/heads/master' && github.actor != 'dependabot[bot]' }} | |
uses: ./.github/workflows/reusable-docker-build.yml | |
with: | |
publish: ${{ matrix.publish }} | |
tags: ${{ matrix.version }} | |
secrets: inherit | |
################################################################################################ | |
# verify matrix job results | |
# since branch protection rules can't group the job matrix together as a required status check. | |
# this will serve as a workaround | |
# code obtained from | |
# - https://github.com/orgs/community/discussions/26822#discussioncomment-5122101 | |
matrix-results: | |
needs: docker-build-image | |
runs-on: ubuntu-latest | |
name: verify job matrix results | |
if: always() | |
steps: | |
- run: exit 1 | |
# see https://stackoverflow.com/a/67532120/4907315 | |
if: >- | |
${{ | |
contains(needs.*.result, 'failure') | |
|| contains(needs.*.result, 'cancelled') | |
}} | |
################################################################################################ | |
semantic-release: | |
# prerequisite of a job that uses matrix | |
# https://github.com/orgs/community/discussions/42010#discussioncomment-4439644 | |
needs: docker-build-image | |
runs-on: ubuntu-latest | |
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/development' | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
persist-credentials: false | |
- uses: actions/setup-node@v3 | |
with: | |
node-version: latest | |
- name: Cache node modules | |
uses: actions/cache@v3 | |
with: | |
path: node_modules | |
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} | |
restore-keys: ${{ runner.os }}-node- | |
- run: npm install --production=false | |
- name: semantic-release | |
run: npx semantic-release --ci | |
env: | |
GITHUB_TOKEN: ${{ secrets.DEPENDABOT_TOKEN }} | |
#################################################################################################### |
This file contains hidden or 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
--- | |
# .github/workflows/cleanup-docker-image.yml | |
# remove docker image that's NOT from `master` or `development` | |
# | |
# branch created from `development` will create docker image with the tagname of the branch name | |
# after a pull request is merged to the `development` branch, this github action will remove | |
# docker image with the tagname of the `development` branch | |
# | |
# Ex: | |
# - branch name `620` from `development` branch is created | |
# - committed some code | |
# - github action creates docker image with tagname `620` | |
# - created a pull request | |
# - after pull request closed and merged, this github action will remove the docker image with tagname `620` | |
# | |
# code obtained from | |
# - extracting json property conditionally | |
# - https://stackoverflow.com/a/47887662/3053548 | |
# - GET list of docker images | |
# - https://docs.github.com/en/rest/packages/packages?apiVersion=2022-11-28#list-package-versions-for-a-package-owned-by-a-user | |
# - github action to extract version ID | |
# - https://github.com/actions/delete-package-versions/issues/101#issuecomment-1553166050 | |
name: Cleanup docker image on PR merge | |
on: | |
pull_request_target: | |
# types: | |
# - closed | |
# - opened | |
branches-ignore: | |
- master | |
env: | |
FORCE_COLOR: true | |
CONTAINER_REGISTRY: ghcr.io | |
IMAGE_NAME: clumsy-coder/pihole-dashboard | |
PACKAGE_NAME: pihole-dashboard | |
permissions: | |
# needed for pushing docker images from branches created by dependabot[bot]. | |
# check | |
# - https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs#defining-access-for-the-github_token-scopes | |
# - https://github.blog/changelog/2021-10-06-github-actions-workflows-triggered-by-dependabot-prs-will-respect-permissions-key-in-workflows/ | |
# - https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#responding-to-events | |
# | |
# check issue | |
# - #649 | |
# check commit | |
# - 1548272 | |
# - cc87aba | |
# - 5f9737d | |
packages: write | |
pull-requests: write | |
jobs: | |
remove-dev-image: | |
if: github.event.pull_request.merged == true | |
runs-on: ubuntu-latest | |
env: | |
HEAD_REF: ${{ github.event.pull_request.head.ref }} | |
steps: | |
- name: replace `/` with `-` in branch name | |
# mainly used for branches created by dependabot. they contain `/` which docker tagname cannot have | |
# https://docs.docker.com/engine/reference/commandline/tag/#description | |
# https://stackoverflow.com/a/73799519/3053548 | |
# https://stackoverflow.com/a/73467468/3053548 | |
# check issue: | |
# - #649 | |
# check commit | |
# - 1548272 | |
# - cc87aba | |
run: echo "HEAD_REF=${HEAD_REF//\//-}" >> "$GITHUB_ENV" | |
- run: echo "$GITHUB_ENV" | |
- name: Get Version ID of docker image | |
run: | | |
curl -L \ | |
-H "Accept: application/vnd.github+json" \ | |
-H "Authorization: Bearer ${{ secrets.DEPENDABOT_TOKEN }}" \ | |
-H "X-GitHub-Api-Version: 2022-11-28" \ | |
https://api.github.com/users/${{ github.repository_owner }}/packages/container/${{ env.PACKAGE_NAME }}/versions >> containerMeta.json ; | |
echo "VERSION_ID=$(jq -r '.[] | select(.metadata.container.tags[] == "${{ env.HEAD_REF }}").id' containerMeta.json)" >> "$GITHUB_ENV" ; | |
- name: Print version ID | |
run: echo ${{ env.VERSION_ID }} | |
- name: Remove pull request image from container registry | |
uses: actions/delete-package-versions@v4 | |
if: ${{ env.VERSION_ID != '' }} | |
with: | |
package-name: ${{ env.PACKAGE_NAME }} | |
package-type: 'container' | |
package-version-ids: "${{ env.VERSION_ID }}" |
This file contains hidden or 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
--- | |
# .github/workflows/dependabot.yml | |
name: Dependabot automerge | |
on: pull_request | |
# description: Automerge dependabot pull requests | |
# | |
# auto merge Dependabot pull requests | |
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#enable-auto-merge-on-a-pull-request | |
jobs: | |
automerge-pr: | |
runs-on: ubuntu-latest | |
permissions: | |
pull-requests: write | |
contents: write | |
if: | | |
github.actor == 'dependabot[bot]' && | |
( | |
startsWith(github.event.pull_request.title, 'build(deps):') || | |
startsWith(github.event.pull_request.title, 'build(devDep):') || | |
startsWith(github.event.pull_request.title, 'ci(github-action):') || | |
startsWith(github.event.pull_request.title, 'ci(docker):') | |
) | |
steps: | |
- name: Dependabot metadata | |
id: metadata | |
uses: dependabot/[email protected] | |
with: | |
github-token: '${{ secrets.GITHUB_TOKEN }}' | |
# make sure to enable 'auto-merge' in the repo | |
# https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/configuring-pull-request-merges/managing-auto-merge-for-pull-requests-in-your-repository | |
- name: Enable auto-merge for Dependabot PRs | |
run: gh pr merge --auto --rebase "$PR_URL" | |
env: | |
PR_URL: ${{github.event.pull_request.html_url}} | |
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} |
This file contains hidden or 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
--- | |
# .github/workflows/reusable-docker-build.yml | |
name: Build and push Docker image | |
# When calling this workflow, ensure you use | |
# secrets: inherit | |
# So the DOCKER_USERNAME and DOCKER_PASSWORD are available. | |
on: | |
workflow_call: | |
inputs: | |
publish: | |
type: boolean | |
description: Whether to publish the image to Github Registry | |
required: false | |
default: false | |
tags: | |
type: string | |
required: true | |
description: docker image version | |
# labels: | |
# type: string | |
# required: true | |
# description: docker image labels | |
env: | |
FORCE_COLOR: true | |
CONTAINER_REGISTRY: ghcr.io | |
IMAGE_NAME: clumsy-coder/pihole-dashboard | |
PACKAGE_NAME: pihole-dashboard | |
permissions: | |
# needed for pushing docker images from branches created by dependabot[bot]. | |
# check | |
# - https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs#defining-access-for-the-github_token-scopes | |
# - https://github.blog/changelog/2021-10-06-github-actions-workflows-triggered-by-dependabot-prs-will-respect-permissions-key-in-workflows/ | |
# - https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions#responding-to-events | |
# | |
# check issue | |
# - #649 | |
# check commit | |
# - 1548272 | |
# - cc87aba | |
# - 5f9737d | |
packages: write | |
jobs: | |
build-and-push: | |
name: Docker Build and Push | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
- name: generate GIT_SHA and GIT_REF to GITHUB_ENV | |
run: | | |
{ | |
echo "GIT_SHA=$(echo ${{ github.sha }} | cut -c1-7)" | |
echo "GIT_REF=$(git symbolic-ref -q --short HEAD || git describe --tags --exact-match)" | |
} >> "$GITHUB_ENV" | |
- name: Create .env.local for NextJS | |
run: | | |
{ | |
printf "NEXT_PUBLIC_BUILD_VERSION=%s\n" "${{ inputs.tags }}" | |
printf "NEXT_PUBLIC_BUILD_ID=%s\n" "$(echo ${{ github.sha }} | cut -c -7)" | |
printf "SECRET_COOKIE_PASSWORD=%s\n" "${{ secrets.SECRET_COOKIE_PASSWORD }}" | |
printf "SECURE_COOKIE_TTL=%s\n" "${{ secrets.SECURE_COOKIE_TTL }}" | |
printf "NEXT_PUBLIC_POLLING_AUTH_SESSION=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_AUTH_SESSION }}" | |
printf "NEXT_PUBLIC_POLLING_SUMMARY=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_SUMMARY }}" | |
printf "NEXT_PUBLIC_POLLING_FORWARDED_DESTINATIONS=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_FORWARDED_DESTINATIONS }}" | |
printf "NEXT_PUBLIC_POLLING_QUERY_TYPES=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_QUERY_TYPES }}" | |
printf "NEXT_PUBLIC_POLLING_TOP_PERMITTED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_TOP_PERMITTED_QUERIES }}" | |
printf "NEXT_PUBLIC_POLLING_TOP_BLOCKED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_TOP_BLOCKED_QUERIES }}" | |
printf "NEXT_PUBLIC_NUM_ENTRIES_TOP_PERMITTED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_NUM_ENTRIES_TOP_PERMITTED_QUERIES }}" | |
printf "NEXT_PUBLIC_NUM_ENTRIES_TOP_BLOCKED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_NUM_ENTRIES_TOP_BLOCKED_QUERIES }}" | |
printf "NEXT_PUBLIC_POLLING_TOP_CLIENTS_ALLOWED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_TOP_CLIENTS_ALLOWED_QUERIES }}" | |
printf "NEXT_PUBLIC_POLLING_TOP_CLIENTS_BLOCKED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_TOP_CLIENTS_BLOCKED_QUERIES }}" | |
printf "NEXT_PUBLIC_NUM_ENTRIES_TOP_CLIENTS_ALLOWED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_NUM_ENTRIES_TOP_CLIENTS_ALLOWED_QUERIES }}" | |
printf "NEXT_PUBLIC_NUM_ENTRIES_TOP_CLIENTS_BLOCKED_QUERIES=%s\n" "${{ secrets.NEXT_PUBLIC_NUM_ENTRIES_TOP_CLIENTS_BLOCKED_QUERIES }}" | |
printf "NEXT_PUBLIC_POLLING_QUERIES_OVERTIME=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_QUERIES_OVERTIME }}" | |
printf "NEXT_PUBLIC_POLLING_CLIENTS_OVERTIME=%s\n" "${{ secrets.NEXT_PUBLIC_POLLING_CLIENTS_OVERTIME }}" | |
printf "NEXT_PUBLIC_BUILD_TIME=%s\n" "$(date +%s)" | |
} >> .env.local | |
cat .env.local | |
- name: Set up QEMU | |
uses: docker/setup-qemu-action@v3 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Login to Container Registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Extract Docker image metadata | |
id: meta | |
uses: docker/[email protected] | |
with: | |
images: ${{ env.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }} | |
labels: | | |
org.opencontainers.image.authors=${{ github.repository_owner }} | |
org.opencontainers.image.ref.name=${{ env.GIT_REF }} | |
org.opencontainers.image.version=${{ inputs.tags }} | |
- name: Build and push Docker image | |
uses: docker/build-push-action@v5 | |
with: | |
context: . | |
file: ./dockerfile | |
pull: true | |
# push: ${{ inputs.publish }} | |
load: true | |
tags: ${{ env.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.tags }} | |
labels: ${{ steps.meta.outputs.labels }} | |
cache-from: type=gha | |
cache-to: type=gha,mode=max | |
- name: View current docker images | |
run: docker images | |
- name: Push docker image | |
if: ${{ inputs.publish }} | |
run: docker push ${{ env.CONTAINER_REGISTRY }}/${{ env.IMAGE_NAME }}:${{ inputs.tags }} | |
- name: Remove untagged image on container registry | |
if: ${{ inputs.publish }} | |
uses: actions/delete-package-versions@v4 | |
with: | |
package-name: ${{ env.PACKAGE_NAME }} | |
package-type: 'container' | |
delete-only-untagged-versions: 'true' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment