Publish multi-platform Alpine image
This commit is contained in:
1 parent
b534c13b40
commit
046d5fe11b
5 files changed
+234
-2
No files matched your search
Executable
+137
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${RELEASE_TAG:?RELEASE_TAG is required}"
|
||||
: "${IMAGE_REPOSITORY:?IMAGE_REPOSITORY is required}"
|
||||
|
||||
CRANE_BIN="${CRANE_BIN:-crane}"
|
||||
ALPINE_IMAGE="${ALPINE_IMAGE:-docker.io/library/alpine:latest}"
|
||||
DIST_DIR="${DIST_DIR:-dist}"
|
||||
SOURCE_URL="${SOURCE_URL:-}"
|
||||
REVISION="${REVISION:-$(git rev-parse HEAD)}"
|
||||
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git show -s --format=%ct HEAD)}"
|
||||
|
||||
version="${RELEASE_TAG#v}"
|
||||
if [ -z "$version" ]; then
|
||||
echo "Release tag must contain a version" >&2
|
||||
exit 1
|
||||
fi
|
||||
case "$SOURCE_DATE_EPOCH" in
|
||||
*[!0-9]* | '')
|
||||
echo "SOURCE_DATE_EPOCH must be an integer" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
work_dir="$(mktemp -d /tmp/pixhelf-container.XXXXXX)"
|
||||
cleanup() {
|
||||
case "$work_dir" in
|
||||
/tmp/pixhelf-container.*) rm -rf -- "$work_dir" ;;
|
||||
*) echo "Refusing to remove unexpected path: $work_dir" >&2 ;;
|
||||
esac
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
crane_args=()
|
||||
if [ "${CRANE_INSECURE:-0}" = 1 ]; then
|
||||
crane_args+=(--insecure)
|
||||
fi
|
||||
crane() {
|
||||
"$CRANE_BIN" "${crane_args[@]}" "$@"
|
||||
}
|
||||
|
||||
created="$(date --utc --date="@$SOURCE_DATE_EPOCH" '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
platform_digest=''
|
||||
|
||||
build_platform() {
|
||||
local arch="$1"
|
||||
local binary="$2"
|
||||
local root="$work_dir/root-$arch"
|
||||
local layer="$work_dir/pixhelf-$arch.tar"
|
||||
local platform_ref="$IMAGE_REPOSITORY:$version-$arch"
|
||||
|
||||
test -x "$binary"
|
||||
install -d -m 0755 "$root/usr/local/bin" "$root/gallery"
|
||||
install -d -m 0777 "$root/cache"
|
||||
install -m 0755 "$binary" "$root/usr/local/bin/pixhelf"
|
||||
|
||||
tar \
|
||||
--sort=name \
|
||||
--mtime="@$SOURCE_DATE_EPOCH" \
|
||||
--owner=0 \
|
||||
--group=0 \
|
||||
--numeric-owner \
|
||||
-C "$root" \
|
||||
-cf "$layer" \
|
||||
cache gallery usr
|
||||
|
||||
crane append \
|
||||
--platform "linux/$arch" \
|
||||
--base "$ALPINE_IMAGE" \
|
||||
--new_layer "$layer" \
|
||||
--new_tag "$platform_ref" \
|
||||
--set-base-image-annotations
|
||||
|
||||
crane mutate "$platform_ref" \
|
||||
--platform "linux/$arch" \
|
||||
--set-platform "linux/$arch" \
|
||||
--entrypoint=/usr/local/bin/pixhelf \
|
||||
--cmd=--gallery-dir \
|
||||
--cmd=/gallery \
|
||||
--cmd=--cache-dir \
|
||||
--cmd=/cache \
|
||||
--cmd=--listen \
|
||||
--cmd=0.0.0.0:3002 \
|
||||
--user=65532:65532 \
|
||||
--workdir=/ \
|
||||
--exposed-ports=3002/tcp \
|
||||
--label=org.opencontainers.image.title=Pixhelf \
|
||||
--label="org.opencontainers.image.description=Self-hosted image gallery" \
|
||||
--label="org.opencontainers.image.version=$version" \
|
||||
--label="org.opencontainers.image.revision=$REVISION" \
|
||||
--label="org.opencontainers.image.created=$created" \
|
||||
--label="org.opencontainers.image.source=$SOURCE_URL" \
|
||||
--tag "$platform_ref"
|
||||
|
||||
platform_digest="$(crane digest "$platform_ref")"
|
||||
echo "Published linux/$arch as $platform_ref@$platform_digest"
|
||||
}
|
||||
|
||||
build_platform amd64 "$DIST_DIR/pixhelf-amd64-linux"
|
||||
amd64_digest="$platform_digest"
|
||||
build_platform arm64 "$DIST_DIR/pixhelf-arm64-linux"
|
||||
arm64_digest="$platform_digest"
|
||||
|
||||
canonical_ref="$IMAGE_REPOSITORY:$version"
|
||||
crane index append \
|
||||
--manifest "$IMAGE_REPOSITORY@$amd64_digest" \
|
||||
--manifest "$IMAGE_REPOSITORY@$arm64_digest" \
|
||||
--tag "$canonical_ref"
|
||||
|
||||
if [ "$RELEASE_TAG" != "$version" ]; then
|
||||
crane tag "$canonical_ref" "$RELEASE_TAG"
|
||||
fi
|
||||
case "$version" in
|
||||
*-*) echo "Prerelease detected; latest tag was not updated" ;;
|
||||
*) crane tag "$canonical_ref" latest ;;
|
||||
esac
|
||||
|
||||
crane manifest "$canonical_ref" | node -e '
|
||||
let input = "";
|
||||
process.stdin.on("data", (chunk) => input += chunk);
|
||||
process.stdin.on("end", () => {
|
||||
const index = JSON.parse(input);
|
||||
const platforms = new Set((index.manifests ?? []).map(
|
||||
(manifest) => `${manifest.platform?.os}/${manifest.platform?.architecture}`,
|
||||
));
|
||||
for (const expected of ["linux/amd64", "linux/arm64"]) {
|
||||
if (!platforms.has(expected)) {
|
||||
throw new Error(`Published image is missing ${expected}`);
|
||||
}
|
||||
}
|
||||
console.log(`Verified multi-platform image: ${[...platforms].sort().join(", ")}`);
|
||||
});
|
||||
'
|
||||
|
||||
echo "Published $canonical_ref"
|
||||
@@ -186,7 +186,7 @@ jobs:
|
||||
delete_asset pixhelf-amd64
|
||||
delete_asset pixhelf-arm64
|
||||
|
||||
for asset in dist/pixhelf-amd64-linux dist/pixhelf-arm64-linux; do
|
||||
for asset in dist/pixhelf-amd64-linux dist/pixhelf-arm64-linux compose.yml; do
|
||||
asset_name="$(basename "$asset")"
|
||||
delete_asset "$asset_name"
|
||||
curl --silent --show-error --fail \
|
||||
@@ -197,3 +197,46 @@ jobs:
|
||||
"${api}/repos/${repository}/releases/${release_id}/assets?name=${asset_name}"
|
||||
printf '\n'
|
||||
done
|
||||
|
||||
- name: Install container publishing tool
|
||||
if: startsWith(forgejo.ref, 'refs/tags/v')
|
||||
run: |
|
||||
set -eu
|
||||
crane_version=0.21.9
|
||||
crane_archive="go-containerregistry_Linux_x86_64.tar.gz"
|
||||
crane_sha256=5c16d8ddb971cb1d5e6ed8b1e743da8224414eeba2c2762d8f1a61b2f095699e
|
||||
crane_url="https://github.com/google/go-containerregistry/releases/download/v${crane_version}/${crane_archive}"
|
||||
|
||||
curl --location --silent --show-error --fail \
|
||||
--output "/tmp/${crane_archive}" \
|
||||
"$crane_url"
|
||||
printf '%s %s\n' "$crane_sha256" "/tmp/${crane_archive}" \
|
||||
| sha256sum --check
|
||||
tar -xzf "/tmp/${crane_archive}" -C /usr/local/bin crane
|
||||
crane version
|
||||
|
||||
- name: Publish multi-platform Alpine container
|
||||
if: startsWith(forgejo.ref, 'refs/tags/v')
|
||||
run: |
|
||||
set -eu
|
||||
token="${FORGEJO_TOKEN:-${GITHUB_TOKEN:-}}"
|
||||
actor="${FORGEJO_ACTOR:-${GITHUB_ACTOR}}"
|
||||
server="${FORGEJO_SERVER_URL:-${GITHUB_SERVER_URL}}"
|
||||
repository="${FORGEJO_REPOSITORY:-${GITHUB_REPOSITORY}}"
|
||||
registry="${server#*://}"
|
||||
registry="${registry%%/*}"
|
||||
image_path="$(printf '%s' "$repository" | tr '[:upper:]' '[:lower:]')"
|
||||
image_repository="${registry}/${image_path}"
|
||||
test -n "$token"
|
||||
|
||||
export DOCKER_CONFIG=/tmp/pixhelf-container-auth
|
||||
install -d -m 0700 "$DOCKER_CONFIG"
|
||||
printf '%s' "$token" \
|
||||
| crane auth login "$registry" --username "$actor" --password-stdin
|
||||
|
||||
export RELEASE_TAG
|
||||
export IMAGE_REPOSITORY="$image_repository"
|
||||
export SOURCE_URL="${server}/${repository}"
|
||||
export REVISION
|
||||
REVISION="$(git rev-parse HEAD)"
|
||||
.forgejo/scripts/publish-container.sh
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
/pic/
|
||||
/.pixhelf-cache/
|
||||
/target/
|
||||
/dist/
|
||||
/frontend/node_modules/
|
||||
/frontend/dist/
|
||||
*.log
|
||||
|
||||
@@ -37,6 +37,33 @@ cargo build --release
|
||||
两个 Release 产物都是无扩展名的独立可执行文件,下载后需要执行
|
||||
`chmod +x pixhelf-amd64-linux` 或 `chmod +x pixhelf-arm64-linux`。
|
||||
|
||||
## Docker Compose
|
||||
|
||||
发布工作流还会将同一批 AMD64、ARM64 二进制分别装入 `alpine:latest`,并发布为
|
||||
`git.pixhelf.com/adminroot/pixhelf` 多架构镜像。最简安装方式:
|
||||
|
||||
```bash
|
||||
curl -LO https://git.pixhelf.com/adminroot/pixhelf/raw/branch/master/compose.yml
|
||||
mkdir -p pic
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
打开 `http://localhost:3002`。`compose.yml` 默认只读挂载当前目录的 `./pic`,并用
|
||||
Docker volume 保存缩略图缓存。更新时执行:
|
||||
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
默认使用 `latest`,也可以固定版本:
|
||||
|
||||
```bash
|
||||
PIXHELF_IMAGE=git.pixhelf.com/adminroot/pixhelf:0.1.7 docker compose up -d
|
||||
```
|
||||
|
||||
容器以 UID/GID `65532:65532` 运行,因此图库目录和图片需要对该用户可读。
|
||||
|
||||
## 参数
|
||||
|
||||
```text
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
services:
|
||||
pixhelf:
|
||||
image: ${PIXHELF_IMAGE:-git.pixhelf.com/adminroot/pixhelf:latest}
|
||||
pull_policy: always
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
ports:
|
||||
- "${PIXHELF_PORT:-3002}:3002"
|
||||
volumes:
|
||||
- "${PIXHELF_GALLERY:-./pic}:/gallery:ro"
|
||||
- pixhelf-cache:/cache
|
||||
read_only: true
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "-", "http://127.0.0.1:3002/api/health"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
pixhelf-cache:
|
||||
Reference in new issue
Block a user