From 7d44c541a4da9c8cd6d842f891af8d5c0b76a704 Mon Sep 17 00:00:00 2001 From: Sung <8265228+sungwoncho@users.noreply.github.com> Date: Sun, 19 Oct 2025 14:30:55 -0700 Subject: [PATCH] Add Docker images for linux arm64, armv7, 386 (#697) * Add multi-platform Docker support for ARM64, ARMv7, and 386 * Support freebsd amd64 for server * Build docker images locally --- .github/workflows/release-server.yml | 12 ++++- Makefile | 56 ++++--------------- host/docker/Dockerfile | 28 ++++++++-- host/docker/build.sh | 80 ++++++++++++++++++++++++++-- host/docker/release.sh | 13 ----- scripts/cli/release-homebrew.sh | 53 ------------------ scripts/release.sh | 57 -------------------- scripts/server/build.sh | 3 ++ 8 files changed, 122 insertions(+), 180 deletions(-) delete mode 100755 host/docker/release.sh delete mode 100755 scripts/cli/release-homebrew.sh delete mode 100755 scripts/release.sh diff --git a/.github/workflows/release-server.yml b/.github/workflows/release-server.yml index f52e1508..a5f52933 100644 --- a/.github/workflows/release-server.yml +++ b/.github/workflows/release-server.yml @@ -55,10 +55,19 @@ jobs: ./scripts/generate-changelog.sh server "$TAG" "$PREV_TAG" > /tmp/changelog.txt cat /tmp/changelog.txt + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Prepare Docker build context run: | VERSION="${{ steps.version.outputs.version }}" cp build/server/dnote_server_${VERSION}_linux_amd64.tar.gz host/docker/ + cp build/server/dnote_server_${VERSION}_linux_arm64.tar.gz host/docker/ + cp build/server/dnote_server_${VERSION}_linux_arm.tar.gz host/docker/ + cp build/server/dnote_server_${VERSION}_linux_386.tar.gz host/docker/ - name: Login to Docker Hub uses: docker/login-action@v3 @@ -71,11 +80,12 @@ jobs: with: context: ./host/docker push: true + platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386 tags: | dnote/dnote:${{ steps.version.outputs.version }} dnote/dnote:latest build-args: | - tarballName=dnote_server_${{ steps.version.outputs.version }}_linux_amd64.tar.gz + version=${{ steps.version.outputs.version }} - name: Create GitHub release env: diff --git a/Makefile b/Makefile index 12cf32cf..5037c408 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,15 @@ endif @${currentDir}/scripts/server/build.sh $(version) .PHONY: build-server +build-server-docker: build-server +ifndef version + $(error version is required. Usage: make version=0.1.0 [platform=linux/amd64] build-server-docker) +endif + + @echo "==> building Docker image" + @(cd ${currentDir}/host/docker && ./build.sh $(version) $(platform)) +.PHONY: build-server-docker + build-cli: ifeq ($(debug), true) @echo "==> building cli in dev mode" @@ -82,53 +91,6 @@ endif endif .PHONY: build-cli -## release -release-cli: clean build-cli -ifndef version - $(error version is required. Usage: make version=0.1.0 release-cli) -endif -ifndef GH - $(error please install github-cli) -endif - - @echo "==> releasing cli" - @${currentDir}/scripts/release.sh cli $(version) ${cliOutputDir} -.PHONY: release-cli - -release-cli-homebrew: -ifndef version - $(error version is required. Usage: make version=0.1.0 release-cli-homebrew) -endif - - @echo "==> releasing cli on Homebrew" - @${currentDir}/scripts/cli/release-homebrew.sh $(version) -.PHONY: release-cli - -release-server: -ifndef version - $(error version is required. Usage: make version=0.1.0 release-server) -endif -ifndef GH - $(error please install github-cli) -endif - - @echo "==> releasing server" - @${currentDir}/scripts/release.sh server $(version) ${serverOutputDir} - - @echo "==> building and releasing docker image" - @(cd ${currentDir}/host/docker && ./build.sh $(version)) - @(cd ${currentDir}/host/docker && ./release.sh $(version)) -.PHONY: release-server - -# migrations -create-migration: -ifndef filename - $(error filename is required. Usage: make filename=your-filename create-migration) -endif - - @(cd ${currentDir}/pkg/server/database && ./scripts/create-migration.sh $(filename)) -.PHONY: create-migration - clean: @git clean -f @rm -rf build diff --git a/host/docker/Dockerfile b/host/docker/Dockerfile index 88681591..e67da318 100644 --- a/host/docker/Dockerfile +++ b/host/docker/Dockerfile @@ -1,12 +1,30 @@ FROM busybox:glibc -ARG tarballName -RUN test -n "$tarballName" +ARG TARGETPLATFORM +ARG version -WORKDIR dnote +RUN test -n "$TARGETPLATFORM" || (echo "TARGETPLATFORM is required" && exit 1) +RUN test -n "$version" || (echo "version is required" && exit 1) -COPY "$tarballName" . -RUN tar -xvzf "$tarballName" +WORKDIR /tmp/tarballs + +# Copy all architecture tarballs +COPY dnote_server_*.tar.gz ./ + +# Select and extract the correct tarball based on target platform +RUN case "$TARGETPLATFORM" in \ + "linux/amd64") ARCH="amd64" ;; \ + "linux/arm64") ARCH="arm64" ;; \ + "linux/arm/v7") ARCH="arm" ;; \ + "linux/386") ARCH="386" ;; \ + *) echo "Unsupported platform: $TARGETPLATFORM" && exit 1 ;; \ + esac && \ + TARBALL="dnote_server_${version}_linux_${ARCH}.tar.gz" && \ + echo "Extracting $TARBALL for $TARGETPLATFORM" && \ + mkdir -p /dnote && \ + tar -xvzf "$TARBALL" -C /dnote + +WORKDIR /dnote COPY entrypoint.sh . ENTRYPOINT ["./entrypoint.sh"] diff --git a/host/docker/build.sh b/host/docker/build.sh index 3ea9c143..add55578 100755 --- a/host/docker/build.sh +++ b/host/docker/build.sh @@ -1,13 +1,85 @@ #!/usr/bin/env bash +# Build Docker image for local testing +# +# Usage: +# # builds for host platform (auto-detected) +# ./build.sh 1.0.0 +# +# # builds arm64 +# ./build.sh 1.0.0 linux/arm64 +# +# # builds multiple platforms +# ./build.sh 1.0.0 "linux/amd64,linux/arm64,linux/arm/v7,linux/386" set -eux version=$1 +# Detect host platform if not specified +if [ -z "${2:-}" ]; then + HOST_ARCH=$(uname -m) + case "$HOST_ARCH" in + x86_64) platform="linux/amd64" ;; + aarch64|arm64) platform="linux/arm64" ;; + armv7l) platform="linux/arm/v7" ;; + i386|i686) platform="linux/386" ;; + *) + echo "Warning: Unsupported architecture: $HOST_ARCH, defaulting to linux/amd64" + platform="linux/amd64" + ;; + esac + echo "Auto-detected platform: $platform" +else + platform=$2 +fi + dir=$(dirname "${BASH_SOURCE[0]}") projectDir="$dir/../.." -tarballName="dnote_server_${version}_linux_amd64.tar.gz" -# copy over the build artifact to the Docker build context -cp "$projectDir/build/server/$tarballName" "$dir" +# Copy all Linux tarballs to Docker build context +cp "$projectDir/build/server/dnote_server_${version}_linux_amd64.tar.gz" "$dir/" +cp "$projectDir/build/server/dnote_server_${version}_linux_arm64.tar.gz" "$dir/" +cp "$projectDir/build/server/dnote_server_${version}_linux_arm.tar.gz" "$dir/" +cp "$projectDir/build/server/dnote_server_${version}_linux_386.tar.gz" "$dir/" -docker build --network=host -t dnote/dnote:"$version" --build-arg tarballName="$tarballName" . +# Count platforms (check for comma) +if [[ "$platform" == *","* ]]; then + echo "Building for multiple platforms: $platform" + + # Check if multiarch builder exists, create if not + if ! docker buildx ls | grep -q "multiarch"; then + echo "Creating multiarch builder for multi-platform builds..." + docker buildx create --name multiarch --use + docker buildx inspect --bootstrap + else + echo "Using existing multiarch builder" + docker buildx use multiarch + fi + echo "" + + docker buildx build \ + --platform "$platform" \ + -t dnote/dnote:"$version" \ + -t dnote/dnote:latest \ + --build-arg version="$version" \ + "$dir" + + # Switch back to default builder + docker buildx use default +else + echo "Building for single platform: $platform" + echo "Image will be loaded to local Docker daemon" + + docker buildx build \ + --platform "$platform" \ + -t dnote/dnote:"$version" \ + -t dnote/dnote:latest \ + --build-arg version="$version" \ + --load \ + "$dir" +fi + +echo "" +echo "Build complete!" +if [[ "$platform" != *","* ]]; then + echo "Test with: docker run --rm dnote/dnote:$version ./dnote-server version" +fi diff --git a/host/docker/release.sh b/host/docker/release.sh deleted file mode 100755 index 4c2be4af..00000000 --- a/host/docker/release.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -set -eux - -version=$1 - -docker login - -# tag the release -docker tag dnote/dnote:"$version" dnote/dnote:latest - -# publish -docker push dnote/dnote:"$version" -docker push dnote/dnote:latest diff --git a/scripts/cli/release-homebrew.sh b/scripts/cli/release-homebrew.sh deleted file mode 100755 index f48f16dc..00000000 --- a/scripts/cli/release-homebrew.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -set -eux - -currentDir=$(dirname "${BASH_SOURCE[0]}") -cliHomebrewDir=${currentDir}/../../homebrew-dnote - -if [ ! -d "$cliHomebrewDir" ]; then - echo "homebrew-dnote not found locally. Cloning." - git clone git@github.com:dnote/homebrew-dnote.git "$cliHomebrewDir" -fi - -version=$1 - -echo "version: $version" - -# Download source tarball and calculate SHA256 -source_url="https://github.com/dnote/dnote/archive/refs/tags/cli-v${version}.tar.gz" -echo "Calculating SHA256 for: $source_url" -sha=$(curl -L "$source_url" | shasum -a 256 | cut -d ' ' -f 1) - -pushd "$cliHomebrewDir" - -echo "pulling latest dnote-homebrew repo" -git checkout master -git pull origin master - -cat > ./Formula/dnote.rb << EOF -class Dnote < Formula - desc "Simple command line notebook for programmers" - homepage "https://www.getdnote.com" - url "https://github.com/dnote/dnote/archive/refs/tags/cli-v${version}.tar.gz" - sha256 "${sha}" - license "GPL-3.0" - head "https://github.com/dnote/dnote.git", branch: "master" - - depends_on "go" => :build - - def install - ldflags = "-s -w -X main.apiEndpoint=https://api.getdnote.com -X main.versionTag=#{version}" - system "go", "build", *std_go_args(ldflags: ldflags), "-tags", "fts5", "./pkg/cli" - end - - test do - system "#{bin}/dnote", "version" - end -end -EOF - -git add . -git commit --author="Bot " -m "Release ${version}" -git push origin master - -popd diff --git a/scripts/release.sh b/scripts/release.sh deleted file mode 100755 index 3cda5897..00000000 --- a/scripts/release.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -# -# release.sh releases the tarballs and checksum in the build directory -# to GitHub and brew. A prerequisite is to build those files using build.sh. -# use: ./scripts/release.sh cli v0.4.8 path/to/assets - -set -euxo pipefail - -project=$1 -version=$2 -assetPath=$3 - -if [ "$project" != "cli" ] && [ "$project" != "server" ]; then - echo "unrecognized project '$project'" - exit 1 -fi -if [ -z "$version" ]; then - echo "no version specified." - exit 1 -fi -if [[ $version == v* ]]; then - echo "do not prefix version with v" - exit 1 -fi - -# 1. push tag -version_tag="$project-v$version" - -echo "* tagging and pushing the tag" -git tag -a "$version_tag" -m "Release $version_tag" -git push --tags - -# 2. release on GitHub -files=("$assetPath"/*) -file_flags=() -for file in "${files[@]}"; do - file_flags+=("$file") -done - -# mark as prerelease if version is not in a form of major.minor.patch -# e.g. 1.0.1-beta.1 -flags=() -if [[ ! "$version" =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then - flags+=("--prerelease") -fi - -echo "* creating release" -set -x - -# Create release -gh release create \ - "$version_tag" \ - "${file_flags[@]}" \ - "${flags[@]}" \ - --title="$version_tag"\ - --notes="Please see the [CHANGELOG](https://github.com/dnote/dnote/blob/master/CHANGELOG.md)" \ - --draft diff --git a/scripts/server/build.sh b/scripts/server/build.sh index a4e24df5..8ee95164 100755 --- a/scripts/server/build.sh +++ b/scripts/server/build.sh @@ -74,3 +74,6 @@ go install src.techknowlogick.com/xgo@latest build linux amd64 build linux arm64 +build linux arm +build linux 386 +build freebsd amd64