feat: builds with gox and buildx (#2381)

* feat: builds with gox and buildx

This change builds all of Authelia respective binaries in parallel within a single step and distributes as necessary to subsequent steps, we now also build and distribute for the following OS/Architecture: freebsd/amd64.

Our CI/CD pipeline now also utilises docker buildx as a default for builds and pushes.

* refactor: clean up docker helper

* Remove `authelia-scripts docker push-image` command as all pushes will be performed with buildx and manifests
* Rename the --arch flag to --container
* Add Dockerfile.dev for users that want to build an Authelia container from source without utilising suites
* Set Dockerfile.dev as default for `authelia-scripts docker build` command

* refactor: variant -> container
pull/2383/head
Amir Zarrinkafsh 2021-09-16 22:39:18 +10:00 committed by GitHub
parent e1ca24344a
commit 92ec00d7c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 212 additions and 448 deletions

View File

@ -5,24 +5,26 @@
<dd> <dd>
<a href="artifact://authelia-linux-amd64.tar.gz">authelia-linux-amd64.tar.gz</a><br> <a href="artifact://authelia-linux-amd64.tar.gz">authelia-linux-amd64.tar.gz</a><br>
<a href="artifact://authelia-linux-amd64.tar.gz.sha256">authelia-linux-amd64.tar.gz.sha256</a><br> <a href="artifact://authelia-linux-amd64.tar.gz.sha256">authelia-linux-amd64.tar.gz.sha256</a><br>
<a href="artifact://authelia-freebsd-amd64.tar.gz">authelia-freebsd-amd64.tar.gz</a><br>
<a href="artifact://authelia-freebsd-amd64.tar.gz.sha256">authelia-freebsd-amd64.tar.gz.sha256</a><br>
<a href="artifact://authelia_amd64.deb">authelia_amd64.deb</a><br> <a href="artifact://authelia_amd64.deb">authelia_amd64.deb</a><br>
<a href="artifact://authelia_amd64.deb.sha256">authelia_amd64.deb.sha256</a> <a href="artifact://authelia_amd64.deb.sha256">authelia_amd64.deb.sha256</a>
</dd> </dd>
</div> </div>
<div class="m1"> <div class="m1">
<dt>arm32v7</dt> <dt>arm</dt>
<dd> <dd>
<a href="artifact://authelia-linux-arm32v7.tar.gz">authelia-linux-arm32v7.tar.gz</a><br> <a href="artifact://authelia-linux-arm.tar.gz">authelia-linux-arm.tar.gz</a><br>
<a href="artifact://authelia-linux-arm32v7.tar.gz.sha256">authelia-linux-arm32v7.tar.gz.sha256</a><br> <a href="artifact://authelia-linux-arm.tar.gz.sha256">authelia-linux-arm.tar.gz.sha256</a><br>
<a href="artifact://authelia_armhf.deb">authelia_armhf.deb</a><br> <a href="artifact://authelia_armhf.deb">authelia_armhf.deb</a><br>
<a href="artifact://authelia_armhf.deb.sha256">authelia_armhf.deb.sha256</a> <a href="artifact://authelia_armhf.deb.sha256">authelia_armhf.deb.sha256</a>
</dd> </dd>
</div> </div>
<div class="m1"> <div class="m1">
<dt>arm64v8</dt> <dt>arm64</dt>
<dd> <dd>
<a href="artifact://authelia-linux-arm64v8.tar.gz">authelia-linux-arm64v8.tar.gz</a><br> <a href="artifact://authelia-linux-arm64.tar.gz">authelia-linux-arm64.tar.gz</a><br>
<a href="artifact://authelia-linux-arm64v8.tar.gz.sha256">authelia-linux-arm64v8.tar.gz.sha256</a><br> <a href="artifact://authelia-linux-arm64.tar.gz.sha256">authelia-linux-arm64.tar.gz.sha256</a><br>
<a href="artifact://authelia_arm64.deb">authelia_arm64.deb</a><br> <a href="artifact://authelia_arm64.deb">authelia_arm64.deb</a><br>
<a href="artifact://authelia_arm64.deb.sha256">authelia_arm64.deb.sha256</a> <a href="artifact://authelia_arm64.deb.sha256">authelia_arm64.deb.sha256</a>
</dd> </dd>

View File

@ -22,20 +22,13 @@ env:
CI_BYPASS: ${CI_BYPASS} CI_BYPASS: ${CI_BYPASS}
steps: steps:
- label: ":docker: Image Deployments" - label: ":docker: Deploy Manifest"
command: ".buildkite/steps/deployimages.sh | buildkite-agent pipeline upload"
if: build.env("CI_BYPASS") != "true"
- wait:
if: build.env("CI_BYPASS") != "true"
- label: ":docker: Deploy Manifests"
command: "authelia-scripts docker push-manifest" command: "authelia-scripts docker push-manifest"
depends_on:
- "unit-test"
retry: retry:
manual: manual:
permit_on_passed: true permit_on_passed: true
env:
DOCKER_CLI_EXPERIMENTAL: "enabled"
agents: agents:
upload: "fast" upload: "fast"
if: build.env("CI_BYPASS") != "true" if: build.env("CI_BYPASS") != "true"
@ -43,9 +36,7 @@ steps:
- label: ":github: Deploy Artifacts" - label: ":github: Deploy Artifacts"
command: "ghartifacts.sh" command: "ghartifacts.sh"
depends_on: depends_on:
- "build-docker-linux-amd64" - "unit-test"
- "build-docker-linux-arm32v7"
- "build-docker-linux-arm64v8"
- "build-deb-package-amd64" - "build-deb-package-amd64"
- "build-deb-package-armhf" - "build-deb-package-armhf"
- "build-deb-package-arm64" - "build-deb-package-arm64"

View File

@ -31,7 +31,7 @@ if [[ "${BUILDKITE_LABEL}" =~ ":selenium:" ]] || [[ "${BUILDKITE_LABEL}" =~ ":do
fi fi
fi fi
if [[ "${BUILDKITE_LABEL}" == ":docker: Image Deployments" ]]; then if [[ "${BUILDKITE_LABEL}" == ":debian: Package Builds" ]]; then
cat .buildkite/annotations/artifacts | buildkite-agent annotate --style "success" --context "ctx-success" cat .buildkite/annotations/artifacts | buildkite-agent annotate --style "success" --context "ctx-success"
fi fi
@ -40,7 +40,7 @@ if [[ "${BUILDKITE_LABEL}" =~ ":docker: Deploy" ]]; then
docker logout ghcr.io docker logout ghcr.io
fi fi
if [[ "${BUILDKITE_LABEL}" == ":docker: Deploy Manifests" ]] && [[ "${BUILDKITE_BRANCH}" == "master" ]] && [[ "${BUILDKITE_PULL_REQUEST}" == "false" ]]; then if [[ "${BUILDKITE_LABEL}" == ":docker: Deploy Manifest" ]] && [[ "${BUILDKITE_BRANCH}" == "master" ]] && [[ "${BUILDKITE_PULL_REQUEST}" == "false" ]]; then
echo "--- :docker: Removing tags for deleted branches" echo "--- :docker: Removing tags for deleted branches"
anontoken=$(curl -fsL --retry 3 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:authelia/authelia:pull' | jq -r .token) anontoken=$(curl -fsL --retry 3 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:authelia/authelia:pull' | jq -r .token)
authtoken=$(curl -fs --retry 3 -H "Content-Type: application/json" -X "POST" -d '{"username": "'${DOCKER_USERNAME}'", "password": "'${DOCKER_PASSWORD}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) authtoken=$(curl -fs --retry 3 -H "Content-Type: application/json" -X "POST" -d '{"username": "'${DOCKER_USERNAME}'", "password": "'${DOCKER_PASSWORD}'"}' https://hub.docker.com/v2/users/login/ | jq -r .token)

View File

@ -2,25 +2,25 @@
set +u set +u
declare -A BUILDS=(["linux"]="amd64 arm arm64" ["freebsd"]="amd64")
DOCKER_IMAGE=authelia/authelia DOCKER_IMAGE=authelia/authelia
if [[ "${BUILDKITE_LABEL}" == ":hammer_and_wrench: Unit Test" ]]; then if [[ "${BUILDKITE_LABEL}" == ":hammer_and_wrench: Unit Test" ]]; then
echo "--- :docker: Saving artifacts for :buildkite: :docker: :github: releases"
for OS in "${!BUILDS[@]}"; do
for ARCH in ${BUILDS[$OS]}; do
tar -czf "authelia-${OS}-${ARCH}.tar.gz" "authelia-${OS}-${ARCH}" authelia.service config.template.yml
sha256sum "authelia-${OS}-${ARCH}.tar.gz" > "authelia-${OS}-${ARCH}.tar.gz.sha256"
done
done
tar -czf authelia-public_html.tar.gz -C dist public_html tar -czf authelia-public_html.tar.gz -C dist public_html
sha256sum authelia-public_html.tar.gz > authelia-public_html.tar.gz.sha256 sha256sum authelia-public_html.tar.gz > authelia-public_html.tar.gz.sha256
fi fi
if [[ "${BUILDKITE_LABEL}" =~ ":docker: Build Image" ]]; then if [[ "${BUILDKITE_LABEL}" == ":docker: Build Image [coverage]" ]]; then
echo "--- :docker: Saving artifacts for :buildkite: :docker: :github: releases" # Saving image for docker push
# Save binary for buildkite and github artifacts docker save "${DOCKER_IMAGE}" | zstdmt -T0 -12 > "authelia-image-coverage.tar.zst"
if [[ "${ARCH}" != "coverage" ]]; then
docker create --name authelia-binary "${DOCKER_IMAGE}:latest"
docker cp authelia-binary:/app/authelia "./authelia-${OS}-${ARCH}"
docker rm -f authelia-binary
tar -czf "authelia-${OS}-${ARCH}.tar.gz" "authelia-${OS}-${ARCH}" authelia.service config.template.yml
sha256sum "authelia-${OS}-${ARCH}.tar.gz" > "authelia-${OS}-${ARCH}.tar.gz.sha256"
fi
# Saving image for push to docker hub
docker save "${DOCKER_IMAGE}" | zstdmt -T0 -12 > "authelia-image-${ARCH}.tar.zst"
fi fi
if [[ "${BUILDKITE_LABEL}" =~ ":debian: Build Package" ]]; then if [[ "${BUILDKITE_LABEL}" =~ ":debian: Build Package" ]]; then

View File

@ -14,12 +14,6 @@ if [[ "${BUILDKITE_LABEL}" =~ ":selenium:" ]]; then
docker tag authelia/authelia authelia:dist docker tag authelia/authelia authelia:dist
fi fi
if [[ "${BUILDKITE_LABEL}" =~ ":docker: Build Image" ]] && [[ "${ARCH}" != "coverage" ]]; then
echo "--- :react: :swagger: Extract frontend assets"
buildkite-agent artifact download "authelia-public_html.tar.gz" .
tar xzf authelia-public_html.tar.gz
fi
if [[ "${BUILDKITE_LABEL}" =~ ":debian: Build Package" ]]; then if [[ "${BUILDKITE_LABEL}" =~ ":debian: Build Package" ]]; then
buildkite-agent artifact download "authelia-linux-${ARCH}.tar.gz" . buildkite-agent artifact download "authelia-linux-${ARCH}.tar.gz" .
fi fi
@ -29,6 +23,12 @@ if [[ "${BUILDKITE_LABEL}" =~ ":docker: Deploy Image" ]]; then
zstdcat "authelia-image-${ARCH}.tar.zst" | docker load zstdcat "authelia-image-${ARCH}.tar.zst" | docker load
fi fi
if [[ "${BUILDKITE_LABEL}" == ":docker: Deploy Manifest" ]]; then
echo "--- :go: :react: :swagger: Extract pre-built binary"
buildkite-agent artifact download "authelia-linux-*.tar.gz" .
for archive in authelia-linux-*.tar.gz; do tar xzf ${archive} --wildcards "authelia-linux-*"; done
fi
if [[ "${BUILDKITE_LABEL}" == ":github: Deploy Artifacts" ]]; then if [[ "${BUILDKITE_LABEL}" == ":github: Deploy Artifacts" ]]; then
buildkite-agent artifact download "authelia-*.tar.gz*" . buildkite-agent artifact download "authelia-*.tar.gz*" .
buildkite-agent artifact download "authelia_*.deb*" . buildkite-agent artifact download "authelia_*.deb*" .

View File

@ -37,35 +37,29 @@ steps:
agents: agents:
build: "unit-test" build: "unit-test"
artifact_paths: artifact_paths:
- "authelia-public_html.tar.gz" - "authelia-*.tar.gz"
- "authelia-public_html.tar.gz.sha256" - "authelia-*.tar.gz.sha256"
key: "unit-test" key: "unit-test"
if: build.env("CI_BYPASS") != "true" if: build.env("CI_BYPASS") != "true"
- wait: - wait:
if: build.env("CI_BYPASS") != "true" if: build.env("CI_BYPASS") != "true"
- label: ":docker: Image Builds" - label: ":docker: Build Image [coverage]"
command: ".buildkite/steps/buildimages.sh | buildkite-agent pipeline upload" command: "authelia-scripts docker build --container=coverage"
concurrency: 3 agents:
concurrency_group: "builds" build: "linux-coverage"
artifact_paths:
- "authelia-image-coverage.tar.zst"
depends_on: ~ depends_on: ~
if: build.env("CI_BYPASS") != "true" key: "build-docker-linux-coverage"
if: build.env("CI_BYPASS") != "true" && build.branch !~ /^(v[0-9]+\.[0-9]+\.[0-9]+)$\$/ && build.message !~ /\[(skip test|test skip)\]/
- label: ":debian: Package Builds" - label: ":debian: Package Builds"
command: ".buildkite/steps/debpackages.sh | buildkite-agent pipeline upload" command: ".buildkite/steps/debpackages.sh | buildkite-agent pipeline upload"
depends_on: ~ depends_on: ~
if: build.branch !~ /^(dependabot|renovate)\/.*/ && build.env("CI_BYPASS") != "true" if: build.branch !~ /^(dependabot|renovate)\/.*/ && build.env("CI_BYPASS") != "true"
- wait:
if: build.env("CI_BYPASS") != "true"
- label: ":vertical_traffic_light: Build Concurrency Gate"
command: "echo End of concurrency gate"
concurrency: 3
concurrency_group: "builds"
if: build.env("CI_BYPASS") != "true"
- wait: - wait:
if: build.branch !~ /^(v[0-9]+\.[0-9]+\.[0-9]+)$\$/ && build.env("CI_BYPASS") != "true" && build.message !~ /\[(skip test|test skip)\]/ if: build.branch !~ /^(v[0-9]+\.[0-9]+\.[0-9]+)$\$/ && build.env("CI_BYPASS") != "true" && build.message !~ /\[(skip test|test skip)\]/

View File

@ -1,43 +0,0 @@
#!/usr/bin/env bash
set -eu
declare -A BUILDS=(["linux"]="amd64 arm32v7 arm64v8 coverage")
for BUILD_OS in "${!BUILDS[@]}"; do
for BUILD_ARCH in ${BUILDS[$BUILD_OS]}; do
cat << EOF
- label: ":docker: Build Image [${BUILD_ARCH}]"
command: "authelia-scripts docker build --arch=${BUILD_ARCH}"
retry:
manual:
permit_on_passed: true
agents:
build: "${BUILD_OS}-${BUILD_ARCH}"
artifact_paths:
- "authelia-image-${BUILD_ARCH}.tar.zst"
EOF
if [[ "${BUILD_ARCH}" != "coverage" ]]; then
cat << EOF
- "authelia-${BUILD_OS}-${BUILD_ARCH}.tar.gz"
- "authelia-${BUILD_OS}-${BUILD_ARCH}.tar.gz.sha256"
depends_on:
- "unit-test"
EOF
fi
cat << EOF
env:
ARCH: "${BUILD_ARCH}"
OS: "${BUILD_OS}"
key: "build-docker-${BUILD_OS}-${BUILD_ARCH}"
EOF
if [[ "${BUILD_ARCH}" == "coverage" ]]; then
cat << EOF
if: build.branch !~ /^(v[0-9]+\.[0-9]+\.[0-9]+)$\$/ && build.message !~ /\[(skip test|test skip)\]/
EOF
else
cat << EOF
if: build.branch !~ /^(dependabot|renovate)\/.*/
EOF
fi
done
done

View File

@ -11,8 +11,8 @@ fi
wget https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=authelia-bin -qO PKGBUILD && \ wget https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=authelia-bin -qO PKGBUILD && \
sed -i -e '/^pkgname=/c pkgname=authelia' -e "/pkgver=/c $VERSION" -e '10,14d' \ sed -i -e '/^pkgname=/c pkgname=authelia' -e "/pkgver=/c $VERSION" -e '10,14d' \
-e 's/source_x86_64.*/source_x86_64=("authelia-linux-amd64.tar.gz")/' \ -e 's/source_x86_64.*/source_x86_64=("authelia-linux-amd64.tar.gz")/' \
-e 's/source_aarch64.*/source_aarch64=("authelia-linux-arm64v8.tar.gz")/' \ -e 's/source_aarch64.*/source_aarch64=("authelia-linux-arm64.tar.gz")/' \
-e 's/source_armv7h.*/source_armv7h=("authelia-linux-arm32v7.tar.gz")/' \ -e 's/source_armv7h.*/source_armv7h=("authelia-linux-arm.tar.gz")/' \
-e 's/sha256sums_x86_64.*/sha256sums_x86_64=("SKIP")/' \ -e 's/sha256sums_x86_64.*/sha256sums_x86_64=("SKIP")/' \
-e 's/sha256sums_aarch64.*/sha256sums_aarch64=("SKIP")/' \ -e 's/sha256sums_aarch64.*/sha256sums_aarch64=("SKIP")/' \
-e 's/sha256sums_armv7h.*/sha256sums_armv7h=("SKIP")/' PKGBUILD -e 's/sha256sums_armv7h.*/sha256sums_armv7h=("SKIP")/' PKGBUILD

View File

@ -14,23 +14,19 @@ EOF
if [[ "${DEB_PACKAGE}" == "amd64" ]]; then if [[ "${DEB_PACKAGE}" == "amd64" ]]; then
cat << EOF cat << EOF
ARCH: "${DEB_PACKAGE}" ARCH: "${DEB_PACKAGE}"
depends_on:
- "build-docker-linux-amd64"
EOF EOF
elif [[ "${DEB_PACKAGE}" == "armhf" ]]; then elif [[ "${DEB_PACKAGE}" == "armhf" ]]; then
cat << EOF cat << EOF
ARCH: "arm32v7" ARCH: "arm"
depends_on:
- "build-docker-linux-arm32v7"
EOF EOF
else else
cat << EOF cat << EOF
ARCH: "arm64v8" ARCH: "arm64"
depends_on:
- "build-docker-linux-arm64v8"
EOF EOF
fi fi
cat << EOF cat << EOF
depends_on:
- "unit-test"
key: "build-deb-package-${DEB_PACKAGE}" key: "build-deb-package-${DEB_PACKAGE}"
EOF EOF
done done

View File

@ -1,32 +0,0 @@
#!/usr/bin/env bash
set -eu
for BUILD_ARCH in amd64 arm32v7 arm64v8; do
cat << EOF
- label: ":docker: Deploy Image [${BUILD_ARCH}]"
command: "authelia-scripts docker push-image --arch=${BUILD_ARCH}"
retry:
manual:
permit_on_passed: true
depends_on:
EOF
if [[ "${BUILD_ARCH}" == "amd64" ]]; then
cat << EOF
- "build-docker-linux-amd64"
EOF
elif [[ "${BUILD_ARCH}" == "arm32v7" ]]; then
cat << EOF
- "build-docker-linux-arm32v7"
EOF
else
cat << EOF
- "build-docker-linux-arm64v8"
EOF
fi
cat << EOF
agents:
upload: "fast"
env:
ARCH: "${BUILD_ARCH}"
EOF
done

View File

@ -5,8 +5,9 @@ artifacts=()
for FILE in \ for FILE in \
authelia-linux-amd64.tar.gz authelia-linux-amd64.tar.gz.sha256 \ authelia-linux-amd64.tar.gz authelia-linux-amd64.tar.gz.sha256 \
authelia-linux-arm32v7.tar.gz authelia-linux-arm32v7.tar.gz.sha256 \ authelia-linux-arm.tar.gz authelia-linux-arm.tar.gz.sha256 \
authelia-linux-arm64v8.tar.gz authelia-linux-arm64v8.tar.gz.sha256 \ authelia-linux-arm64.tar.gz authelia-linux-arm64.tar.gz.sha256 \
authelia-freebsd-amd64.tar.gz authelia-freebsd-amd64.tar.gz.sha256 \
authelia-public_html.tar.gz authelia-public_html.tar.gz.sha256; authelia-public_html.tar.gz authelia-public_html.tar.gz.sha256;
do do
# Add the version to the artifact name # Add the version to the artifact name

View File

@ -1,26 +1,9 @@
# CI/Git Directories # Ignore All
.git *
.cache
.buildkite
.github
coverage.txt
Dockerfile*
# Node Modules Directories
**/node_modules
# Documentation
docs
examples
# Dot Files and Markdown
.*
*.md
# Other
internal/server/public_html
authelia.service
bootstrap.sh
# Overrides # Overrides
!authelia-linux-*
!LICENSE
!entrypoint.sh
!healthcheck.sh
!.healthcheck.env !.healthcheck.env

View File

@ -1,47 +1,32 @@
# =======================================
# ===== Build image for the backend =====
# =======================================
FROM golang:1.17.1-alpine AS builder-backend
WORKDIR /go/src/app
COPY go.mod go.sum ./
RUN \
echo ">> Downloading go modules..." && \
go mod download
COPY / ./
ARG LDFLAGS_EXTRA
RUN \
mv public_html internal/server/public_html && \
chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -tags netgo \
-ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia
# =================================== # ===================================
# ===== Authelia official image ===== # ===== Authelia official image =====
# =================================== # ===================================
FROM alpine:3.14.2 FROM alpine:3.14.2
ARG TARGETOS
ARG TARGETARCH
WORKDIR /app WORKDIR /app
RUN apk --no-cache add ca-certificates su-exec tzdata
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
EXPOSE 9091
VOLUME /config
# Set environment variables # Set environment variables
ENV PATH="/app:${PATH}" \ ENV PATH="/app:${PATH}" \
PUID=0 \ PUID=0 \
PGID=0 PGID=0
RUN \
apk --no-cache add ca-certificates su-exec tzdata
COPY LICENSE .healthcheck.env entrypoint.sh healthcheck.sh ./
RUN \
chmod 0666 /app/.healthcheck.env
COPY authelia-${TARGETOS}-${TARGETARCH} ./authelia
EXPOSE 9091
VOLUME /config
ENTRYPOINT ["/app/entrypoint.sh"] ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["--config", "/config/configuration.yml"] CMD ["--config", "/config/configuration.yml"]
HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh

View File

@ -1,48 +0,0 @@
# =======================================
# ===== Build image for the backend =====
# =======================================
FROM golang:1.17.1-alpine AS builder-backend
WORKDIR /go/src/app
COPY go.mod go.sum ./
RUN \
echo ">> Downloading go modules..." && \
go mod download
COPY / ./
ARG LDFLAGS_EXTRA
RUN \
mv public_html internal/server/public_html && \
chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -tags netgo \
-ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia
# ===================================
# ===== Authelia official image =====
# ===================================
FROM arm64v8/alpine:3.14.2
WORKDIR /app
RUN \
apk --no-cache add ca-certificates su-exec tzdata
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
EXPOSE 9091
VOLUME /config
# Set environment variables
ENV PATH="/app:${PATH}" \
PUID=0 \
PGID=0
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["--config", "/config/configuration.yml"]
HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh

View File

@ -0,0 +1,22 @@
# CI/Git Directories
coverage.txt
Dockerfile*
# Node Modules Directories
**/node_modules
# Documentation
docs
examples
# Dot Files and Markdown
.*
*.md
# Other
internal/server/public_html
authelia.service
bootstrap.sh
# Overrides
!.healthcheck.env

View File

@ -1,3 +1,15 @@
# ========================================
# ===== Build image for the frontend =====
# ========================================
FROM node:16-alpine AS builder-frontend
WORKDIR /node/src/app
COPY web ./
# Install the dependencies and build
RUN yarn install --frozen-lockfile && INLINE_RUNTIME_CHUNK=false yarn coverage
# ======================================= # =======================================
# ===== Build image for the backend ===== # ===== Build image for the backend =====
# ======================================= # =======================================
@ -13,36 +25,41 @@ go mod download
COPY / ./ COPY / ./
ARG LDFLAGS_EXTRA # Prepare static files to be embedded in Go binary
COPY --from=builder-frontend /node/src/internal/server/public_html internal/server/public_html
ARG LDFLAGS_EXTRA
RUN \ RUN \
mv public_html internal/server/public_html && \ mv api internal/server/public_html/api && \
chmod 0666 /go/src/app/.healthcheck.env && \ chmod 0666 /go/src/app/.healthcheck.env && \
echo ">> Starting go build..." && \ echo ">> Starting go build..." && \
GOOS=linux GOARCH=arm CGO_ENABLED=0 go build -tags netgo \ CGO_ENABLED=0 go build -tags netgo \
-ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia -ldflags "-s -w ${LDFLAGS_EXTRA}" -trimpath -o authelia ./cmd/authelia
# =================================== # ===================================
# ===== Authelia official image ===== # ===== Authelia official image =====
# =================================== # ===================================
FROM arm32v7/alpine:3.14.2 FROM alpine:3.14.2
WORKDIR /app WORKDIR /app
RUN \
apk --no-cache add ca-certificates su-exec tzdata
COPY --from=builder-backend /go/src/app/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
EXPOSE 9091
VOLUME /config
# Set environment variables # Set environment variables
ENV PATH="/app:${PATH}" \ ENV PATH="/app:${PATH}" \
PUID=0 \ PUID=0 \
PGID=0 PGID=0
RUN \
apk --no-cache add ca-certificates su-exec tzdata
COPY --from=builder-backend /go/src/app/cmd/authelia/authelia /go/src/app/LICENSE /go/src/app/entrypoint.sh /go/src/app/healthcheck.sh /go/src/app/.healthcheck.env ./
RUN \
chmod 0666 /app/.healthcheck.env
EXPOSE 9091
VOLUME /config
ENTRYPOINT ["/app/entrypoint.sh"] ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["--config", "/config/configuration.yml"] CMD ["--config", "/config/configuration.yml"]
HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh HEALTHCHECK --interval=30s --timeout=3s --start-period=1m CMD /app/healthcheck.sh

View File

@ -0,0 +1,22 @@
# CI/Git Directories
coverage.txt
Dockerfile*
# Node Modules Directories
**/node_modules
# Documentation
docs
examples
# Dot Files and Markdown
.*
*.md
# Other
internal/server/public_html
authelia.service
bootstrap.sh
# Overrides
!.healthcheck.env

View File

@ -10,12 +10,18 @@ import (
"github.com/authelia/authelia/v4/internal/utils" "github.com/authelia/authelia/v4/internal/utils"
) )
func buildAutheliaBinary(xflags []string) { func buildAutheliaBinary(xflags []string, buildkite bool) {
cmd := utils.CommandWithStdout("go", "build", "-o", "../../"+OutputDir+"/authelia", "-ldflags", strings.Join(xflags, " ")) cmd := utils.CommandWithStdout("go", "build", "-tags", "netgo", "-trimpath", "-o", OutputDir+"/authelia", "-ldflags", "-s -w "+strings.Join(xflags, " "), "./cmd/authelia/")
cmd.Dir = "cmd/authelia"
cmd.Env = append(os.Environ(), cmd.Env = append(os.Environ(),
"GOOS=linux", "GOARCH=amd64", "CGO_ENABLED=0") "CGO_ENABLED=0")
if buildkite {
cmd = utils.CommandWithStdout("gox", "-tags=netgo", "-output={{.Dir}}-{{.OS}}-{{.Arch}}", "-ldflags=-s -w "+strings.Join(xflags, " "), "-osarch=linux/amd64 linux/arm linux/arm64 freebsd/amd64", "./cmd/authelia/")
cmd.Env = append(os.Environ(),
"GOFLAGS=-trimpath", "CGO_ENABLED=0")
}
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
@ -98,7 +104,7 @@ func Build(cobraCmd *cobra.Command, args []string) {
Clean(cobraCmd, args) Clean(cobraCmd, args)
xflags, err := getXFlags("", "0", "") xflags, err := getXFlags(os.Getenv("BUILDKITE_BRANCH"), os.Getenv("BUILDKITE_BUILD_NUMBER"), "")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -118,11 +124,12 @@ func Build(cobraCmd *cobra.Command, args []string) {
buildkite, _ := cobraCmd.Flags().GetBool("buildkite") buildkite, _ := cobraCmd.Flags().GetBool("buildkite")
if buildkite { if buildkite {
log.Debug("Buildkite job detected, skipping Authelia Go binary build") log.Debug("Building Authelia Go binaries with gox...")
} else { } else {
log.Debug("Building Authelia Go binary...") log.Debug("Building Authelia Go binary...")
buildAutheliaBinary(xflags)
} }
buildAutheliaBinary(xflags, buildkite)
cleanAssets() cleanAssets()
} }

View File

@ -6,19 +6,15 @@ import (
"os" "os"
"regexp" "regexp"
"strings" "strings"
"time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/authelia/authelia/v4/internal/utils"
) )
var arch string var container string
var supportedArch = []string{"amd64", "arm32v7", "arm64v8", "coverage"} var containers = []string{"dev", "coverage"}
var defaultArch = "amd64" var defaultContainer = "dev"
var buildkiteQEMU = os.Getenv("BUILDKITE_AGENT_META_DATA_QEMU")
var ciBranch = os.Getenv("BUILDKITE_BRANCH") var ciBranch = os.Getenv("BUILDKITE_BRANCH")
var ciPullRequest = os.Getenv("BUILDKITE_PULL_REQUEST") var ciPullRequest = os.Getenv("BUILDKITE_PULL_REQUEST")
var ciTag = os.Getenv("BUILDKITE_TAG") var ciTag = os.Getenv("BUILDKITE_TAG")
@ -28,38 +24,23 @@ var publicRepo = regexp.MustCompile(`.*:.*`)
var tags = dockerTags.FindStringSubmatch(ciTag) var tags = dockerTags.FindStringSubmatch(ciTag)
func init() { func init() {
DockerBuildCmd.PersistentFlags().StringVar(&arch, "arch", defaultArch, "target architecture among: "+strings.Join(supportedArch, ", ")) DockerBuildCmd.PersistentFlags().StringVar(&container, "container", defaultContainer, "target container among: "+strings.Join(containers, ", "))
DockerPushCmd.PersistentFlags().StringVar(&arch, "arch", defaultArch, "target architecture among: "+strings.Join(supportedArch, ", "))
} }
func checkArchIsSupported(arch string) { func checkContainerIsSupported(container string) {
for _, a := range supportedArch { for _, v := range containers {
if arch == a { if container == v {
return return
} }
} }
log.Fatal("Architecture is not supported. Please select one of " + strings.Join(supportedArch, ", ") + ".") log.Fatal("Container is not supported. Please select one of " + strings.Join(containers, ", ") + ".")
} }
func dockerBuildOfficialImage(arch string) error { func dockerBuildOfficialImage(arch string) error {
docker := &Docker{} docker := &Docker{}
// Set default Architecture Dockerfile to amd64. filename := "Dockerfile"
dockerfile := "Dockerfile" dockerfile := fmt.Sprintf("%s.%s", filename, arch)
// If not the default value.
if arch != defaultArch {
dockerfile = fmt.Sprintf("%s.%s", dockerfile, arch)
}
if arch == "arm32v7" || arch == "arm64v8" {
if buildkiteQEMU != stringTrue {
err := utils.CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run()
if err != nil {
log.Fatal(err)
}
}
}
flags, err := getXFlags(ciBranch, os.Getenv("BUILDKITE_BUILD_NUMBER"), "") flags, err := getXFlags(ciBranch, os.Getenv("BUILDKITE_BUILD_NUMBER"), "")
if err != nil { if err != nil {
@ -76,8 +57,8 @@ var DockerBuildCmd = &cobra.Command{
Short: "Build the docker image of Authelia", Short: "Build the docker image of Authelia",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
log.Infof("Building Docker image %s...", DockerImageName) log.Infof("Building Docker image %s...", DockerImageName)
checkArchIsSupported(arch) checkContainerIsSupported(container)
err := dockerBuildOfficialImage(arch) err := dockerBuildOfficialImage(container)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -92,23 +73,11 @@ var DockerBuildCmd = &cobra.Command{
}, },
} }
// DockerPushCmd Command for pushing Authelia docker image to DockerHub.
var DockerPushCmd = &cobra.Command{
Use: "push-image",
Short: "Publish Authelia docker image to Docker Hub",
Run: func(cmd *cobra.Command, args []string) {
log.Infof("Pushing Docker image %s to Docker Hub...", DockerImageName)
checkArchIsSupported(arch)
publishDockerImage(arch)
},
}
// DockerManifestCmd Command for pushing Authelia docker manifest to DockerHub. // DockerManifestCmd Command for pushing Authelia docker manifest to DockerHub.
var DockerManifestCmd = &cobra.Command{ var DockerManifestCmd = &cobra.Command{
Use: "push-manifest", Use: "push-manifest",
Short: "Publish Authelia docker manifest to Docker Hub", Short: "Publish Authelia docker manifest to Docker Hub",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
log.Infof("Pushing Docker manifest of %s to Docker Hub...", DockerImageName)
publishDockerManifest() publishDockerManifest()
}, },
} }
@ -142,124 +111,53 @@ func login(docker *Docker, registry string) {
} }
} }
func deploy(docker *Docker, tag, registry string) { func deployManifest(docker *Docker, tag string) {
imageWithTag := registry + "/" + DockerImageName + ":" + tag log.Infof("Docker manifest %s:%s will be deployed on %s and %s", DockerImageName, tag, dockerhub, ghcr)
log.Infof("Docker image %s will be deployed on %s", imageWithTag, registry) dockerhub := dockerhub + "/" + DockerImageName + ":" + tag
ghcr := ghcr + "/" + DockerImageName + ":" + tag
if err := docker.Tag(DockerImageName, imageWithTag); err != nil { if err := docker.Manifest(dockerhub, ghcr); err != nil {
log.Fatal(err) log.Fatal(err)
} }
if err := docker.Push(imageWithTag); err != nil {
log.Fatal(err)
}
}
func deployManifest(docker *Docker, tag, amd64tag, arm32v7tag, arm64v8tag, registry string) {
dockerImagePrefix := registry + "/" + DockerImageName + ":"
log.Infof("Docker manifest %s%s will be deployed on %s", dockerImagePrefix, tag, registry)
err := docker.Manifest(dockerImagePrefix+tag, dockerImagePrefix+amd64tag, dockerImagePrefix+arm32v7tag, dockerImagePrefix+arm64v8tag)
if err != nil {
log.Fatal(err)
}
tags := []string{amd64tag, arm32v7tag, arm64v8tag}
if registry == dockerhub {
for _, t := range tags {
log.Infof("Docker removing tag for %s%s on Docker Hub", dockerImagePrefix, t)
if err := utils.RunFuncWithRetry(3, 10*time.Second, func() (err error) {
err = docker.CleanTag(t)
return
}); err != nil {
log.Fatal(err)
}
}
}
}
func publishDockerImage(arch string) {
docker := &Docker{}
for _, registry := range registries {
switch {
case ciTag != "":
if len(tags) == 4 {
log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
login(docker, registry)
deploy(docker, tags[1]+"-"+arch, registry)
if !ignoredSuffixes.MatchString(ciTag) {
deploy(docker, tags[2]+"-"+arch, registry)
deploy(docker, tags[3]+"-"+arch, registry)
deploy(docker, "latest-"+arch, registry)
}
} else {
log.Fatal("Docker image will not be published, the specified tag does not conform to the standard")
}
case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
login(docker, registry)
deploy(docker, ciBranch+"-"+arch, registry)
case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
login(docker, registry)
deploy(docker, "PR"+ciPullRequest+"-"+arch, registry)
case ciBranch == masterTag && ciPullRequest == stringFalse:
login(docker, registry)
deploy(docker, "master-"+arch, registry)
default:
log.Info("Docker image will not be published")
}
}
} }
func publishDockerManifest() { func publishDockerManifest() {
docker := &Docker{} docker := &Docker{}
for _, registry := range registries { switch {
switch { case ciTag != "":
case ciTag != "": if len(tags) == 4 {
if len(tags) == 4 { log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3]) login(docker, dockerhub)
login(docker, registry) login(docker, ghcr)
deployManifest(docker, tags[1], tags[1]+"-amd64", tags[1]+"-arm32v7", tags[1]+"-arm64v8", registry) deployManifest(docker, tags[1])
publishDockerReadme(docker)
if registry == dockerhub { if !ignoredSuffixes.MatchString(ciTag) {
publishDockerReadme(docker) deployManifest(docker, tags[2])
} deployManifest(docker, tags[3])
deployManifest(docker, "latest")
if !ignoredSuffixes.MatchString(ciTag) {
deployManifest(docker, tags[2], tags[2]+"-amd64", tags[2]+"-arm32v7", tags[2]+"-arm64v8", registry)
deployManifest(docker, tags[3], tags[3]+"-amd64", tags[3]+"-arm32v7", tags[3]+"-arm64v8", registry)
deployManifest(docker, "latest", "latest-amd64", "latest-arm32v7", "latest-arm64v8", registry)
if registry == dockerhub {
publishDockerReadme(docker)
}
}
} else {
log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard")
}
case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
login(docker, registry)
deployManifest(docker, ciBranch, ciBranch+"-amd64", ciBranch+"-arm32v7", ciBranch+"-arm64v8", registry)
case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
login(docker, registry)
deployManifest(docker, "PR"+ciPullRequest, "PR"+ciPullRequest+"-amd64", "PR"+ciPullRequest+"-arm32v7", "PR"+ciPullRequest+"-arm64v8", registry)
case ciBranch == masterTag && ciPullRequest == stringFalse:
login(docker, registry)
deployManifest(docker, "master", "master-amd64", "master-arm32v7", "master-arm64v8", registry)
if registry == dockerhub {
publishDockerReadme(docker) publishDockerReadme(docker)
} }
default: } else {
log.Info("Docker manifest will not be published") log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard")
} }
case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, ciBranch)
case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, "PR"+ciPullRequest)
case ciBranch == masterTag && ciPullRequest == stringFalse:
login(docker, dockerhub)
login(docker, ghcr)
deployManifest(docker, "master")
publishDockerReadme(docker)
default:
log.Info("Docker manifest will not be published")
} }
} }

