Add multi-package Alpine packaging

This commit is contained in:
Joachim Schlöffel
2026-06-08 23:43:29 +02:00
parent d5a32abcd4
commit edc68c825b
22 changed files with 1013 additions and 146 deletions

View File

@@ -36,7 +36,7 @@ jobs:
- name: Build packages - name: Build packages
run: | run: |
scripts/apk/clean.sh scripts/apk/clean.sh
apkbuild-lint packaging/alpine/local/seaweedfs/APKBUILD find packaging/alpine/local -mindepth 2 -maxdepth 2 -name APKBUILD -exec apkbuild-lint {} +
scripts/apk/ci-build.sh scripts/apk/ci-build.sh
scripts/apk/list-packages.sh scripts/apk/list-packages.sh

432
README.md
View File

@@ -1,26 +1,222 @@
# SeaweedFS Alpine Package # Alpine Packages
Local Alpine 3.23 packaging for SeaweedFS `4.31`. The workflow runs Alpine tooling Alpine 3.23 packaging for internal packages.
inside Docker and writes signed packages under `packages/local/<arch>/`.
## Commands This repository builds signed `x86_64` and `aarch64` APKs and publishes them to
the Gitea Alpine package registry under:
```text
https://code.factoring.digital/api/packages/fsp-ops/alpine/v3.23/seaweedfs-alpine
```
## Use In Production
### Add The Package Repository
Install the registry signing key on each Alpine node:
```sh ```sh
curl -fsSLo /etc/apk/keys/fsp-ops-alpine.rsa.pub \
https://code.factoring.digital/api/packages/fsp-ops/alpine/key
```
Add the package repository:
```sh
printf '%s\n' \
'https://code.factoring.digital/api/packages/fsp-ops/alpine/v3.23/seaweedfs-alpine' \
>> /etc/apk/repositories
apk update
```
If the registry is private, include a Gitea user and package token in the
repository URL:
```text
https://<user>:<token>@code.factoring.digital/api/packages/fsp-ops/alpine/v3.23/seaweedfs-alpine
```
### Install SeaweedFS Node Roles
Install the base package plus only the OpenRC role packages needed on that node.
For a single-node test or a compact small deployment:
```sh
apk add seaweedfs \
seaweedfs-master-openrc \
seaweedfs-volume-openrc \
seaweedfs-filer-openrc
```
For separated production nodes, install only the role running there:
```sh
apk add seaweedfs seaweedfs-master-openrc
apk add seaweedfs seaweedfs-volume-openrc
apk add seaweedfs seaweedfs-filer-openrc
```
Available OpenRC role packages:
```text
seaweedfs-master-openrc -> /etc/init.d/seaweedfs.master
seaweedfs-volume-openrc -> /etc/init.d/seaweedfs.volume
seaweedfs-filer-openrc -> /etc/init.d/seaweedfs.filer
seaweedfs-s3-openrc -> /etc/init.d/seaweedfs.s3
seaweedfs-webdav-openrc -> /etc/init.d/seaweedfs.webdav
seaweedfs-sftp-openrc -> /etc/init.d/seaweedfs.sftp
seaweedfs-admin-openrc -> /etc/init.d/seaweedfs.admin
seaweedfs-worker-openrc -> /etc/init.d/seaweedfs.worker
```
The package name is `seaweedfs`, matching Alpine aports. If a node already has
Alpine's old generic OpenRC package installed, remove it before installing a
role-specific split:
```sh
apk del seaweedfs-openrc
apk add seaweedfs seaweedfs-master-openrc
```
### Configure Services
Runtime files are installed in the usual Alpine locations:
```text
/usr/bin/weed
/etc/seaweedfs/*.toml
/etc/conf.d/seaweedfs.*
/etc/init.d/seaweedfs.*
```
Edit `/etc/conf.d/seaweedfs.<role>` for command-line flags and
`/etc/seaweedfs/*.toml` for SeaweedFS config. The packaged defaults are short
and production-neutral; full upstream example configs are in:
```text
/usr/share/doc/seaweedfs/examples/
```
Enable and start only the services needed on the node:
```sh
rc-update add seaweedfs.master default
rc-service seaweedfs.master start
rc-update add seaweedfs.volume default
rc-service seaweedfs.volume start
rc-update add seaweedfs.filer default
rc-service seaweedfs.filer start
```
Check the installed binary and service state:
```sh
weed version
rc-service seaweedfs.master status
```
### Optional Packages
Install docs and generated examples:
```sh
apk add seaweedfs-doc
```
Install bash completion:
```sh
apk add bash-completion seaweedfs-bash-completion
```
`seaweedfs-bash-completion` is also selected automatically when `seaweedfs` and
`bash-completion` are installed together.
### Upgrade Or Pin
Use normal Alpine package operations:
```sh
apk upgrade seaweedfs
rc-service seaweedfs.master restart
```
Pin a specific package build when needed:
```sh
apk add seaweedfs=4.31-r3
```
### Install GreptimeDB
Install GreptimeDB:
```sh
apk add greptimedb
```
Check the installed binary:
```sh
greptime --version
```
## Background And Contribution
### Package Layout
Package sources live under:
```text
packaging/alpine/local/<pkgname>/
```
Currently packaged:
```text
seaweedfs SeaweedFS 4.31 release binary and OpenRC role splits
greptimedb GreptimeDB 1.0.2 built from source for x86_64
```
### SeaweedFS Package
The APKBUILD repackages the official SeaweedFS Linux release tarballs. It does
not build SeaweedFS from source. The package installs:
- `weed` as `/usr/bin/weed`
- minimal active config under `/etc/seaweedfs/`
- role-specific OpenRC subpackages
- upstream example configs under `/usr/share/doc/seaweedfs/examples/`
- generated bash completion as a separate subpackage
Local build output is written under:
```text
packages/local/<arch>/
```
Signing keys and distfiles are cached under:
```text
.cache/abuild/
.cache/apk-distfiles/
```
### Build Commands
```sh
mise run apk:lint
mise run apk:build mise run apk:build
mise run apk:build-all mise run apk:build-all
mise run apk:build-x86_64
mise run apk:build-aarch64
mise run apk:update-generated
mise run apk:checksum
mise run apk:lint
mise run apk:packages mise run apk:packages
mise run apk:smoke mise run apk:smoke
mise run apk:test-install
mise run apk:test-shell mise run apk:test-shell
mise run apk:publish-check mise run apk:publish-check
mise run apk:publish-gitea
mise run gitea-workflow-build
mise run apk:shell
mise run apk:clean
``` ```
`apk:build` targets `x86_64` by default. Multi-arch builds target `x86_64` and `apk:build` targets `x86_64` by default. Multi-arch builds target `x86_64` and
@@ -30,116 +226,121 @@ mise run apk:clean
ALPINE_ARCHES="x86_64 aarch64" mise run apk:build-all ALPINE_ARCHES="x86_64 aarch64" mise run apk:build-all
``` ```
The release binary is also exposed as a mise HTTP tool stub: Unqualified build and install-test tasks discover all packages. GreptimeDB is a
source build and can take close to an hour; it is limited to `x86_64` during
production evaluation. Target fast package work explicitly:
Build or test a subset of packages with `ALPINE_PACKAGE` or `ALPINE_PACKAGES`:
```sh ```sh
./bin/weed version ALPINE_PACKAGE=seaweedfs mise run apk:build
ALPINE_PACKAGES="seaweedfs other-package" mise run apk:build-all
ALPINE_PACKAGE=seaweedfs mise run apk:test-install
ALPINE_PACKAGE=greptimedb mise run apk:checksum
ALPINE_PACKAGE=greptimedb mise run apk:build
ALPINE_PACKAGE=greptimedb SKIP_BUILD=1 mise run apk:test-install
``` ```
## Package Layout Use the test shell to inspect the package in Alpine with the current build
installed:
```sh
ALPINE_PACKAGE=seaweedfs mise run apk:shell
mise run apk:test-shell
```
Use the install test to validate the production instructions against the local
package repository in a fresh Alpine container:
```sh
mise run apk:test-install
```
Use `SKIP_BUILD=1` to reuse existing local packages:
```sh
SKIP_BUILD=1 mise run apk:test-install
SKIP_BUILD=1 mise run apk:test-shell
```
### Adding Packages
Each package lives under:
```text
packaging/alpine/local/<pkgname>/
```
The only required file is `APKBUILD`. Package-specific helpers can be added as
hooks:
```text
packaging/alpine/local/<pkgname>/scripts/test-install.sh
packaging/alpine/local/<pkgname>/scripts/update-generated-sources.sh
```
Repo-level tasks discover all `packaging/alpine/local/*/APKBUILD` files. Hook
tasks skip packages that do not provide the requested hook.
Start new packages from the blueprint:
```text
packaging/alpine/blueprint/
```
For compiled software, declare build tools in `makedepends` and use normal
Alpine `build()`, `check()`, and `package()` functions in the package's
`APKBUILD`.
### Generated Package Sources
Generated files should live next to the package's `APKBUILD` so
`abuild checksum` can track them as ordinary package sources. For SeaweedFS,
that includes:
```text ```text
packaging/alpine/local/seaweedfs/APKBUILD
packaging/alpine/local/seaweedfs/*.toml
packaging/alpine/local/seaweedfs/example-*.toml packaging/alpine/local/seaweedfs/example-*.toml
packaging/alpine/local/seaweedfs/weed.bash-completion packaging/alpine/local/seaweedfs/weed.bash-completion
packaging/alpine/local/seaweedfs/seaweedfs.*.confd
packaging/alpine/local/seaweedfs/seaweedfs.initd
packages/local/<arch>/
``` ```
The package repackages upstream release tarballs, so builds use Refresh generated sources through package-local hooks:
`ALPINE_BUILD_PLATFORM=linux/amd64` by default. Signing keys and distfiles are
cached in `.cache/abuild/` and `.cache/apk-distfiles/`.
The Gitea workflow publishes to the Alpine package registry branch `v3.23`.
Override `ALPINE_VERSION` and `ALPINE_REGISTRY_BRANCH` if you intentionally
build for another Alpine branch, including `edge`.
Use `mise run gitea-workflow-build` for a local `pull_request` workflow check
through `act`; the publish step is skipped for that event.
## Test And Publish
```sh ```sh
mise run apk:smoke mise run apk:update-generated
mise run apk:test-shell ALPINE_PACKAGE=seaweedfs mise run apk:update-generated
mise run apk:publish-check mise run apk:checksum
``` ```
`apk:publish-check` runs APKBUILD linting, rebuilds configured architectures, Run `apk:checksum` after changing any file listed in `source=`.
lists package metadata, and installs the built packages in an Alpine container.
`apk:test-shell` builds the selected architecture, installs the local package
set in an Alpine container, and opens a shell. Use `SKIP_BUILD=1` to reuse an
existing local repository. Set `TEST_SHELL_TIMEOUT` to adjust the timeout for
`apk update` and `apk add` inside the test container.
Install from the local repo with the base package plus the role-specific OpenRC ### Gitea Workflow
subpackage you need:
```sh The workflow at `.gitea/workflows/build.yml` builds in an `alpine:3.23`
apk add seaweedfs seaweedfs-master-openrc container and publishes on push or tag. It skips publishing for pull request
``` events.
Optional generated material is split out: Repository variables:
```sh
apk add seaweedfs-doc
apk add bash-completion seaweedfs-bash-completion
```
The doc split installs upstream scaffolds under
`/usr/share/doc/seaweedfs/examples/`. The bash completion split installs
`/usr/share/bash-completion/completions/weed` and is also selected by
`install_if` when `seaweedfs` and `bash-completion` are installed together.
This repository publishes `seaweedfs`, matching Alpine aports. Remove Alpine's
old generic OpenRC package before installing a role split:
```sh
apk del seaweedfs-openrc
apk add seaweedfs seaweedfs-master-openrc
```
The OpenRC subpackages are:
```text ```text
seaweedfs-master-openrc -> seaweedfs.master INSTANCE_URL=https://code.factoring.digital
seaweedfs-volume-openrc -> seaweedfs.volume PACKAGE_OWNER=fsp-ops
seaweedfs-filer-openrc -> seaweedfs.filer PACKAGE_NAME=seaweedfs-alpine
seaweedfs-s3-openrc -> seaweedfs.s3
seaweedfs-webdav-openrc -> seaweedfs.webdav
seaweedfs-sftp-openrc -> seaweedfs.sftp
seaweedfs-admin-openrc -> seaweedfs.admin
seaweedfs-worker-openrc -> seaweedfs.worker
``` ```
Each service has defaults in `/etc/conf.d/seaweedfs.*`. Enable only the roles Repository secrets:
needed on the node:
```text
PACKAGE_USER=<gitea package publisher>
PACKAGE_TOKEN=<token with package write access>
```
Pre-check the workflow locally with `act`:
```sh ```sh
rc-update add seaweedfs.master default mise run gitea-workflow-build
rc-service seaweedfs.master start
``` ```
If the repo key is missing on a target system, copy `.cache/abuild/*.rsa.pub` Publish already-built local packages manually:
into `/etc/apk/keys/` before `apk update`.
## Gitea Registry
The Gitea workflow reads these repository variables and secrets:
```text
Variables: INSTANCE_URL, PACKAGE_OWNER, PACKAGE_NAME
Secrets: PACKAGE_USER, PACKAGE_TOKEN
```
It uploads each built `*.apk` with HTTP `PUT` to:
```text
${INSTANCE_URL}/api/packages/${PACKAGE_OWNER}/alpine/v3.23/${PACKAGE_NAME}
```
For local publishing, run:
```sh ```sh
INSTANCE_URL=https://code.factoring.digital \ INSTANCE_URL=https://code.factoring.digital \
@@ -150,21 +351,38 @@ PACKAGE_TOKEN=... \
mise run apk:publish-gitea mise run apk:publish-gitea
``` ```
## Alpine Package Dos And Don'ts ### Alpine Package Rules
Do: Do:
- Keep local `source` files next to the `APKBUILD`. - Keep local package sources next to the `APKBUILD`.
- Put full upstream scaffolds in `/usr/share/doc/seaweedfs/examples/`, not active `/etc`. - Put full upstream scaffolds in `/usr/share/doc/seaweedfs/examples/`, not active `/etc`.
- Run `mise run apk:update-generated` when updating generated examples or completions. - Install only the OpenRC role packages needed on each node.
- Run `mise run apk:checksum` after changing any package source file. - Keep `/etc/conf.d` defaults short and production-neutral.
- Increment `pkgrel` for package-only changes; reset it when `pkgver` changes. - Increment `pkgrel` for package-only changes; reset it when `pkgver` changes.
- Keep installed defaults short and production-neutral. - Run `mise run apk:publish-check` before opening a PR.
- Run `mise run apk:publish-check` before handing off a repository.
Don't: Don't:
- Edit generated `src/`, `pkg/`, or `packages/` output. - Edit generated `src/`, `pkg/`, or `packages/` output.
- Hand-edit checksum lines when `abuild checksum` can do it. - Hand-edit checksum lines when `abuild checksum` can update them.
- Bundle long upstream examples as active `/etc` defaults. - Install all OpenRC roles by default.
- Install all OpenRC roles on a node by default. - Put long upstream examples into active `/etc` defaults.
### Practical PR Checklist
Before opening a PR, run:
```sh
mise run apk:publish-check
mise run gitea-workflow-build
mise run apk:test-install
```
Then test at least one installed role in the package shell:
```sh
mise run apk:test-shell
rc-service seaweedfs.master start
weed version
```

View File

@@ -48,6 +48,10 @@ run = "scripts/apk/ci-smoke.sh"
description = "Open an Alpine shell with the current local package build installed" description = "Open an Alpine shell with the current local package build installed"
run = "scripts/apk/test-shell.sh" run = "scripts/apk/test-shell.sh"
[tasks."apk:test-install"]
description = "Test README installation instructions in a fresh Alpine container"
run = "scripts/apk/test-install.sh"
[tasks."apk:packages"] [tasks."apk:packages"]
description = "List built Alpine packages and dependencies" description = "List built Alpine packages and dependencies"
run = "scripts/apk/list-packages.sh" run = "scripts/apk/list-packages.sh"

View File

@@ -0,0 +1,28 @@
# Maintainer: Your Name <you@example.com>
pkgname=example-package
pkgver=0.1.0
pkgrel=0
pkgdesc="Short package description"
url="https://example.com/example-package"
arch="x86_64 aarch64"
license="Apache-2.0"
depends=""
makedepends="build-base"
subpackages="$pkgname-doc"
source="$pkgname-$pkgver.tar.gz::https://example.com/releases/$pkgver.tar.gz"
builddir="$srcdir/$pkgname-$pkgver"
build() {
make
}
check() {
make check
}
package() {
install -Dm755 "$builddir"/example-package "$pkgdir"/usr/bin/example-package
}
sha512sums="
"

View File

@@ -0,0 +1,26 @@
# Alpine Package Blueprint
Copy this directory to `packaging/alpine/local/<pkgname>/` and replace the
placeholder values before building.
Required:
- `APKBUILD`
Optional package-local hooks:
- `scripts/test-install.sh` validates README-style installation instructions.
- `scripts/update-generated-sources.sh` refreshes generated files that are
listed in `source=`.
Repo-level tasks discover packages from `packaging/alpine/local/*/APKBUILD`.
Limit a task to one or more packages with:
```sh
ALPINE_PACKAGE=<pkgname> mise run apk:build
ALPINE_PACKAGES="<pkg-a> <pkg-b>" mise run apk:build-all
ALPINE_PACKAGE=<pkgname> mise run apk:test-install
```
For compiled software, put normal Alpine build logic in `build()`, `check()`,
and `package()` and declare build tools in `makedepends`.

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}"
package_name="${PACKAGE_NAME:-$(basename "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)")}"
alpine_version="${ALPINE_VERSION:-3.23}"
arch="${ALPINE_ARCH:-x86_64}"
case "${arch}" in
x86_64)
platform="${ALPINE_TEST_PLATFORM:-linux/amd64}"
;;
aarch64)
platform="${ALPINE_TEST_PLATFORM:-linux/arm64}"
;;
*)
printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2
exit 2
;;
esac
if [[ "${SKIP_BUILD:-0}" != "1" ]]; then
ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}"
fi
docker run --rm --platform "${platform}" \
-e "ALPINE_ARCH=${arch}" \
-e "PACKAGE_NAME=${package_name}" \
-v "${repo_root}/packages/local:/repo:ro" \
"alpine:${alpine_version}" \
sh -lc '
set -e
cp "/repo/${ALPINE_ARCH}"/*.rsa.pub /etc/apk/keys/
printf "%s\n" /repo >> /etc/apk/repositories
apk update
apk add "${PACKAGE_NAME}"
# Replace these with package-specific install assertions.
apk info -e "${PACKAGE_NAME}"
'

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
package_dir="${PACKAGE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
printf 'No generated sources for %s\n' "${package_dir}"

View File

@@ -0,0 +1,57 @@
# Maintainer: Joachim Schlöffel <me@joachim-schloeffel.com>
pkgname=greptimedb
pkgver=1.0.2
pkgrel=0
pkgdesc="Cloud-native observability database for metrics, logs, and traces"
url="https://github.com/GreptimeTeam/greptimedb"
arch="x86_64"
license="Apache-2.0"
depends="ca-certificates"
makedepends="
binutils
cargo
clang
cmake
coreutils
lld
linux-headers
make
mold
openssl-dev
openssl-libs-static
perl
protobuf
protobuf-dev
rust
zlib-dev
zlib-static
zstd-dev
zstd-static
"
options="net"
source="$pkgname-$pkgver.tar.gz::https://github.com/GreptimeTeam/greptimedb/archive/refs/tags/v$pkgver.tar.gz"
export CARGO_HOME="$srcdir/cargo"
export CARGO_BUILD_JOBS="${CARGO_BUILD_JOBS:-2}"
export LIBRARY_PATH="/usr/lib"
build() {
cargo build \
--release \
--locked \
--bin greptime \
--features servers/dashboard
}
check() {
"$builddir"/target/release/greptime --version
}
package() {
install -Dm755 "$builddir"/target/release/greptime \
"$pkgdir"/usr/bin/greptime
}
sha512sums="
7f4ac722b84a26816030e65d504b37a53edfca15de669a4f6ee7a903f1a29c8358dcc2376a0a6cfd9ded13b0c5d7550a6856b9b10dc8cd080c6b12970553a0ea greptimedb-1.0.2.tar.gz
"

View File

@@ -0,0 +1,77 @@
# syntax=docker/dockerfile:1.7
ARG ALPINE_VERSION=3.23
ARG RUST_VERSION=1.90.0
FROM alpine:${ALPINE_VERSION} AS builder
ARG GREPTIMEDB_REF=main
ARG RUST_VERSION
RUN apk add --no-cache \
bash \
ca-certificates \
curl \
git \
build-base \
linux-headers \
clang \
lld \
mold \
cmake \
make \
perl \
pkgconf \
protobuf \
protobuf-dev \
zlib-dev \
zlib-static \
zstd-dev \
zstd-static \
openssl-dev \
openssl-libs-static \
binutils \
coreutils
ENV RUSTUP_HOME=/usr/local/rustup
ENV CARGO_HOME=/usr/local/cargo
ENV PATH=/usr/local/cargo/bin:${PATH}
ENV CARGO_BUILD_JOBS=2
ENV LIBRARY_PATH=/usr/lib
RUN curl https://sh.rustup.rs -sSf | sh -s -- \
-y \
--profile minimal \
--default-toolchain ${RUST_VERSION}
WORKDIR /src
RUN git clone https://github.com/GreptimeTeam/greptimedb.git . \
&& git checkout ${GREPTIMEDB_REF}
RUN --mount=type=cache,target=/usr/local/cargo/registry \
--mount=type=cache,target=/usr/local/cargo/git \
cargo build \
--release \
--locked \
--bin greptime \
--features servers/dashboard
RUN mkdir -p /out \
&& cp /src/target/release/greptime /out/greptime.debug \
&& cp /src/target/release/greptime /out/greptime \
&& strip /out/greptime \
&& ls -lh /out \
&& file /out/greptime \
&& ldd /out/greptime || true
FROM scratch AS artifact
COPY --from=builder /out/greptime /greptime
COPY --from=builder /out/greptime.debug /greptime.debug
FROM alpine:${ALPINE_VERSION} AS runtime
RUN apk add --no-cache ca-certificates
COPY --from=builder /out/greptime /usr/local/bin/greptime
ENTRYPOINT ["/usr/local/bin/greptime"]

View File

@@ -0,0 +1,53 @@
IMAGE ?= greptime-alpine-builder
RUNTIME_IMAGE ?= greptime-alpine
GREPTIMEDB_REF ?= main
ALPINE_VERSION ?= 3.23
RUST_VERSION ?= 1.90.0
OUT_DIR ?= dist
.PHONY: artifact image build inspect run clean
artifact:
mkdir -p $(OUT_DIR)
docker build \
--target artifact \
--build-arg GREPTIMEDB_REF=$(GREPTIMEDB_REF) \
--build-arg ALPINE_VERSION=$(ALPINE_VERSION) \
--build-arg RUST_VERSION=$(RUST_VERSION) \
--output type=local,dest=$(OUT_DIR) .
chmod +x $(OUT_DIR)/greptime
build:
docker build \
--target builder \
--build-arg GREPTIMEDB_REF=$(GREPTIMEDB_REF) \
--build-arg ALPINE_VERSION=$(ALPINE_VERSION) \
--build-arg RUST_VERSION=$(RUST_VERSION) \
-t $(IMAGE):$(GREPTIMEDB_REF) .
image:
docker build \
--target runtime \
--build-arg GREPTIMEDB_REF=$(GREPTIMEDB_REF) \
--build-arg ALPINE_VERSION=$(ALPINE_VERSION) \
--build-arg RUST_VERSION=$(RUST_VERSION) \
-t $(RUNTIME_IMAGE):$(GREPTIMEDB_REF) .
inspect: artifact
file $(OUT_DIR)/greptime
ldd $(OUT_DIR)/greptime || true
ls -lh $(OUT_DIR)/greptime $(OUT_DIR)/greptime.debug
run: image
docker run --rm -it \
-p 127.0.0.1:4000-4003:4000-4003 \
-v "$$(pwd)/greptimedb_data:/greptimedb_data" \
$(RUNTIME_IMAGE):$(GREPTIMEDB_REF) \
standalone start \
--http-addr 0.0.0.0:4000 \
--rpc-bind-addr 0.0.0.0:4001 \
--mysql-addr 0.0.0.0:4002 \
--postgres-addr 0.0.0.0:4003
clean:
rm -rf $(OUT_DIR)

View File

@@ -0,0 +1,33 @@
# GreptimeDB Alpine Package
This package builds `greptime` from the upstream GreptimeDB source release.
The old `Dockerfile` and `Makefile` in this directory are reference material for
the previous manual build. The APKBUILD uses the same build shape directly:
```sh
cargo build --release --locked --bin greptime --features servers/dashboard
```
## Package Commands
Refresh the source checksum after changing `pkgver`:
```sh
ALPINE_PACKAGE=greptimedb mise run apk:checksum
```
Build only this package:
```sh
ALPINE_PACKAGE=greptimedb mise run apk:build
```
Install-test an existing build without recompiling:
```sh
ALPINE_PACKAGE=greptimedb SKIP_BUILD=1 mise run apk:test-install
```
The full build can take close to an hour. Do not run it as part of lightweight
metadata or script checks.

View File

@@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}"
package_name="${PACKAGE_NAME:-greptimedb}"
alpine_version="${ALPINE_VERSION:-3.23}"
arch="${ALPINE_ARCH:-x86_64}"
case "${arch}" in
x86_64)
platform="${ALPINE_TEST_PLATFORM:-linux/amd64}"
;;
aarch64)
platform="${ALPINE_TEST_PLATFORM:-linux/arm64}"
;;
*)
printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2
exit 2
;;
esac
if [[ "${SKIP_BUILD:-0}" != "1" ]]; then
ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}"
fi
if [[ ! -d "${repo_root}/packages/local/${arch}" ]]; then
printf 'missing local repository: packages/local/%s\n' "${arch}" >&2
printf 'run: ALPINE_PACKAGE=%s mise run apk:build\n' "${package_name}" >&2
exit 1
fi
docker run --rm --platform "${platform}" \
-e "ALPINE_ARCH=${arch}" \
-e "PACKAGE_NAME=${package_name}" \
-v "${repo_root}/packages/local:/repo:ro" \
"alpine:${alpine_version}" \
sh -lc '
set -e
cp "/repo/${ALPINE_ARCH}"/*.rsa.pub /etc/apk/keys/
printf "%s\n" /repo >> /etc/apk/repositories
apk update
apk add "${PACKAGE_NAME}"
test -x /usr/bin/greptime
greptime --version
'

View File

@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
package_dir="${PACKAGE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
printf 'No generated sources for %s\n' "${package_dir}"

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}"
package_name="${PACKAGE_NAME:-seaweedfs}"
alpine_version="${ALPINE_VERSION:-3.23}"
arch="${ALPINE_ARCH:-x86_64}"
case "${arch}" in
x86_64)
platform="${ALPINE_TEST_PLATFORM:-linux/amd64}"
;;
aarch64)
platform="${ALPINE_TEST_PLATFORM:-linux/arm64}"
;;
*)
printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2
exit 2
;;
esac
if [[ "${SKIP_BUILD:-0}" != "1" ]]; then
ALPINE_PACKAGE="${package_name}" "${repo_root}/scripts/apk/build.sh" build "${arch}"
fi
if [[ ! -d "${repo_root}/packages/local/${arch}" ]]; then
printf 'missing local repository: packages/local/%s\n' "${arch}" >&2
printf 'run: ALPINE_PACKAGE=%s mise run apk:build\n' "${package_name}" >&2
exit 1
fi
docker run --rm --platform "${platform}" \
-e "ALPINE_ARCH=${arch}" \
-v "${repo_root}/packages/local:/repo:ro" \
"alpine:${alpine_version}" \
sh -lc '
set -e
cp "/repo/${ALPINE_ARCH}"/*.rsa.pub /etc/apk/keys/
printf "%s\n" /repo >> /etc/apk/repositories
apk update
apk add seaweedfs \
seaweedfs-master-openrc \
seaweedfs-volume-openrc \
seaweedfs-filer-openrc
test -x /usr/bin/weed
test -d /etc/seaweedfs
test -f /etc/seaweedfs/master.toml
test -f /etc/seaweedfs/filer.toml
test -f /etc/conf.d/seaweedfs.master
test -f /etc/conf.d/seaweedfs.volume
test -f /etc/conf.d/seaweedfs.filer
test -f /etc/init.d/seaweedfs.master
test -f /etc/init.d/seaweedfs.volume
test -f /etc/init.d/seaweedfs.filer
weed version
rc-update add seaweedfs.master default
rc-update add seaweedfs.volume default
rc-update add seaweedfs.filer default
test -L /etc/runlevels/default/seaweedfs.master
test -L /etc/runlevels/default/seaweedfs.volume
test -L /etc/runlevels/default/seaweedfs.filer
apk add seaweedfs-doc bash-completion
apk info -e seaweedfs-bash-completion
test -d /usr/share/doc/seaweedfs/examples
test -f /usr/share/doc/seaweedfs/examples/master.toml
test -f /usr/share/bash-completion/completions/weed
'

View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../../.." && pwd)}"
package_dir="${PACKAGE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
configs=(credential filer master notification replication security shell)
for config in "${configs[@]}"; do
"${repo_root}/bin/weed" scaffold -config "${config}" 2>/dev/null \
> "${package_dir}/example-${config}.toml"
done
"${repo_root}/bin/weed" autocomplete bash 2>/dev/null \
| sed -E 's#complete -C "?[^"]*/weed"? weed#complete -C /usr/bin/weed weed#' \
> "${package_dir}/weed.bash-completion"

View File

@@ -4,6 +4,8 @@ set -euo pipefail
command_name="${1:-build}" command_name="${1:-build}"
requested_arch="${2:-${ALPINE_ARCH:-x86_64}}" requested_arch="${2:-${ALPINE_ARCH:-x86_64}}"
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
. "${repo_root}/scripts/apk/package-lib.sh"
alpine_version="${ALPINE_VERSION:-3.23}" alpine_version="${ALPINE_VERSION:-3.23}"
repo_name="${ALPINE_REPO_NAME:-local}" repo_name="${ALPINE_REPO_NAME:-local}"
build_platform="${ALPINE_BUILD_PLATFORM:-linux/amd64}" build_platform="${ALPINE_BUILD_PLATFORM:-linux/amd64}"
@@ -27,10 +29,14 @@ validate_arch() {
run_for_arch() { run_for_arch() {
local arch="$1" local arch="$1"
local subcommand="$2" local subcommand="$2"
local package_dir="$3"
local image_name local image_name
local container_package_dir
validate_arch "${arch}" validate_arch "${arch}"
image_name="${ALPINE_APK_BUILDER_IMAGE:-seaweedfs-apk-builder:${arch}}" apk_validate_package_dir "${package_dir}"
container_package_dir="$(apk_container_package_dir "${repo_root}" "${package_dir}")"
image_name="${ALPINE_APK_BUILDER_IMAGE:-alpine-apk-builder:${arch}}"
docker build \ docker build \
--platform "${build_platform}" \ --platform "${build_platform}" \
@@ -50,6 +56,7 @@ run_for_arch() {
-e "ALPINE_ARCH=${arch}" \ -e "ALPINE_ARCH=${arch}" \
-e "CARCH=${arch}" \ -e "CARCH=${arch}" \
-e "ALPINE_REPO_NAME=${repo_name}" \ -e "ALPINE_REPO_NAME=${repo_name}" \
-e "APKBUILD_DIR=${container_package_dir}" \
-e "PACKAGER=${PACKAGER:-Joachim Schlöffel <me@joachim-schloeffel.com>}" \ -e "PACKAGER=${PACKAGER:-Joachim Schlöffel <me@joachim-schloeffel.com>}" \
-v "${repo_root}:/work" \ -v "${repo_root}:/work" \
-v "${repo_root}/.cache/abuild:/home/builder/.abuild" \ -v "${repo_root}/.cache/abuild:/home/builder/.abuild" \
@@ -66,12 +73,38 @@ mkdir -p \
case "${command_name}" in case "${command_name}" in
build-all) build-all)
for arch in ${ALPINE_ARCHES:-x86_64 aarch64}; do while IFS= read -r package_dir; do
run_for_arch "${arch}" build for arch in ${ALPINE_ARCHES:-x86_64 aarch64}; do
done if ! apk_package_supports_arch "${package_dir}" "${arch}"; then
printf 'Skipping %s for unsupported arch %s\n' "$(apk_package_name "${package_dir}")" "${arch}"
continue
fi
run_for_arch "${arch}" build "${package_dir}"
done
done < <(apk_package_dirs "${repo_root}")
;; ;;
build|checksum|lint|shell) build|checksum|lint)
run_for_arch "${requested_arch}" "${command_name}" while IFS= read -r package_dir; do
if [[ "${command_name}" == "build" ]] && ! apk_package_supports_arch "${package_dir}" "${requested_arch}"; then
printf 'Skipping %s for unsupported arch %s\n' "$(apk_package_name "${package_dir}")" "${requested_arch}"
continue
fi
run_for_arch "${requested_arch}" "${command_name}" "${package_dir}"
done < <(apk_package_dirs "${repo_root}")
;;
shell)
package_count=0
selected_package_dir=""
while IFS= read -r package_dir; do
package_count=$((package_count + 1))
selected_package_dir="${package_dir}"
done < <(apk_package_dirs "${repo_root}")
if [[ "${package_count}" -ne 1 ]]; then
printf 'apk:shell needs exactly one package; set ALPINE_PACKAGE=<name>\n' >&2
exit 2
fi
run_for_arch "${requested_arch}" "${command_name}" "${selected_package_dir}"
;; ;;
*) *)
printf 'unknown command: %s\n' "${command_name}" >&2 printf 'unknown command: %s\n' "${command_name}" >&2

View File

@@ -2,10 +2,12 @@
set -euo pipefail set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
package_dir="${APKBUILD_DIR:-${repo_root}/packaging/alpine/local/seaweedfs}" . "${repo_root}/scripts/apk/package-lib.sh"
repo_name="${ALPINE_REPO_NAME:-local}" repo_name="${ALPINE_REPO_NAME:-local}"
arches="${ALPINE_ARCHES:-x86_64 aarch64}" arches="${ALPINE_ARCHES:-x86_64 aarch64}"
packager="${PACKAGER:-Joachim Schlöffel <me@joachim-schloeffel.com>}" packager="${PACKAGER:-Joachim Schlöffel <me@joachim-schloeffel.com>}"
selected_packages="${ALPINE_PACKAGE:-${ALPINE_PACKAGES:-}}"
if [[ "${1:-}" != "--as-builder" && "$(id -u)" == "0" ]]; then if [[ "${1:-}" != "--as-builder" && "$(id -u)" == "0" ]]; then
addgroup -g 1000 builder 2>/dev/null || addgroup builder addgroup -g 1000 builder 2>/dev/null || addgroup builder
@@ -15,7 +17,7 @@ if [[ "${1:-}" != "--as-builder" && "$(id -u)" == "0" ]]; then
printf 'permit nopass :wheel\n' > /etc/doas.d/wheel.conf printf 'permit nopass :wheel\n' > /etc/doas.d/wheel.conf
printf '%%wheel ALL=(ALL) NOPASSWD: ALL\n' > /etc/sudoers.d/wheel printf '%%wheel ALL=(ALL) NOPASSWD: ALL\n' > /etc/sudoers.d/wheel
chown -R builder:builder "${repo_root}" chown -R builder:builder "${repo_root}"
exec su builder -c "ALPINE_ARCHES='${arches}' ALPINE_REPO_NAME='${repo_name}' PACKAGER='${packager}' '${BASH_SOURCE[0]}' --as-builder" exec su builder -c "ALPINE_ARCHES='${arches}' ALPINE_REPO_NAME='${repo_name}' ALPINE_PACKAGES='${selected_packages}' PACKAGER='${packager}' '${BASH_SOURCE[0]}' --as-builder"
fi fi
export PACKAGER="${packager}" export PACKAGER="${packager}"
@@ -36,20 +38,29 @@ fi
doas cp "${HOME}"/.abuild/*.rsa.pub /etc/apk/keys/ doas cp "${HOME}"/.abuild/*.rsa.pub /etc/apk/keys/
for arch in ${arches}; do while IFS= read -r package_dir; do
case "${arch}" in apk_validate_package_dir "${package_dir}"
x86_64|aarch64) ;;
*) printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2; exit 2 ;;
esac
printf 'Building %s for %s\n' "$(basename "${package_dir}")" "${arch}" for arch in ${arches}; do
( case "${arch}" in
export ALPINE_ARCH="${arch}" x86_64|aarch64) ;;
export CARCH="${arch}" *) printf 'unsupported Alpine architecture: %s\n' "${arch}" >&2; exit 2 ;;
cd "${package_dir}" esac
abuild -r
)
mkdir -p "${REPODEST}/${repo_name}/${arch}" if ! apk_package_supports_arch "${package_dir}" "${arch}"; then
cp "${HOME}"/.abuild/*.rsa.pub "${REPODEST}/${repo_name}/${arch}/" printf 'Skipping %s for unsupported arch %s\n' "$(basename "${package_dir}")" "${arch}"
done continue
fi
printf 'Building %s for %s\n' "$(basename "${package_dir}")" "${arch}"
(
export ALPINE_ARCH="${arch}"
export CARCH="${arch}"
cd "${package_dir}"
abuild -r
)
mkdir -p "${REPODEST}/${repo_name}/${arch}"
cp "${HOME}"/.abuild/*.rsa.pub "${REPODEST}/${repo_name}/${arch}/"
done
done < <(apk_package_dirs "${repo_root}")

View File

@@ -4,7 +4,7 @@ set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
repo_name="${ALPINE_REPO_NAME:-local}" repo_name="${ALPINE_REPO_NAME:-local}"
arch="${ALPINE_ARCH:-$(apk --print-arch)}" arch="${ALPINE_ARCH:-$(apk --print-arch)}"
packages="${SMOKE_PACKAGES:-seaweedfs seaweedfs-master-openrc seaweedfs-filer-openrc seaweedfs-worker-openrc}" packages="${SMOKE_PACKAGES:-seaweedfs seaweedfs-master-openrc seaweedfs-filer-openrc seaweedfs-worker-openrc greptimedb}"
repo_dir="${repo_root}/packages/${repo_name}/${arch}" repo_dir="${repo_root}/packages/${repo_name}/${arch}"
read -r -a package_list <<< "${packages}" read -r -a package_list <<< "${packages}"
@@ -18,6 +18,9 @@ echo "${repo_root}/packages/${repo_name}" >> /etc/apk/repositories
apk update apk update
apk add "${package_list[@]}" apk add "${package_list[@]}"
weed version weed version
if command -v greptime >/dev/null 2>&1; then
greptime --version
fi
ls -1 /etc/seaweedfs ls -1 /etc/seaweedfs
if ls /etc/init.d/seaweedfs.* >/dev/null 2>&1; then if ls /etc/init.d/seaweedfs.* >/dev/null 2>&1; then
find /etc/init.d -maxdepth 1 -name 'seaweedfs.*' -print | sort find /etc/init.d -maxdepth 1 -name 'seaweedfs.*' -print | sort

View File

@@ -3,7 +3,7 @@ set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
rm -rf \ rm -rf "${repo_root}/packages"
"${repo_root}/packages" \ find "${repo_root}/packaging/alpine/local" \
"${repo_root}/packaging/alpine/local/seaweedfs/pkg" \ \( -type d -name pkg -o -type d -name src \) \
"${repo_root}/packaging/alpine/local/seaweedfs/src" -prune -exec rm -rf {} +

131
scripts/apk/package-lib.sh Normal file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env bash
apk_repo_root() {
cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd
}
apk_package_root() {
local repo_root="$1"
printf '%s\n' "${ALPINE_PACKAGE_ROOT:-${repo_root}/packaging/alpine/local}"
}
apk_package_dirs() {
local repo_root="$1"
local package_root
local package
local dir
package_root="$(apk_package_root "${repo_root}")"
if [[ -n "${APKBUILD_DIR:-}" ]]; then
printf '%s\n' "${APKBUILD_DIR}"
return
fi
if [[ -n "${ALPINE_PACKAGE:-}" || -n "${ALPINE_PACKAGES:-}" ]]; then
for package in ${ALPINE_PACKAGE:-${ALPINE_PACKAGES:-}}; do
case "${package}" in
/*|.*/*|*/*)
dir="${package}"
;;
*)
dir="${package_root}/${package}"
;;
esac
printf '%s\n' "${dir}"
done
return
fi
find "${package_root}" -mindepth 2 -maxdepth 2 -name APKBUILD -print \
| sed 's#/APKBUILD$##' \
| sort
}
apk_validate_package_dir() {
local package_dir="$1"
if [[ ! -f "${package_dir}/APKBUILD" ]]; then
printf 'missing APKBUILD: %s\n' "${package_dir}/APKBUILD" >&2
return 1
fi
}
apk_package_name() {
basename "$1"
}
apk_package_arches() {
local package_dir="$1"
awk -F= '
$1 == "arch" {
gsub(/["'\'']/, "", $2)
gsub(/^[ \t]+|[ \t]+$/, "", $2)
print $2
exit
}
' "${package_dir}/APKBUILD"
}
apk_package_supports_arch() {
local package_dir="$1"
local arch="$2"
local supported_arches
local supported_arch
supported_arches="$(apk_package_arches "${package_dir}")"
for supported_arch in ${supported_arches}; do
case "${supported_arch}" in
all|noarch|"${arch}")
return 0
;;
esac
done
return 1
}
apk_container_package_dir() {
local repo_root="$1"
local package_dir="$2"
case "${package_dir}" in
"${repo_root}"/*)
printf '/work/%s\n' "${package_dir#"${repo_root}/"}"
;;
*)
printf 'package directory must be inside repository: %s\n' "${package_dir}" >&2
return 1
;;
esac
}
apk_run_package_hook() {
local repo_root="$1"
local package_dir="$2"
local hook_name="$3"
shift 3
local package_name
local hook_path
package_name="$(apk_package_name "${package_dir}")"
hook_path="${package_dir}/scripts/${hook_name}.sh"
if [[ ! -e "${hook_path}" ]]; then
printf 'Skipping %s: no %s hook\n' "${package_name}" "${hook_name}"
return
fi
if [[ ! -x "${hook_path}" ]]; then
printf 'package hook is not executable: %s\n' "${hook_path}" >&2
return 1
fi
printf 'Running %s hook for %s\n' "${hook_name}" "${package_name}"
REPO_ROOT="${repo_root}" \
PACKAGE_DIR="${package_dir}" \
PACKAGE_NAME="${package_name}" \
"${hook_path}" "$@"
}

10
scripts/apk/test-install.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
. "${repo_root}/scripts/apk/package-lib.sh"
while IFS= read -r package_dir; do
apk_validate_package_dir "${package_dir}"
apk_run_package_hook "${repo_root}" "${package_dir}" test-install
done < <(apk_package_dirs "${repo_root}")

View File

@@ -2,14 +2,9 @@
set -euo pipefail set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
package_dir="${repo_root}/packaging/alpine/local/seaweedfs" . "${repo_root}/scripts/apk/package-lib.sh"
configs=(credential filer master notification replication security shell)
for config in "${configs[@]}"; do while IFS= read -r package_dir; do
"${repo_root}/bin/weed" scaffold -config "${config}" 2>/dev/null \ apk_validate_package_dir "${package_dir}"
> "${package_dir}/example-${config}.toml" apk_run_package_hook "${repo_root}" "${package_dir}" update-generated-sources
done done < <(apk_package_dirs "${repo_root}")
"${repo_root}/bin/weed" autocomplete bash 2>/dev/null \
| sed -E 's#complete -C "?[^"]*/weed"? weed#complete -C /usr/bin/weed weed#' \
> "${package_dir}/weed.bash-completion"