From 34dc98bfee5a1ced34b6906a5cf55eeeff03aa6e Mon Sep 17 00:00:00 2001 From: Aleksey Shein Date: Thu, 25 Jun 2026 15:56:30 +0200 Subject: [PATCH 1/2] build: standardize release asset naming (#1728) Rework build.sh so released assets have stable, predictable names that are easy to automate against: - drop the generated timestamp from tarball names - drop the "-binary" infix from tarball names - name macOS tarballs with "darwin" (matches GOOS) instead of "osx" - drop the version from .rpm/.deb filenames; the release download URL already carries it. Package metadata still records version + epoch - build arm64 .rpm and .deb in addition to amd64, using each ecosystem's arch convention (x86_64/aarch64 for rpm, amd64/arm64 for deb) - stop publishing the bare "gh-ost" binary; it was just the last build (darwin/arm64) and was ambiguous. Tarballs and packages cover every OS/arch - emit a SHA256SUMS manifest so downloads can be verified with `sha256sum -c SHA256SUMS` Resulting assets: gh-ost-{linux,darwin}-{amd64,arm64}.tar.gz gh-ost.{x86_64,aarch64}.rpm gh-ost.{amd64,arm64}.deb SHA256SUMS Also refactor build() to take GOOS/GOARCH directly, share common fpm flags via an array, and update the README build.sh description. --- README.md | 2 +- build.sh | 58 ++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 9ec44d318..898b2d2f5 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ Please see [Coding gh-ost](doc/coding-ghost.md) for a guide to getting started d `gh-ost` is a Go project; it is built with Go `1.15` and above. To build on your own, use either: - [script/build](https://github.com/github/gh-ost/blob/master/script/build) - this is the same build script used by CI hence the authoritative; artifact is `./bin/gh-ost` binary. -- [build.sh](https://github.com/github/gh-ost/blob/master/build.sh) for building `tar.gz` artifacts in `/tmp/gh-ost-release` +- [build.sh](https://github.com/github/gh-ost/blob/master/build.sh) for building the release artifacts (`tar.gz` archives plus `.rpm`/`.deb` packages and a `SHA256SUMS` manifest) in `/tmp/gh-ost-release` Generally speaking, `master` branch is stable, but only [releases](https://github.com/github/gh-ost/releases) are to be used in production. diff --git a/build.sh b/build.sh index 8c5bf47d8..4572a88ca 100755 --- a/build.sh +++ b/build.sh @@ -11,38 +11,51 @@ function setuptree() { } function build { - osname=$1 - osshort=$2 - GOOS=$3 - GOARCH=$4 + GOOS=$1 + GOARCH=$2 if ! go version | egrep -q 'go1\.(1[5-9]|[2-9][0-9]{1})' ; then echo "go version must be 1.15 or above" exit 1 fi - echo "Building ${osname}-${GOARCH} binary" + echo "Building ${GOOS}-${GOARCH} binary" export GOOS export GOARCH CGO_ENABLED="${CGO_ENABLED:-0}" go build -ldflags "$ldflags" -o "$buildpath/$target" go/cmd/gh-ost/main.go if [ $? -ne 0 ]; then - echo "Build failed for ${osname} ${GOARCH}." + echo "Build failed for ${GOOS} ${GOARCH}." exit 1 fi - (cd $buildpath && tar cfz ./gh-ost-binary-${osshort}-${GOARCH}-${timestamp}.tar.gz $target) + (cd $buildpath && tar cfz ./gh-ost-${GOOS}-${GOARCH}.tar.gz $target) - # build RPM and deb for Linux, x86-64 only - if [ "$GOOS" == "linux" ] && [ "$GOARCH" == "amd64" ] ; then + # build RPM and deb packages for Linux + if [ "$GOOS" == "linux" ] ; then echo "Creating Distro full packages" + case "$GOARCH" in + amd64) rpm_arch=x86_64 ; deb_arch=amd64 ;; + arm64) rpm_arch=aarch64 ; deb_arch=arm64 ;; + *) rpm_arch=$GOARCH ; deb_arch=$GOARCH ;; + esac builddir=$(setuptree) cp $buildpath/$target $builddir/gh-ost/usr/bin cd $buildpath - fpm -v "${RELEASE_VERSION}" --epoch 1 -f -s dir -n gh-ost -m 'GitHub' --description "GitHub's Online Schema Migrations for MySQL " --url "https://github.com/github/gh-ost" --vendor "GitHub" --license "Apache 2.0" -C $builddir/gh-ost --prefix=/ -t rpm --rpm-rpmbuild-define "_build_id_links none" --rpm-os linux . - fpm -v "${RELEASE_VERSION}" --epoch 1 -f -s dir -n gh-ost -m 'GitHub' --description "GitHub's Online Schema Migrations for MySQL " --url "https://github.com/github/gh-ost" --vendor "GitHub" --license "Apache 2.0" -C $builddir/gh-ost --prefix=/ -t deb --deb-no-default-config-files . + + fpm_opts=( + -v "${RELEASE_VERSION}" --epoch 1 -f -s dir -n gh-ost + -m 'GitHub' --description "GitHub's Online Schema Migrations for MySQL " + --url "https://github.com/github/gh-ost" --vendor "GitHub" --license "Apache 2.0" + -C "$builddir/gh-ost" --prefix=/ + ) + fpm "${fpm_opts[@]}" -t rpm -a "${rpm_arch}" -p "gh-ost.${rpm_arch}.rpm" --rpm-rpmbuild-define "_build_id_links none" --rpm-os linux . + fpm "${fpm_opts[@]}" -t deb -a "${deb_arch}" -p "gh-ost.${deb_arch}.deb" --deb-no-default-config-files . cd - fi + + # Drop the bare binary so only tarballs and packages are published as release assets + rm -f "$buildpath/$target" } main() { @@ -60,22 +73,23 @@ main() { buildpath=/tmp/gh-ost-release target=gh-ost - timestamp=$(date "+%Y%m%d%H%M%S") ldflags="-X main.AppVersion=${RELEASE_VERSION} -X main.GitCommit=${GIT_COMMIT}" mkdir -p ${buildpath} rm -rf ${buildpath:?}/* - build GNU/Linux linux linux amd64 - build GNU/Linux linux linux arm64 - build macOS osx darwin amd64 - build macOS osx darwin arm64 - - bin_files=$(find $buildpath/gh-ost* -type f -maxdepth 1) - echo "Binaries found in:" - echo "$bin_files" - + build linux amd64 + build linux arm64 + build darwin amd64 + build darwin arm64 + + # Generate a SHA256SUMS manifest with basenames only, so a downloaded set can be + # verified in place with `sha256sum -c SHA256SUMS` (no absolute build paths leak in). + # The name has no gh-ost prefix, so it self-excludes from the gh-ost* glob below. echo "Checksums:" - (shasum -a256 $bin_files 2>/dev/null) + ( cd "$buildpath" && shasum -a256 gh-ost* | tee SHA256SUMS ) + + echo "Release assets:" + ls -1 "$buildpath" echo "Build Success!" } From 22875f4f4e5b34d4f0a306e9f11308492ea8fe58 Mon Sep 17 00:00:00 2001 From: Aleksey Shein Date: Thu, 25 Jun 2026 19:24:47 +0200 Subject: [PATCH 2/2] build: simplify packaging Dockerfile and dock script (#1728) Slim down the release packaging tooling: - Dockerfile.packaging: collapse the per-package RUN layers into one, drop packages already provided by the golang:bookworm base (git, tar, curl, gcc, g++, bash) and the unused rsync, and drop the legacy GOPATH src layout (the build is module-mode). Only ruby/ruby-dev/build-essential (for fpm) and rpm (for rpmbuild) are installed now. - Use a multi-stage build with a `scratch` artifacts stage so `docker build --output type=local` exports the assets straight to the host. This drops the intermediate container, bind mount, -it TTY and find|xargs cp dance from `script/dock pkg`. - build.sh: stage the fpm package tree in the system temp dir instead of under $buildpath, so it never lands among the exported release artifacts. --- Dockerfile.packaging | 31 +++++++++++++++---------------- build.sh | 4 +++- script/dock | 19 +++++++++++++------ 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Dockerfile.packaging b/Dockerfile.packaging index 41ac23eb5..7c4a1db84 100644 --- a/Dockerfile.packaging +++ b/Dockerfile.packaging @@ -1,20 +1,19 @@ -FROM golang:1.25.9-bookworm +FROM golang:1.25.9-bookworm AS build -RUN apt-get update -RUN apt-get install -y ruby ruby-dev rubygems build-essential -RUN gem install fpm -ENV GOPATH=/tmp/go +# build.sh shells out to fpm to produce the .deb/.rpm packages. fpm is a Ruby +# gem (needs ruby + ruby-dev + a C toolchain), and the rpm target needs +# rpmbuild from the `rpm` package. Everything else build.sh uses (git, tar, +# gcc, bash, ...) already ships in the golang:bookworm base image. +RUN apt-get update \ + && apt-get install -y --no-install-recommends ruby ruby-dev build-essential rpm \ + && rm -rf /var/lib/apt/lists/* \ + && gem install --no-document fpm -RUN apt-get install -y curl -RUN apt-get install -y rsync -RUN apt-get install -y gcc -RUN apt-get install -y g++ -RUN apt-get install -y bash -RUN apt-get install -y git -RUN apt-get install -y tar -RUN apt-get install -y rpm - -RUN mkdir -p $GOPATH/src/github.com/github/gh-ost -WORKDIR $GOPATH/src/github.com/github/gh-ost +WORKDIR /gh-ost COPY . . RUN bash build.sh + +# Export-only stage: `docker build --target artifacts --output type=local,dest=...` +# writes just the release assets to the host, no intermediate container needed. +FROM scratch AS artifacts +COPY --from=build /tmp/gh-ost-release/ / diff --git a/build.sh b/build.sh index 4572a88ca..847ddf0cf 100755 --- a/build.sh +++ b/build.sh @@ -3,8 +3,10 @@ RELEASE_VERSION= buildpath= +# Create a scratch tree to stage the package filesystem for fpm. It lives in the +# system temp dir, not $buildpath, so it never lands among the release artifacts. function setuptree() { - b=$( mktemp -d $buildpath/gh-ostXXXXXX ) || return 1 + b=$( mktemp -d ) || return 1 mkdir -p $b/gh-ost mkdir -p $b/gh-ost/usr/bin echo $b diff --git a/script/dock b/script/dock index 486061d82..93bed8265 100755 --- a/script/dock +++ b/script/dock @@ -1,9 +1,12 @@ #!/bin/bash +set -e + # Usage: -# dock [arg] -# dock test: build gh-ost & run unit and integration tests -# docker pkg [target-path]: build gh-ost release packages and copy to target path (default path: /tmp/gh-ost-release) +# dock [arg] +# dock test: build gh-ost & run unit and integration tests +# dock pkg [target-path]: build gh-ost release packages and write them to +# target-path (default: /tmp/gh-ost-release) command="$1" @@ -14,12 +17,16 @@ case "$command" in ;; "pkg") packages_path="${2:-/tmp/gh-ost-release}" - docker_target="gh-ost-packaging" - docker build . -f Dockerfile.packaging -t "${docker_target}" && docker run --rm -it -v "${packages_path}:/tmp/pkg" "${docker_target}:latest" bash -c 'find /tmp/gh-ost-release/ -maxdepth 1 -type f | xargs cp -t /tmp/pkg' + # BuildKit exports the artifacts stage straight to the host; no intermediate + # container, bind mount, or copy step needed. Requires BuildKit (default in + # modern Docker; set DOCKER_BUILDKIT=1 on older daemons). + DOCKER_BUILDKIT=1 docker build . -f Dockerfile.packaging \ + --target artifacts --output "type=local,dest=${packages_path}" echo "packages generated on ${packages_path}:" ls -l "${packages_path}" ;; *) - >&2 echo "Usage: dock dock [arg]" + >&2 echo "Usage: dock [target-path]" exit 1 + ;; esac