View File

@ -9,14 +9,11 @@ var DockerImageName = "authelia/authelia"
// IntermediateDockerImageName local name of the docker image. // IntermediateDockerImageName local name of the docker image.
var IntermediateDockerImageName = "authelia:dist" var IntermediateDockerImageName = "authelia:dist"
var registries = []string{"docker.io", "ghcr.io"}
const dockerhub = "docker.io" const dockerhub = "docker.io"
const ghcr = "ghcr.io" const ghcr = "ghcr.io"
const masterTag = "master" const masterTag = "master"
const stringFalse = "false" const stringFalse = "false"
const stringTrue = "true"
const webDirectory = "web" const webDirectory = "web"
const fmtLDFLAGSX = "-X 'github.com/authelia/authelia/v4/internal/utils.%s=%s'" const fmtLDFLAGSX = "-X 'github.com/authelia/authelia/v4/internal/utils.%s=%s'"

View File

@ -22,40 +22,12 @@ func (d *Docker) Tag(image, tag string) error {
// Login login to the dockerhub registry. // Login login to the dockerhub registry.
func (d *Docker) Login(username, password, registry string) error { func (d *Docker) Login(username, password, registry string) error {
return utils.CommandWithStdout("docker", "login", registry, "-u", username, "-p", password).Run() return utils.CommandWithStdout("bash", "-c", `echo `+password+` | docker login `+registry+` --password-stdin -u `+username).Run()
}
// Push push a docker image to dockerhub.
func (d *Docker) Push(tag string) error {
return utils.CommandWithStdout("docker", "push", tag).Run()
} }
// Manifest push a docker manifest to dockerhub. // Manifest push a docker manifest to dockerhub.
func (d *Docker) Manifest(tag, amd64tag, arm32v7tag, arm64v8tag string) error { func (d *Docker) Manifest(tag1, tag2 string) error {
err := utils.CommandWithStdout("docker", "manifest", "create", tag, amd64tag, arm32v7tag, arm64v8tag).Run() return utils.CommandWithStdout("docker", "build", "-t", tag1, "-t", tag2, "--platform", "linux/amd64,linux/arm/v7,linux/arm64", "--builder", "buildx", "--push", ".").Run()
if err != nil {
panic(err)
}
err = utils.CommandWithStdout("docker", "manifest", "annotate", tag, arm32v7tag, "--os", "linux", "--arch", "arm").Run()
if err != nil {
panic(err)
}
err = utils.CommandWithStdout("docker", "manifest", "annotate", tag, arm64v8tag, "--os", "linux", "--arch", "arm64", "--variant", "v8").Run()
if err != nil {
panic(err)
}
return utils.CommandWithStdout("docker", "manifest", "push", "--purge", tag).Run()
}
// CleanTag remove a tag from dockerhub.
func (d *Docker) CleanTag(tag string) error {
return utils.CommandWithStdout("bash", "-c", `token=$(curl -fs --retry 3 -H "Content-Type: application/json" -X "POST" -d '{"username": "'$DOCKER_USERNAME'", "password": "'$DOCKER_PASSWORD'"}' https://hub.docker.com/v2/users/login/ | jq -r .token) && curl -fs --retry 3 -o /dev/null -L -X "DELETE" -H "Authorization: JWT $token" https://hub.docker.com/v2/repositories/`+DockerImageName+"/tags/"+tag+"/").Run()
} }
// PublishReadme push README.md to dockerhub. // PublishReadme push README.md to dockerhub.

View File

@ -50,7 +50,7 @@ var Commands = []AutheliaCommandDefinition{
{ {
Name: "docker", Name: "docker",
Short: "Commands related to building and publishing docker image", Short: "Commands related to building and publishing docker image",
SubCommands: CobraCommands{DockerBuildCmd, DockerPushCmd, DockerManifestCmd}, SubCommands: CobraCommands{DockerBuildCmd, DockerManifestCmd},
}, },
{ {
Name: "serve [config]", Name: "serve [config]",