mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-21 06:36:54 +00:00
Every workflow that builds the Rust volume server first installed protoc from a package manager - twelve steps across apt, brew and choco. That is 37s per job on a good day, and this week archive.ubuntu.com stalled long enough for four jobs to burn their whole timeout without reaching a build. protoc-bin-vendored ships the compiler as a build-dependency, so it now arrives through the cargo registry the workflows already cache and there is nothing left to install. cargo build works on a machine with no protoc at all, which is worth as much locally as it is in CI. It also pins the version. The apt protoc on ubuntu-22.04 is 3.12, old enough to reject proto3 optional, which is why build.rs passes --experimental_allow_proto3_optional; the vendored one is 31.1. The flag stays, since it costs nothing and keeps a build against an older PROTOC working, and an explicit PROTOC still overrides the vendored binary for packagers who supply their own.
507 lines
20 KiB
YAML
507 lines
20 KiB
YAML
name: "docker: build latest container"
|
|
|
|
# Manual fallback only. On tag push, container_release_unified.yml already
|
|
# re-tags the released versioned image as `latest` / `latest_large_disk`,
|
|
# so a full rebuild here is unnecessary. Run this manually if you need to
|
|
# rebuild `latest` from an arbitrary ref.
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
source_ref:
|
|
description: 'Git ref to build (branch, tag, or commit SHA)'
|
|
required: true
|
|
default: 'master'
|
|
image_tag:
|
|
description: 'Docker tag to publish (without variant suffix)'
|
|
required: true
|
|
default: 'latest'
|
|
variant:
|
|
description: 'Variant to build manually'
|
|
required: true
|
|
type: choice
|
|
default: all
|
|
options:
|
|
- all
|
|
- standard
|
|
- large_disk
|
|
publish:
|
|
description: 'Publish images and manifests'
|
|
required: true
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
contents: read
|
|
security-events: write
|
|
|
|
jobs:
|
|
setup:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
variants: ${{ steps.set-variants.outputs.variants }}
|
|
publish: ${{ steps.set-publish.outputs.publish }}
|
|
steps:
|
|
- name: Select variants for this run
|
|
id: set-variants
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ "${{ github.event.inputs.variant }}" != "all" ]; then
|
|
variants="[\"${{ github.event.inputs.variant }}\"]"
|
|
else
|
|
variants='["standard","large_disk"]'
|
|
fi
|
|
echo "variants=$variants" >> "$GITHUB_OUTPUT"
|
|
- name: Select publish mode
|
|
id: set-publish
|
|
run: |
|
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
echo "publish=${{ github.event.inputs.publish }}" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "publish=true" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
# ── Pre-build Rust volume server binaries natively ──────────────────
|
|
build-rust-binaries:
|
|
runs-on: ubuntu-22.04
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- target: x86_64-unknown-linux-musl
|
|
arch: amd64
|
|
- target: aarch64-unknown-linux-musl
|
|
arch: arm64
|
|
cross: true
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref || github.ref }}
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
targets: ${{ matrix.target }}
|
|
|
|
- name: Install musl tools (amd64)
|
|
if: ${{ !matrix.cross }}
|
|
run: sudo apt-get install -y musl-tools
|
|
|
|
- name: Install cross-compilation tools (arm64)
|
|
if: matrix.cross
|
|
run: |
|
|
sudo apt-get install -y gcc-aarch64-linux-gnu
|
|
echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV"
|
|
# Disable glibc fortify source — its __memcpy_chk etc. symbols don't exist in musl
|
|
echo "CFLAGS_aarch64_unknown_linux_musl=-U_FORTIFY_SOURCE" >> "$GITHUB_ENV"
|
|
|
|
- name: Cache cargo registry and target
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
seaweed-volume/target
|
|
key: rust-docker-${{ matrix.target }}-${{ hashFiles('seaweed-volume/Cargo.lock') }}
|
|
restore-keys: |
|
|
rust-docker-${{ matrix.target }}-
|
|
|
|
- name: Build large-disk variant
|
|
env:
|
|
SEAWEEDFS_COMMIT: ${{ github.sha }}
|
|
run: |
|
|
cd seaweed-volume
|
|
cargo build --release --target ${{ matrix.target }}
|
|
cp target/${{ matrix.target }}/release/weed-volume ../weed-volume-large-disk-${{ matrix.arch }}
|
|
|
|
- name: Build normal variant
|
|
env:
|
|
SEAWEEDFS_COMMIT: ${{ github.sha }}
|
|
run: |
|
|
cd seaweed-volume
|
|
cargo build --release --target ${{ matrix.target }} --no-default-features
|
|
cp target/${{ matrix.target }}/release/weed-volume ../weed-volume-normal-${{ matrix.arch }}
|
|
|
|
- name: Upload artifacts
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: rust-volume-${{ matrix.arch }}
|
|
path: |
|
|
weed-volume-large-disk-${{ matrix.arch }}
|
|
weed-volume-normal-${{ matrix.arch }}
|
|
|
|
build:
|
|
needs: [setup, build-rust-binaries]
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
platform: [amd64, arm64, arm, 386]
|
|
variant: ${{ fromJSON(needs.setup.outputs.variants) }}
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref || github.ref }}
|
|
- name: Free Disk Space
|
|
run: |
|
|
echo "Available disk space before cleanup:"
|
|
df -h
|
|
# Remove pre-installed tools
|
|
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
|
|
# Clean package managers
|
|
sudo apt-get clean
|
|
sudo rm -rf /var/lib/apt/lists/*
|
|
# Clean Docker aggressively
|
|
sudo docker system prune -af --volumes
|
|
# Clean Go cache if it exists
|
|
[ -d ~/.cache/go-build ] && rm -rf ~/.cache/go-build || true
|
|
[ -d /go/pkg ] && rm -rf /go/pkg || true
|
|
echo "Available disk space after cleanup:"
|
|
df -h
|
|
|
|
- name: Configure variant
|
|
id: config
|
|
run: |
|
|
if [ "${{ matrix.variant }}" == "large_disk" ]; then
|
|
echo "tag_suffix=_large_disk" >> $GITHUB_OUTPUT
|
|
echo "build_args=TAGS=5BytesOffset" >> $GITHUB_OUTPUT
|
|
echo "rust_variant=large-disk" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "tag_suffix=" >> $GITHUB_OUTPUT
|
|
echo "build_args=" >> $GITHUB_OUTPUT
|
|
echo "rust_variant=normal" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Download pre-built Rust binaries
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: rust-volume-*
|
|
merge-multiple: true
|
|
path: ./rust-bins
|
|
|
|
- name: Place Rust binaries in Docker context
|
|
run: |
|
|
mkdir -p docker/weed-volume-prebuilt
|
|
for arch in amd64 arm64; do
|
|
src="./rust-bins/weed-volume-${{ steps.config.outputs.rust_variant }}-${arch}"
|
|
if [ -f "$src" ]; then
|
|
cp "$src" "docker/weed-volume-prebuilt/weed-volume-${arch}"
|
|
echo "Placed pre-built Rust binary for ${arch}"
|
|
fi
|
|
done
|
|
ls -la docker/weed-volume-prebuilt/
|
|
|
|
- name: Docker meta
|
|
id: docker_meta
|
|
uses: docker/metadata-action@v6
|
|
with:
|
|
images: |
|
|
chrislusf/seaweedfs
|
|
ghcr.io/chrislusf/seaweedfs
|
|
tags: type=raw,value=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag || 'latest' }},suffix=${{ steps.config.outputs.tag_suffix }}
|
|
labels: |
|
|
org.opencontainers.image.title=seaweedfs
|
|
org.opencontainers.image.description=SeaweedFS is a distributed storage system for blobs, objects, files, and data lake, to store and serve billions of files fast!
|
|
org.opencontainers.image.vendor=Chris Lu
|
|
- name: Set up QEMU
|
|
if: matrix.platform != 'amd64'
|
|
uses: docker/setup-qemu-action@v4.2.0
|
|
- name: Create BuildKit config
|
|
run: |
|
|
cat > /tmp/buildkitd.toml <<EOF
|
|
[registry."docker.io"]
|
|
mirrors = ["https://mirror.gcr.io"]
|
|
EOF
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v4
|
|
with:
|
|
buildkitd-flags: "--debug"
|
|
buildkitd-config: /tmp/buildkitd.toml
|
|
- name: Login to Docker Hub
|
|
if: needs.setup.outputs.publish == 'true'
|
|
uses: docker/login-action@v4.6.0
|
|
with:
|
|
username: ${{ secrets.DOCKER_USERNAME }}
|
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
- name: Login to GHCR
|
|
if: needs.setup.outputs.publish == 'true'
|
|
uses: docker/login-action@v4.6.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ secrets.GHCR_USERNAME }}
|
|
password: ${{ secrets.GHCR_TOKEN }}
|
|
- name: Build ${{ matrix.platform }} ${{ matrix.variant }}
|
|
uses: docker/build-push-action@v7
|
|
env:
|
|
DOCKER_BUILDKIT: 1
|
|
with:
|
|
context: ./docker
|
|
push: ${{ needs.setup.outputs.publish == 'true' }}
|
|
file: ./docker/Dockerfile.go_build
|
|
platforms: linux/${{ matrix.platform }}
|
|
# Push to GHCR only during build to avoid Docker Hub rate limits
|
|
tags: ghcr.io/chrislusf/seaweedfs:${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag || 'latest' }}${{ steps.config.outputs.tag_suffix }}-${{ matrix.platform }}
|
|
labels: ${{ steps.docker_meta.outputs.labels }}
|
|
cache-from: type=gha,scope=${{ matrix.variant }}-${{ matrix.platform }}
|
|
cache-to: type=gha,mode=max,scope=${{ matrix.variant }}-${{ matrix.platform }}
|
|
build-args: |
|
|
BUILDKIT_INLINE_CACHE=1
|
|
BRANCH=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref || github.sha }}
|
|
${{ steps.config.outputs.build_args }}
|
|
- name: Clean up build artifacts
|
|
if: always()
|
|
run: |
|
|
# Clean up Docker build cache and temporary files
|
|
sudo docker system prune -f
|
|
# Remove Go build cache
|
|
sudo rm -rf /tmp/go-build*
|
|
|
|
trivy-scan:
|
|
runs-on: ubuntu-latest
|
|
needs: [setup, build, build-rust-binaries]
|
|
strategy:
|
|
matrix:
|
|
variant: ${{ fromJSON(needs.setup.outputs.variants) }}
|
|
steps:
|
|
- name: Configure variant
|
|
id: config
|
|
run: |
|
|
if [ "${{ matrix.variant }}" == "large_disk" ]; then
|
|
echo "tag_suffix=_large_disk" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "tag_suffix=" >> $GITHUB_OUTPUT
|
|
fi
|
|
- name: Login to GHCR
|
|
if: needs.setup.outputs.publish == 'true'
|
|
uses: docker/login-action@v4.6.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ secrets.GHCR_USERNAME }}
|
|
password: ${{ secrets.GHCR_TOKEN }}
|
|
- name: Checkout for local scan build
|
|
if: needs.setup.outputs.publish != 'true'
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref || github.ref }}
|
|
- name: Download pre-built Rust binaries for local scan
|
|
if: needs.setup.outputs.publish != 'true'
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: rust-volume-*
|
|
merge-multiple: true
|
|
path: ./rust-bins
|
|
- name: Place Rust binaries in Docker context for local scan
|
|
if: needs.setup.outputs.publish != 'true'
|
|
run: |
|
|
rust_variant="normal"
|
|
if [ "${{ matrix.variant }}" == "large_disk" ]; then
|
|
rust_variant="large-disk"
|
|
fi
|
|
mkdir -p docker/weed-volume-prebuilt
|
|
for arch in amd64 arm64; do
|
|
src="./rust-bins/weed-volume-${rust_variant}-${arch}"
|
|
if [ -f "$src" ]; then
|
|
cp "$src" "docker/weed-volume-prebuilt/weed-volume-${arch}"
|
|
echo "Placed pre-built Rust binary for ${arch}"
|
|
fi
|
|
done
|
|
ls -la docker/weed-volume-prebuilt/
|
|
- name: Create BuildKit config for local scan build
|
|
if: needs.setup.outputs.publish != 'true'
|
|
run: |
|
|
cat > /tmp/buildkitd.toml <<EOF
|
|
[registry."docker.io"]
|
|
mirrors = ["https://mirror.gcr.io"]
|
|
EOF
|
|
- name: Set up Docker Buildx for local scan build
|
|
if: needs.setup.outputs.publish != 'true'
|
|
uses: docker/setup-buildx-action@v4
|
|
with:
|
|
buildkitd-flags: "--debug"
|
|
buildkitd-config: /tmp/buildkitd.toml
|
|
- name: Build local scan image tarball
|
|
if: needs.setup.outputs.publish != 'true'
|
|
uses: docker/build-push-action@v7
|
|
env:
|
|
DOCKER_BUILDKIT: 1
|
|
with:
|
|
context: ./docker
|
|
file: ./docker/Dockerfile.go_build
|
|
platforms: linux/amd64
|
|
outputs: type=docker,dest=/tmp/seaweedfs${{ steps.config.outputs.tag_suffix }}-amd64.tar
|
|
build-args: |
|
|
BUILDKIT_INLINE_CACHE=1
|
|
BRANCH=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref || github.sha }}
|
|
${{ matrix.variant == 'large_disk' && 'TAGS=5BytesOffset' || '' }}
|
|
- name: Trivy report (published image)
|
|
if: needs.setup.outputs.publish == 'true'
|
|
# Pin to SHA - mutable tags were compromised (GHSA-69fq-xp46-6x23)
|
|
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
with:
|
|
scan-type: image
|
|
# Scan amd64 only - OS packages are identical across architectures
|
|
# since they all use the same alpine base, so a single-arch scan
|
|
# provides sufficient coverage without multiplying CI time.
|
|
image-ref: ghcr.io/chrislusf/seaweedfs:${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag || 'latest' }}${{ steps.config.outputs.tag_suffix }}-amd64
|
|
scanners: vuln
|
|
vuln-type: os,library
|
|
severity: HIGH,CRITICAL
|
|
ignore-unfixed: true
|
|
limit-severities-for-sarif: true
|
|
format: sarif
|
|
output: trivy-results.sarif
|
|
exit-code: '0'
|
|
- name: Trivy report (local tarball)
|
|
if: needs.setup.outputs.publish != 'true'
|
|
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
with:
|
|
input: /tmp/seaweedfs${{ steps.config.outputs.tag_suffix }}-amd64.tar
|
|
scanners: vuln
|
|
vuln-type: os,library
|
|
severity: HIGH,CRITICAL
|
|
ignore-unfixed: true
|
|
limit-severities-for-sarif: true
|
|
format: sarif
|
|
output: trivy-results.sarif
|
|
exit-code: '0'
|
|
- name: Upload Trivy scan results to GitHub Security
|
|
uses: github/codeql-action/upload-sarif@v4.37.6
|
|
if: always()
|
|
with:
|
|
sarif_file: trivy-results.sarif
|
|
- name: Trivy gate (published image)
|
|
if: needs.setup.outputs.publish == 'true'
|
|
# Gate only on fixable high/critical vulnerabilities. Non-fixable
|
|
# findings are still visible in the SARIF upload above.
|
|
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
with:
|
|
scan-type: image
|
|
image-ref: ghcr.io/chrislusf/seaweedfs:${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag || 'latest' }}${{ steps.config.outputs.tag_suffix }}-amd64
|
|
scanners: vuln
|
|
vuln-type: os,library
|
|
severity: HIGH,CRITICAL
|
|
ignore-unfixed: true
|
|
format: table
|
|
exit-code: '1'
|
|
skip-setup-trivy: true
|
|
- name: Trivy gate (local tarball)
|
|
if: needs.setup.outputs.publish != 'true'
|
|
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
|
|
with:
|
|
input: /tmp/seaweedfs${{ steps.config.outputs.tag_suffix }}-amd64.tar
|
|
scanners: vuln
|
|
vuln-type: os,library
|
|
severity: HIGH,CRITICAL
|
|
ignore-unfixed: true
|
|
format: table
|
|
exit-code: '1'
|
|
skip-setup-trivy: true
|
|
|
|
create-manifest:
|
|
runs-on: ubuntu-latest
|
|
needs: [setup, build, trivy-scan]
|
|
if: needs.setup.outputs.publish == 'true' && github.event_name != 'pull_request'
|
|
strategy:
|
|
matrix:
|
|
variant: ${{ fromJSON(needs.setup.outputs.variants) }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v7
|
|
with:
|
|
ref: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.source_ref || github.ref }}
|
|
|
|
- name: Configure variant
|
|
id: config
|
|
run: |
|
|
if [ "${{ matrix.variant }}" == "large_disk" ]; then
|
|
echo "tag_suffix=_large_disk" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "tag_suffix=" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Docker meta
|
|
id: docker_meta
|
|
uses: docker/metadata-action@v6
|
|
with:
|
|
images: |
|
|
chrislusf/seaweedfs
|
|
ghcr.io/chrislusf/seaweedfs
|
|
tags: type=raw,value=${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag || 'latest' }},suffix=${{ steps.config.outputs.tag_suffix }}
|
|
- name: Login to Docker Hub
|
|
uses: docker/login-action@v4.6.0
|
|
with:
|
|
username: ${{ secrets.DOCKER_USERNAME }}
|
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
- name: Login to GHCR
|
|
uses: docker/login-action@v4.6.0
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ secrets.GHCR_USERNAME }}
|
|
password: ${{ secrets.GHCR_TOKEN }}
|
|
- name: Install crane
|
|
run: |
|
|
# Install crane for efficient multi-arch image copying
|
|
cd $(mktemp -d)
|
|
curl -sL "https://github.com/google/go-containerregistry/releases/latest/download/go-containerregistry_Linux_x86_64.tar.gz" | tar xz
|
|
sudo mv crane /usr/local/bin/
|
|
crane version
|
|
- name: Create and push manifest
|
|
run: |
|
|
SUFFIX="${{ steps.config.outputs.tag_suffix }}"
|
|
BASE_TAG="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.image_tag || 'latest' }}"
|
|
|
|
# Create manifest on GHCR first (no rate limits)
|
|
echo "Creating GHCR manifest (no rate limits)..."
|
|
docker buildx imagetools create -t ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX} \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-amd64 \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-arm64 \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-arm \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-386
|
|
|
|
# Copy the complete multi-arch image from GHCR to Docker Hub
|
|
# This only requires one pull from GHCR (no rate limit) and one push to Docker Hub
|
|
echo "Copying manifest from GHCR to Docker Hub..."
|
|
|
|
# Function to retry with exponential backoff for Docker Hub operations
|
|
retry_with_backoff() {
|
|
local max_attempts=5
|
|
local timeout=1
|
|
local attempt=1
|
|
local exit_code=0
|
|
|
|
while [ $attempt -le $max_attempts ]; do
|
|
if "$@"; then
|
|
return 0
|
|
else
|
|
exit_code=$?
|
|
fi
|
|
|
|
if [ $attempt -lt $max_attempts ]; then
|
|
echo "Attempt $attempt failed. Retrying in ${timeout}s..." >&2
|
|
sleep $timeout
|
|
timeout=$((timeout * 2))
|
|
fi
|
|
|
|
attempt=$((attempt + 1))
|
|
done
|
|
|
|
echo "Command failed after $max_attempts attempts" >&2
|
|
return $exit_code
|
|
}
|
|
|
|
# Use crane or skopeo to copy, fallback to docker if not available
|
|
if command -v crane &> /dev/null; then
|
|
echo "Using crane to copy..."
|
|
retry_with_backoff crane copy ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX} chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}
|
|
elif command -v skopeo &> /dev/null; then
|
|
echo "Using skopeo to copy..."
|
|
retry_with_backoff skopeo copy --all docker://ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX} docker://chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}
|
|
else
|
|
echo "Using docker buildx imagetools (pulling 4 images from Docker Hub)..."
|
|
# Fallback: create manifest directly on Docker Hub (pulls from Docker Hub - rate limited)
|
|
retry_with_backoff docker buildx imagetools create -t chrislusf/seaweedfs:${BASE_TAG}${SUFFIX} \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-amd64 \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-arm64 \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-arm \
|
|
ghcr.io/chrislusf/seaweedfs:${BASE_TAG}${SUFFIX}-386
|
|
fi
|