ship: mandatory multi-arch pipeline (amd64+arm64), fixed git+docker order
This commit is contained in:
parent
f5b4bbe27e
commit
f51af5faf8
|
|
@ -1,5 +0,0 @@
|
||||||
# Copy to .env, set values, and never commit .env.
|
|
||||||
# Used by archive/docker-compose-macmini.yml and archive/docker-compose-logos.yml for tunnel HTTP Basic auth.
|
|
||||||
|
|
||||||
TUNNEL_AUTH_USER=
|
|
||||||
TUNNEL_AUTH_PASS=
|
|
||||||
|
|
@ -18,3 +18,4 @@ __pycache__/
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
archive/
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,12 +0,0 @@
|
||||||
services:
|
|
||||||
tunnel-server:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
target: server
|
|
||||||
image: git.nixc.us/colin/better-argo-tunnels:production
|
|
||||||
|
|
||||||
tunnel-client:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
target: client
|
|
||||||
image: git.nixc.us/colin/better-argo-tunnels:client-production
|
|
||||||
89
ship.sh
89
ship.sh
|
|
@ -1,8 +1,17 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Reliable release: commit → local binaries → multi-arch images → GHCR → git push.
|
|
||||||
# Only input: commit message. Edit CONSTANTS to change registry/platforms.
|
|
||||||
#
|
#
|
||||||
# Usage: ./ship.sh "your message"
|
# Fixed pipeline (do not reorder or skip steps):
|
||||||
|
# 1. git add -A
|
||||||
|
# 2. git commit (normal if staged changes exist, else --allow-empty with the same message)
|
||||||
|
# 3. GIT_COMMIT = current HEAD (baked into Go binaries inside each image via Dockerfile ARG)
|
||||||
|
# 4. docker login ghcr.io
|
||||||
|
# 5. For each of server + client targets: per-arch buildx --push (linux/amd64, linux/arm64), then imagetools manifest
|
||||||
|
# 6. git push GIT_REMOTE BRANCH
|
||||||
|
#
|
||||||
|
# Usage: ./ship.sh <commit message>
|
||||||
|
# Requires: git, docker (buildx), gh; remote "github"; gh token with write:packages for GHCR.
|
||||||
|
#
|
||||||
|
# Multi-arch: Intel/AMD 64-bit (linux/amd64) and ARM64 (linux/arm64), one manifest tag each for server and client.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
@ -15,47 +24,60 @@ COMMIT_MSG="$*"
|
||||||
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
ROOT="$(cd "$(dirname "$0")" && pwd)"
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
|
|
||||||
# --- CONSTANTS ---
|
# --- release targets (edit only here) ---
|
||||||
GITHUB_OWNER="Leopere"
|
GITHUB_OWNER="Leopere"
|
||||||
GITHUB_REPO="better-argo-tunnels"
|
|
||||||
REGISTRY_BASE="ghcr.io/leopere/better-argo-tunnels"
|
REGISTRY_BASE="ghcr.io/leopere/better-argo-tunnels"
|
||||||
TAG_SERVER="production"
|
TAG_SERVER="production"
|
||||||
TAG_CLIENT="client-production"
|
TAG_CLIENT="client-production"
|
||||||
PLATFORMS="linux/amd64,linux/arm64,linux/arm/v7"
|
# Exactly two platforms → one multi-arch manifest per image (amd64 + arm64).
|
||||||
|
PLATFORMS="linux/amd64,linux/arm64"
|
||||||
GIT_REMOTE="github"
|
GIT_REMOTE="github"
|
||||||
# --- end ---
|
# --- end ---
|
||||||
|
|
||||||
for cmd in gh docker git go; do
|
for cmd in git docker gh; do
|
||||||
command -v "$cmd" >/dev/null 2>&1 || {
|
command -v "$cmd" >/dev/null 2>&1 || {
|
||||||
echo "error: need $cmd in PATH" >&2
|
echo "ship: fatal: missing required command: $cmd" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
done
|
done
|
||||||
|
if ! docker buildx version >/dev/null 2>&1; then
|
||||||
|
echo "ship: fatal: docker buildx not available" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
BRANCH="$(git branch --show-current)"
|
BRANCH="$(git branch --show-current)"
|
||||||
if [[ -z "$BRANCH" ]]; then
|
if [[ -z "$BRANCH" ]]; then
|
||||||
echo "error: detached HEAD" >&2
|
echo "ship: fatal: detached HEAD" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! git remote get-url "$GIT_REMOTE" >/dev/null 2>&1; then
|
||||||
|
echo "ship: fatal: git remote '$GIT_REMOTE' is not configured" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "==> git add -A"
|
echo "==> [1/6] git add -A"
|
||||||
git add -A
|
git add -A
|
||||||
if git diff --cached --quiet; then
|
|
||||||
echo "error: nothing to commit" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> git commit"
|
echo "==> [2/6] git commit"
|
||||||
git commit -m "$COMMIT_MSG"
|
if git diff --cached --quiet; then
|
||||||
|
git commit --allow-empty -m "$COMMIT_MSG"
|
||||||
|
else
|
||||||
|
git commit -m "$COMMIT_MSG"
|
||||||
|
fi
|
||||||
|
|
||||||
GIT_COMMIT="$(git rev-parse HEAD)"
|
GIT_COMMIT="$(git rev-parse HEAD)"
|
||||||
BA=(--build-arg "GIT_COMMIT=$GIT_COMMIT")
|
BA=(--build-arg "GIT_COMMIT=${GIT_COMMIT}")
|
||||||
LDFLAGS="-s -w -X github.com/nixc/reverse-ssh-traefik/internal/buildinfo.Commit=${GIT_COMMIT}"
|
echo "==> GIT_COMMIT=${GIT_COMMIT}"
|
||||||
|
|
||||||
echo "==> go build → dist/"
|
echo "==> [3/6] docker login ghcr.io"
|
||||||
mkdir -p dist
|
if ! token="$(gh auth token)" || [[ -z "${token}" ]]; then
|
||||||
CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/tunnel-server ./cmd/server/
|
echo "ship: fatal: gh auth token empty" >&2
|
||||||
CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/tunnel-client ./cmd/client/
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! printf '%s' "$token" | docker login ghcr.io -u "$GITHUB_OWNER" --password-stdin; then
|
||||||
|
echo "ship: fatal: docker login ghcr.io failed (need: gh auth refresh -h github.com -s write:packages -s read:packages)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
pick_builder() {
|
pick_builder() {
|
||||||
if docker buildx inspect orbstack >/dev/null 2>&1; then
|
if docker buildx inspect orbstack >/dev/null 2>&1; then
|
||||||
|
|
@ -71,6 +93,7 @@ platform_slug() {
|
||||||
echo "${p//\//-}"
|
echo "${p//\//-}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Per-arch push then single multi-arch manifest (orbstack/docker driver compatible).
|
||||||
push_multi() {
|
push_multi() {
|
||||||
local target="$1"
|
local target="$1"
|
||||||
local final_tag="$2"
|
local final_tag="$2"
|
||||||
|
|
@ -78,15 +101,15 @@ push_multi() {
|
||||||
local base="${final_tag%:*}"
|
local base="${final_tag%:*}"
|
||||||
local name="${final_tag##*:}"
|
local name="${final_tag##*:}"
|
||||||
local arch_refs=()
|
local arch_refs=()
|
||||||
|
local p slug arch_tag
|
||||||
|
|
||||||
IFS=',' read -ra plats <<< "$PLATFORMS"
|
IFS=',' read -ra plats <<< "$PLATFORMS"
|
||||||
for p in "${plats[@]}"; do
|
for p in "${plats[@]}"; do
|
||||||
p="${p// /}"
|
p="${p// /}"
|
||||||
[[ -z "$p" ]] && continue
|
[[ -z "$p" ]] && continue
|
||||||
local slug
|
|
||||||
slug="$(platform_slug "$p")"
|
slug="$(platform_slug "$p")"
|
||||||
local arch_tag="${base}:${name}-${slug}"
|
arch_tag="${base}:${name}-${slug}"
|
||||||
echo "==> docker buildx $target $p → $arch_tag"
|
echo "==> [4/6] docker buildx push ${target} ${p} -> ${arch_tag}"
|
||||||
docker buildx build \
|
docker buildx build \
|
||||||
"${BA[@]}" \
|
"${BA[@]}" \
|
||||||
--builder "$builder" \
|
--builder "$builder" \
|
||||||
|
|
@ -94,28 +117,24 @@ push_multi() {
|
||||||
--target "$target" \
|
--target "$target" \
|
||||||
-t "$arch_tag" \
|
-t "$arch_tag" \
|
||||||
--push \
|
--push \
|
||||||
|
--provenance=false \
|
||||||
"$ROOT"
|
"$ROOT"
|
||||||
arch_refs+=("$arch_tag")
|
arch_refs+=("$arch_tag")
|
||||||
done
|
done
|
||||||
|
echo "==> [5/6] imagetools manifest ${final_tag}"
|
||||||
docker buildx imagetools create -t "$final_tag" "${arch_refs[@]}"
|
docker buildx imagetools create -t "$final_tag" "${arch_refs[@]}"
|
||||||
}
|
}
|
||||||
|
|
||||||
BUILDER="$(pick_builder)"
|
BUILDER="$(pick_builder)"
|
||||||
|
echo "==> buildx builder: ${BUILDER}"
|
||||||
SERVER_TAG="${REGISTRY_BASE}:${TAG_SERVER}"
|
SERVER_TAG="${REGISTRY_BASE}:${TAG_SERVER}"
|
||||||
CLIENT_TAG="${REGISTRY_BASE}:${TAG_CLIENT}"
|
CLIENT_TAG="${REGISTRY_BASE}:${TAG_CLIENT}"
|
||||||
|
|
||||||
echo "==> ghcr.io login (needs gh token with write:packages)"
|
|
||||||
echo "$(gh auth token)" | docker login ghcr.io -u "$GITHUB_OWNER" --password-stdin
|
|
||||||
|
|
||||||
push_multi server "$SERVER_TAG" "$BUILDER"
|
push_multi server "$SERVER_TAG" "$BUILDER"
|
||||||
push_multi client "$CLIENT_TAG" "$BUILDER"
|
push_multi client "$CLIENT_TAG" "$BUILDER"
|
||||||
|
|
||||||
if ! git remote get-url "$GIT_REMOTE" >/dev/null 2>&1; then
|
echo "==> [6/6] git push ${GIT_REMOTE} ${BRANCH}"
|
||||||
echo "error: git remote '$GIT_REMOTE' missing" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "==> git push $GIT_REMOTE $BRANCH"
|
|
||||||
git push "$GIT_REMOTE" "$BRANCH"
|
git push "$GIT_REMOTE" "$BRANCH"
|
||||||
|
|
||||||
echo "==> ok commit=$GIT_COMMIT"
|
echo "ship: ok commit=${GIT_COMMIT}"
|
||||||
echo " $SERVER_TAG $CLIENT_TAG"
|
echo "ship: images ${SERVER_TAG} ${CLIENT_TAG} (multi-arch: ${PLATFORMS})"
|
||||||
|
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
networks:
|
|
||||||
traefik:
|
|
||||||
external: true
|
|
||||||
|
|
||||||
services:
|
|
||||||
tunnel-server:
|
|
||||||
image: git.nixc.us/colin/better-argo-tunnels:production
|
|
||||||
networks:
|
|
||||||
- traefik
|
|
||||||
environment:
|
|
||||||
SSH_PORT: "2222"
|
|
||||||
PORT_RANGE_START: "10000"
|
|
||||||
PORT_RANGE_END: "10100"
|
|
||||||
TRAEFIK_SSH_HOST: "ingress.nixc.us:65522"
|
|
||||||
TRAEFIK_SSH_USER: "root"
|
|
||||||
TRAEFIK_SSH_KEY: "/keys/deploy_key"
|
|
||||||
SWARM_SERVICE_NAME: "better-argo-tunnels_tunnel-server"
|
|
||||||
TRAEFIK_ENTRYPOINT: "websecure"
|
|
||||||
TRAEFIK_CERT_RESOLVER: "letsencryptresolver"
|
|
||||||
HOSTNAME: "{{.Node.Hostname}}"
|
|
||||||
NODE_ID: "{{.Node.ID}}"
|
|
||||||
SERVICE_NAME: "{{.Service.Name}}"
|
|
||||||
TASK_ID: "{{.Task.ID}}"
|
|
||||||
ENVIRONMENT: "production"
|
|
||||||
volumes:
|
|
||||||
# Tunnel user's keys on ingress.nixc.us (clients connect here; authorized_keys = tunnel user's)
|
|
||||||
- /home/tunnel/.ssh/tunnel_host_key:/keys/host_key:ro
|
|
||||||
- /home/tunnel/.ssh/authorized_keys:/keys/authorized_keys:ro
|
|
||||||
- /home/tunnel/.ssh/ca-userkey:/keys/deploy_key:ro
|
|
||||||
- /home/tunnel/.ssh/ca-userkey-cert.pub:/keys/deploy_key-cert.pub:ro
|
|
||||||
ports:
|
|
||||||
- target: 2222
|
|
||||||
published: 2222
|
|
||||||
protocol: tcp
|
|
||||||
mode: host
|
|
||||||
deploy:
|
|
||||||
replicas: 1
|
|
||||||
placement:
|
|
||||||
constraints:
|
|
||||||
- node.hostname == ingress.nixc.us
|
|
||||||
labels:
|
|
||||||
traefik.enable: "true"
|
|
||||||
traefik.docker.network: "traefik"
|
|
||||||
# Dynamic tunnel labels are added at runtime via docker service update.
|
|
||||||
update_config:
|
|
||||||
order: stop-first
|
|
||||||
failure_action: rollback
|
|
||||||
delay: 0s
|
|
||||||
parallelism: 1
|
|
||||||
restart_policy:
|
|
||||||
condition: on-failure
|
|
||||||
Loading…
Reference in New Issue