Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker multiarch image #703

Merged
merged 15 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
37 changes: 20 additions & 17 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,26 @@ jobs:
sudo chown -R $UID build
make package-zip
ls -lath build
make build-docker
docker image ls
echo "$DOCKER_PASS" | docker login --username "$DOCKER_USER" --password-stdin
echo "$DOCKER_GHCR_PASS" | docker login ghcr.io --username "$DOCKER_GHCR_USER" --password-stdin
docker push --all-tags gotify/server
docker push --all-tags gotify/server-arm7
docker push --all-tags gotify/server-arm64
docker push --all-tags gotify/server-riscv64
docker push --all-tags ghcr.io/gotify/server
docker push --all-tags ghcr.io/gotify/server-arm7
docker push --all-tags ghcr.io/gotify/server-arm64
docker push --all-tags ghcr.io/gotify/server-riscv64
env:
DOCKER_USER: ${{ secrets.DOCKER_USER }}
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
DOCKER_GHCR_USER: ${{ secrets.DOCKER_GHCR_USER }}
DOCKER_GHCR_PASS: ${{ secrets.DOCKER_GHCR_PASS }}
- if: startsWith(github.ref, 'refs/tags/v')
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- if: startsWith(github.ref, 'refs/tags/v')
name: Set up QEMU
uses: docker/setup-qemu-action@v2
- if: startsWith(github.ref, 'refs/tags/v')
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- if: startsWith(github.ref, 'refs/tags/v')
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ secrets.DOCKER_GHCR_USER }}
password: ${{ secrets.DOCKER_GHCR_PASS }}
- if: startsWith(github.ref, 'refs/tags/v')
run: |
make DOCKER_BUILD_PUSH=true build-docker
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
- if: startsWith(github.ref, 'refs/tags/v')
uses: svenstaro/upload-release-action@v2
with:
Expand Down
66 changes: 29 additions & 37 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ DOCKER_BUILD_IMAGE=gotify/build
DOCKER_WORKDIR=/proj
DOCKER_RUN=docker run --rm -v "$$PWD/.:${DOCKER_WORKDIR}" -v "`go env GOPATH`/pkg/mod/.:/go/pkg/mod:ro" -w ${DOCKER_WORKDIR}
DOCKER_GO_BUILD=go build -mod=readonly -a -installsuffix cgo -ldflags "$$LD_FLAGS"
DOCKER_TEST_LEVEL ?= 1 # should be unnecessary but good for a last-line of defense
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
NODE_OPTIONS=$(shell if node --help | grep -q -- "--openssl-legacy-provider"; then echo --openssl-legacy-provider; fi)

test: test-coverage test-js
Expand Down Expand Up @@ -63,63 +64,54 @@ package-zip: extract-licenses
zip -ur $$BUILD.zip ${LICENSE_DIR}; \
done

build-docker-amd64: require-version
cp ${BUILD_DIR}/gotify-linux-amd64 ./docker/gotify-app
cd ${DOCKER_DIR} && \
docker build \
build-docker-multiarch: require-version
docker buildx build --sbom=true --provenance=true \
$(if $(DOCKER_BUILD_PUSH),--push) \
-t gotify/server:latest \
-t gotify/server:${VERSION} \
-t gotify/server:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t gotify/server:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t ghcr.io/gotify/server:latest \
-t ghcr.io/gotify/server:latest \
-t ghcr.io/gotify/server:${VERSION} \
-t ghcr.io/gotify/server:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t ghcr.io/gotify/server:$(shell echo $(VERSION) | cut -d '.' -f -1) .
rm ${DOCKER_DIR}gotify-app

build-docker-arm-7: require-version
cp ${BUILD_DIR}/gotify-linux-arm-7 ./docker/gotify-app
cd ${DOCKER_DIR} && \
docker build -f Dockerfile.armv7 \
-t gotify/server-arm7:latest \
-t gotify/server-arm7:${VERSION} \
-t gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t ghcr.io/gotify/server-arm7:latest \
-t ghcr.io/gotify/server-arm7:${VERSION} \
-t ghcr.io/gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t ghcr.io/gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -1) .
rm ${DOCKER_DIR}gotify-app

