forked from snxraven/peardock
Gatekeeper reported the Bare server binary as damaged because it only had a linker ad-hoc stamp after embed. Seal darwin server binaries with the same sign-macos path used for Electron apps (codesign / rcodesign).
259 lines
8.0 KiB
Bash
Executable File
259 lines
8.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build peardock server + client for all 64-bit hosts and publish a rolling Gitea release.
|
|
#
|
|
# Cross-compile (default — single Linux CI runner is enough):
|
|
# Server: bare-build + bare-runtime prebuilds (all hosts)
|
|
# Client: electron-forge --platform/--arch (downloads Electron + native prebuilds)
|
|
#
|
|
# Required:
|
|
# RELEASE_TOKEN — Gitea PAT with repository release write
|
|
# Optional:
|
|
# GITEA_URL / GITEA_OWNER / GITEA_REPO
|
|
# PEARDOCK_SERVER_HOSTS — default: all 64-bit (see scripts/hosts.cjs)
|
|
# PEARDOCK_CLIENT_HOSTS — default: same all 64-bit list
|
|
# PEARDOCK_SKIP_CLIENT=1
|
|
# RELEASE_TAG (default: rolling)
|
|
# DRY_RUN=1 — build + stage only, no upload
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
VERSION="$(node -p "require('./package.json').version")"
|
|
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
SHA="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
|
TAG="${RELEASE_TAG:-rolling}"
|
|
RELEASE_TITLE="${RELEASE_NAME:-peardock rolling}"
|
|
DIST="$ROOT/dist/release"
|
|
DEFAULT_HOSTS="$(node -p "require('./scripts/hosts.cjs').ALL_64.join(',')")"
|
|
|
|
rm -rf "$DIST"
|
|
mkdir -p "$DIST"
|
|
|
|
log() { echo "[release] $*"; }
|
|
|
|
detect_remote() {
|
|
local url
|
|
url="$(git remote get-url origin 2>/dev/null || true)"
|
|
if [[ "$url" =~ git@([^:]+):([^/]+)/([^/.]+) ]]; then
|
|
echo "https://${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"
|
|
elif [[ "$url" =~ https?://([^/]+)/([^/]+)/([^/.]+) ]]; then
|
|
echo "https://${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}" "${BASH_REMATCH[3]}"
|
|
else
|
|
echo "" "" ""
|
|
fi
|
|
}
|
|
|
|
read -r DETECTED_URL DETECTED_OWNER DETECTED_REPO <<<"$(detect_remote)"
|
|
GITEA_URL="${GITEA_URL:-${DETECTED_URL:-}}"
|
|
GITEA_OWNER="${GITEA_OWNER:-${DETECTED_OWNER:-}}"
|
|
GITEA_REPO="${GITEA_REPO:-${DETECTED_REPO:-peardock}}"
|
|
|
|
if [[ -z "${GITEA_URL}" ]]; then
|
|
log "WARN: could not detect GITEA_URL — set GITEA_URL for upload"
|
|
fi
|
|
|
|
export PEARDOCK_SERVER_HOSTS="${PEARDOCK_SERVER_HOSTS:-$DEFAULT_HOSTS}"
|
|
export PEARDOCK_CLIENT_HOSTS="${PEARDOCK_CLIENT_HOSTS:-$DEFAULT_HOSTS}"
|
|
|
|
# Prefer prebuilt natives when electron-forge cross-packages
|
|
export npm_config_build_from_source="${npm_config_build_from_source:-false}"
|
|
# Skip @electron/rebuild during forge package (prebuilds only) — avoids multi-minute "Finalizing"
|
|
export PEARDOCK_SKIP_REBUILD="${PEARDOCK_SKIP_REBUILD:-1}"
|
|
|
|
|
|
# --- server: all hosts (bare cross-compile) ---
|
|
log "building server binaries for: $PEARDOCK_SERVER_HOSTS"
|
|
node scripts/make.cjs server
|
|
|
|
# --- client: all hosts (electron-forge cross package) ---
|
|
if [[ "${PEARDOCK_SKIP_CLIENT:-0}" != "1" ]]; then
|
|
log "building client binaries for: $PEARDOCK_CLIENT_HOSTS"
|
|
node scripts/make.cjs client
|
|
else
|
|
log "skipping client (PEARDOCK_SKIP_CLIENT=1)"
|
|
fi
|
|
|
|
# --- stage artifacts ---
|
|
sha_file() {
|
|
local f="$1"
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum "$f"
|
|
else
|
|
shasum -a 256 "$f"
|
|
fi
|
|
}
|
|
|
|
stage_server() {
|
|
local host="$1"
|
|
local dir="$ROOT/out/peardock-server-$host"
|
|
local bin="peardock-server"
|
|
[[ "$host" == win32-* ]] && bin="peardock-server.exe"
|
|
if [[ ! -d "$dir" ]]; then
|
|
log "WARN: missing server dir $dir"
|
|
return 1
|
|
fi
|
|
if [[ ! -f "$dir/$bin" ]]; then
|
|
local f
|
|
f="$(find "$dir" -maxdepth 2 -type f \( -name 'peardock-server' -o -name 'peardock-server.exe' \) | head -1 || true)"
|
|
if [[ -n "$f" ]]; then
|
|
bin="$(basename "$f")"
|
|
# ensure top-level copy for tarball convenience
|
|
cp -f "$f" "$dir/$bin" 2>/dev/null || true
|
|
else
|
|
log "WARN: no server binary in $dir"
|
|
return 1
|
|
fi
|
|
fi
|
|
local archive="peardock-server-${VERSION}-${host}.tar.gz"
|
|
tar -C "$dir" -czf "$DIST/$archive" .
|
|
(cd "$DIST" && sha_file "$archive" >"${archive}.sha256")
|
|
log "staged $archive"
|
|
}
|
|
|
|
stage_client() {
|
|
local host="$1"
|
|
local dir="$ROOT/out/peardock-$host"
|
|
if [[ ! -d "$dir" ]]; then
|
|
log "WARN: missing client dir $dir"
|
|
return 1
|
|
fi
|
|
local archive="peardock-client-${VERSION}-${host}.tar.gz"
|
|
tar -C "$ROOT/out" -czf "$DIST/$archive" "peardock-$host"
|
|
(cd "$DIST" && sha_file "$archive" >"${archive}.sha256")
|
|
log "staged $archive"
|
|
}
|
|
|
|
SERVER_OK=0
|
|
CLIENT_OK=0
|
|
SERVER_FAIL=0
|
|
CLIENT_FAIL=0
|
|
|
|
IFS=',' read -ra SHOSTS <<<"$PEARDOCK_SERVER_HOSTS"
|
|
for h in "${SHOSTS[@]}"; do
|
|
h="$(echo "$h" | xargs)"
|
|
if stage_server "$h"; then SERVER_OK=$((SERVER_OK + 1)); else SERVER_FAIL=$((SERVER_FAIL + 1)); fi
|
|
done
|
|
|
|
if [[ "${PEARDOCK_SKIP_CLIENT:-0}" != "1" ]]; then
|
|
IFS=',' read -ra CHOSTS <<<"$PEARDOCK_CLIENT_HOSTS"
|
|
for h in "${CHOSTS[@]}"; do
|
|
h="$(echo "$h" | xargs)"
|
|
if stage_client "$h"; then CLIENT_OK=$((CLIENT_OK + 1)); else CLIENT_FAIL=$((CLIENT_FAIL + 1)); fi
|
|
done
|
|
fi
|
|
|
|
cat >"$DIST/RELEASE_NOTES.md" <<EOF
|
|
# peardock ${VERSION} (${TAG})
|
|
|
|
- Commit: \`${SHA}\`
|
|
- Built: ${STAMP}
|
|
- Server archives staged: ${SERVER_OK} (failed: ${SERVER_FAIL})
|
|
- Client archives staged: ${CLIENT_OK} (failed: ${CLIENT_FAIL})
|
|
|
|
## Hosts (64-bit only)
|
|
|
|
\`linux-x64\`, \`linux-arm64\`, \`darwin-x64\`, \`darwin-arm64\`, \`win32-x64\`, \`win32-arm64\`
|
|
|
|
## Server (Bare standalone)
|
|
|
|
\`\`\`bash
|
|
tar -xzf peardock-server-${VERSION}-linux-x64.tar.gz
|
|
./peardock-server # or peardock-server.exe on Windows
|
|
\`\`\`
|
|
|
|
Needs Docker socket access.
|
|
|
|
## Client (Electron)
|
|
|
|
\`\`\`bash
|
|
tar -xzf peardock-client-${VERSION}-darwin-arm64.tar.gz
|
|
open peardock-darwin-arm64/peardock.app # macOS
|
|
# Linux: ./peardock-linux-x64/peardock-client
|
|
# Windows: peardock-win32-x64\\\\peardock-client.exe
|
|
\`\`\`
|
|
|
|
macOS **client** (`.app`) and **server** (`peardock-server`) binaries are codesigned in CI
|
|
(ad-hoc / self-signed via \`rcodesign\` on Linux, or \`codesign\` on macOS). That avoids the
|
|
Gatekeeper "**damaged** and can't be opened" false positive. First open may still need
|
|
right-click → Open (not notarized unless \`MAC_CODESIGN_IDENTITY\` + Apple Developer ID).
|
|
|
|
## Checksums
|
|
|
|
See \`*.sha256\` beside each archive.
|
|
EOF
|
|
|
|
log "artifacts in $DIST:"
|
|
ls -la "$DIST" || true
|
|
log "summary: server ok=${SERVER_OK} fail=${SERVER_FAIL} | client ok=${CLIENT_OK} fail=${CLIENT_FAIL}"
|
|
|
|
if [[ "$SERVER_OK" -eq 0 ]]; then
|
|
log "ERROR: no server artifacts staged"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${DRY_RUN:-0}" == "1" ]]; then
|
|
log "DRY_RUN=1 — skip Gitea upload"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ -z "${RELEASE_TOKEN:-}" ]]; then
|
|
log "ERROR: RELEASE_TOKEN is required (or DRY_RUN=1)"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${GITEA_URL}" || -z "${GITEA_OWNER}" || -z "${GITEA_REPO}" ]]; then
|
|
log "ERROR: GITEA_URL / GITEA_OWNER / GITEA_REPO must be set"
|
|
exit 1
|
|
fi
|
|
|
|
API="${GITEA_URL%/}/api/v1/repos/${GITEA_OWNER}/${GITEA_REPO}"
|
|
AUTH="Authorization: token ${RELEASE_TOKEN}"
|
|
|
|
log "Gitea API: $API tag=$TAG"
|
|
|
|
REL_JSON="$(curl -fsSL -H "$AUTH" "$API/releases/tags/${TAG}" 2>/dev/null || true)"
|
|
if [[ -n "$REL_JSON" ]]; then
|
|
REL_ID="$(node -e "try{const j=JSON.parse(process.argv[1]);console.log(j.id||'')}catch{console.log('')}" "$REL_JSON")"
|
|
if [[ -n "$REL_ID" ]]; then
|
|
log "deleting previous release id=$REL_ID"
|
|
curl -fsSL -X DELETE -H "$AUTH" "$API/releases/$REL_ID" >/dev/null || true
|
|
fi
|
|
fi
|
|
curl -fsSL -X DELETE -H "$AUTH" "$API/tags/${TAG}" >/dev/null 2>&1 || true
|
|
|
|
CREATE_BODY="$(node -e "
|
|
const notes=require('fs').readFileSync(process.argv[1],'utf8');
|
|
console.log(JSON.stringify({
|
|
tag_name: process.argv[2],
|
|
name: process.argv[3],
|
|
body: notes,
|
|
draft: false,
|
|
prerelease: true,
|
|
target_commitish: process.env.GITHUB_SHA || process.env.GITEA_SHA || 'main'
|
|
}));
|
|
" "$DIST/RELEASE_NOTES.md" "$TAG" "${RELEASE_TITLE} ${VERSION} ${SHA}")"
|
|
|
|
CREATE_RESP="$(curl -fsSL -X POST -H "$AUTH" -H 'Content-Type: application/json' \
|
|
-d "$CREATE_BODY" "$API/releases")"
|
|
REL_ID="$(node -e "console.log(JSON.parse(process.argv[1]).id)" "$CREATE_RESP")"
|
|
log "created release id=$REL_ID"
|
|
|
|
shopt -s nullglob
|
|
for f in "$DIST"/*; do
|
|
[[ -f "$f" ]] || continue
|
|
base="$(basename "$f")"
|
|
case "$base" in
|
|
*.tar.gz|*.sha256|*.md) ;;
|
|
*) continue ;;
|
|
esac
|
|
log "upload $base"
|
|
name_q="$(node -e "console.log(encodeURIComponent(process.argv[1]))" "$base")"
|
|
curl -fsSL -X POST -H "$AUTH" \
|
|
-F "attachment=@${f}" \
|
|
"$API/releases/${REL_ID}/assets?name=${name_q}" \
|
|
>/dev/null
|
|
done
|
|
|
|
log "release published: ${GITEA_URL}/${GITEA_OWNER}/${GITEA_REPO}/releases/tag/${TAG}"
|