build-docker-arm64: require-version
cp ${BUILD_DIR}/gotify-linux-arm64 ./docker/gotify-app
cd ${DOCKER_DIR} && \
docker build -f Dockerfile.arm64 \
-t ghcr.io/gotify/server:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t gotify/server-arm64:latest \
-t gotify/server-arm64:${VERSION} \
-t gotify/server-arm64:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t gotify/server-arm64:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t ghcr.io/gotify/server-arm64:latest \
-t ghcr.io/gotify/server-arm64:${VERSION} \
-t ghcr.io/gotify/server-arm64:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t ghcr.io/gotify/server-arm64:$(shell echo $(VERSION) | cut -d '.' -f -1) .
rm ${DOCKER_DIR}gotify-app

build-docker-riscv64: require-version
cp ${BUILD_DIR}/gotify-linux-riscv64 ./docker/gotify-app
cd ${DOCKER_DIR} && \
docker build -f Dockerfile.riscv64 \
-t ghcr.io/gotify/server-arm64:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t gotify/server-arm7:latest \
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
-t gotify/server-arm7:${VERSION} \
-t gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t ghcr.io/gotify/server-arm7:latest \
-t ghcr.io/gotify/server-arm7:${VERSION} \
-t ghcr.io/gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t ghcr.io/gotify/server-arm7:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t gotify/server-riscv64:latest \
-t gotify/server-riscv64:${VERSION} \
-t gotify/server-riscv64:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t gotify/server-riscv64:$(shell echo $(VERSION) | cut -d '.' -f -1) \
-t ghcr.io/gotify/server-riscv64:latest \
-t ghcr.io/gotify/server-riscv64:${VERSION} \
-t ghcr.io/gotify/server-riscv64:$(shell echo $(VERSION) | cut -d '.' -f -2) \
-t ghcr.io/gotify/server-riscv64:$(shell echo $(VERSION) | cut -d '.' -f -1) .
rm ${DOCKER_DIR}gotify-app

build-docker: build-docker-amd64 build-docker-arm-7 build-docker-arm64 build-docker-riscv64
-t ghcr.io/gotify/server-riscv64:$(shell echo $(VERSION) | cut -d '.' -f -1) \
--build-arg RUN_TESTS=$(DOCKER_TEST_LEVEL) \
--build-arg GO_VERSION=$(shell cat GO_VERSION) \
--platform linux/amd64,linux/arm64,linux/386,linux/arm/v7,linux/riscv64 \
-f docker/Dockerfile .

# Backwards compatibility
build-docker-amd64: build-docker-multiarch
build-docker-i386: build-docker-multiarch
build-docker-arm64: build-docker-multiarch
build-docker-arm-7: build-docker-multiarch
build-docker-riscv64: build-docker-multiarch
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved

build-docker: build-docker-multiarch

build-js:
(cd ui && NODE_OPTIONS="${NODE_OPTIONS}" yarn build)
Expand Down
110 changes: 101 additions & 9 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,104 @@
FROM amd64/debian:stable-slim
ENV GOTIFY_SERVER_PORT="80"
ARG BUILDKIT_SBOM_SCAN_CONTEXT=true
ARG DEBIAN_STABLE_ARCH=stable-slim
jmattheis marked this conversation as resolved.
Show resolved Hide resolved
ARG DEBIAN_EXPERIMENTAL_ARCH=sid-slim
# suppress warnung about invalid variable expansion
ARG GO_VERSION=PLEASE_PROVIDE_GO_VERSION

# Hack to normalize platform to match the chosed build image
# Get the gotify/build image tag
ARG __TARGETPLATFORM_DASHES=${TARGETPLATFORM/\//-}
ARG __TARGETPLATFORM_GO_NOTATION=${__TARGETPLATFORM_DASHES/arm\/v7/arm-7}
# Set the unstable tag if the target platform is not stable
ARG __TARGETPLATFORM_IS_STABLE=${TARGETPLATFORM/linux\/riscv64/}
ARG __DEBIAN_HAS_STABLE_TAG=${__TARGETPLATFORM_IS_STABLE/?*/${DEBIAN_STABLE_ARCH}} # if the last variable is not empty, set it to the debian stable tag
ARG DEBIAN=${__DEBIAN_HAS_STABLE_TAG:-${DEBIAN_EXPERIMENTAL_ARCH}} # else set it to the debian experimental tag

eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
# --- JS Builder ---

FROM --platform=${BUILDPLATFORM} debian:${DEBIAN} AS js-builder

eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
ARG BUILD_JS=0
ARG NODE_OPTIONS
ENV DEBIAN_FRONTEND=noninteractive
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved

COPY . /src/gotify

eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
RUN if [ "$BUILD_JS" = "1" ]; then \
apt-get update && apt-get install -yq --no-install-recommends \
curl \
git \
nodejs \
npm && \
\
NODE_OPTIONS_DEFAULT=$(if node --help | grep -q -- "--openssl-legacy-provider"; then echo --openssl-legacy-provider; fi) && \
export NODE_OPTIONS=${NODE_OPTIONS:-$NODE_OPTIONS_DEFAULT} && \
echo "Using NODE_OPTIONS=$NODE_OPTIONS" && \
cd /src/gotify/ui && \
\
npm install -g yarn && \
\
yarn install && \
yarn build && \
\
cp -r /src/gotify/ui/build /target; \
\
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
else \
mkdir -p /target; \
fi

# --- Go Builder ---

FROM --platform=${BUILDPLATFORM} gotify/build:${GO_VERSION}-${__TARGETPLATFORM_GO_NOTATION} AS builder

eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
ARG BUILDPLATFORM
ARG TARGETPLATFORM
ARG BUILD_JS=0
ARG RUN_TESTS=0 # 0=never, 1=native only
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
ARG GO_BUILD_FLAGS=-mod=readonly -a -installsuffix cgo
ENV DEBIAN_FRONTEND=noninteractive
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved

RUN apt-get update && apt-get install -yq --no-install-recommends \
ca-certificates \
git

COPY . /src/gotify
COPY --from=js-builder /target /ui-build

RUN if [ "$BUILD_JS" = "1" ]; then \
cp -r --update /ui-build /src/gotify/ui/build; \
fi

RUN cd /src/gotify && \
mkdir -p /target/app && \
if [ "$RUN_TESTS" = "1" ] && [ "$BUILDPLATFORM" = "$TARGETPLATFORM" ]; then \
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
go test -v ./...; \
fi && \
go build ${GO_BUILD_FLAGS} -o /target/app/gotify-app

eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
FROM debian:${DEBIAN}

# build-time configurables
ARG GOTIFY_SERVER_EXPOSE=80
ARG GOTIFY_UID=0
jmattheis marked this conversation as resolved.
Show resolved Hide resolved
ENV GOTIFY_SERVER_PORT=$GOTIFY_SERVER_EXPOSE
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved

WORKDIR /app
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -yq \
tzdata \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
ADD gotify-app /app/

RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && apt-get install -yq --no-install-recommends \
tzdata \
curl \
ca-certificates && \
rm -rf /var/lib/apt/lists/* && \
if [ "$GOTIFY_UID" != "0" ]; then \
useradd -u $GOTIFY_UID -r gotify && \
chown gotify:gotify /app; \
fi

USER $GOTIFY_UID

HEALTHCHECK --interval=30s --timeout=5s --start-period=5s CMD curl --fail http://localhost:$GOTIFY_SERVER_PORT/health || exit 1
EXPOSE 80
eternal-flame-AD marked this conversation as resolved.
Show resolved Hide resolved
EXPOSE $GOTIFY_SERVER_EXPOSE

COPY --from=builder /target /

ENTRYPOINT ["./gotify-app"]
5 changes: 0 additions & 5 deletions docker/Dockerfile.arm64

This file was deleted.

5 changes: 0 additions & 5 deletions docker/Dockerfile.armv7

This file was deleted.

5 changes: 0 additions & 5 deletions docker/Dockerfile.riscv64

This file was deleted.

8 changes: 5 additions & 3 deletions test/filepath_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ func TestWithWd(t *testing.T) {
})

assert.Nil(t, os.Mkdir(tmpDir.Path(), 0o644))
assert.Panics(t, func() {
WithWd(tmpDir.Path(), func(string) {})
})
if os.Getuid() != 0 { // root is not subject to this check
assert.Panics(t, func() {
WithWd(tmpDir.Path(), func(string) {})
})
}
assert.Nil(t, os.Remove(tmpDir.Path()))

assert.Nil(t, os.Mkdir(tmpDir.Path(), 0o755))
Expand Down
Loading