The first demo sealed the office cameras into the package and ran the PC standalone - a copy of the product with no head office. The bundle can now carry an installation code instead: setup redeems it exactly as the app's Setup screen does, the PC joins the shop, and its cameras arrive from head office on the first sync. The demo then IS the product - login, Loya, head office - not a local imitation of it. The code is single-use, so one bundle is one install. release.sh ships the bundle with DEMO_PACK=. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
78 lines
4.6 KiB
Bash
Executable File
78 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build the Windows shop-PC package and publish it as a Gitea release.
|
|
#
|
|
# ./release.sh v0.4.2 build dist/Behavision-v0.4.2-windows-x64.zip and publish
|
|
# PUBLISH=0 ./release.sh v0.4.2 build only
|
|
# DEMO_PACK=demo-cameras.enc ./release.sh v0.4.4-demo
|
|
# a demo build: the sealed bundle from
|
|
# behavision-demo-pack ships in engine-src,
|
|
# and setup asks for its unlock code
|
|
#
|
|
# The package is a SOURCE install: the Go binaries are cross-compiled here, the
|
|
# engine ships as a pure-Python wheel and behavision-setup.exe builds a venv on
|
|
# the shop PC. PyInstaller cannot cross-compile, so a frozen engine needs a
|
|
# Windows build machine we do not have; this is what lets a release happen
|
|
# from this Mac at all. Layout matches what behavision-setup expects and what
|
|
# INSTALL.txt describes.
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
export PATH="$PATH:$HOME/go/bin:/opt/homebrew/bin"
|
|
|
|
TAG=${1:?usage: release.sh vX.Y.Z}
|
|
REPO_API=https://gitapp.workolik.com/api/v1/repos/Loyaly/Behavision
|
|
STAGE=dist/Behavision
|
|
ZIP="dist/Behavision-$TAG-windows-x64.zip"
|
|
|
|
step() { printf '\n\033[1m%s\033[0m\n' "$*"; }
|
|
case "$(git describe --tags --always --dirty)" in *-dirty) echo "refusing to release uncommitted changes" >&2; exit 1;; esac
|
|
if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
|
|
[ "$(git rev-parse "$TAG^{}")" = "$(git rev-parse HEAD)" ] || { echo "$TAG exists and is not HEAD" >&2; exit 1; }
|
|
fi
|
|
|
|
step "1. Desktop app (Wails, pure-Go Windows target)"
|
|
(cd desktop/frontend && npm run build >/dev/null)
|
|
rm -rf "$STAGE" && mkdir -p "$STAGE/engine-src"
|
|
(cd desktop && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath \
|
|
-ldflags "-H windowsgui -s -w -X main.version=$TAG" -o "../$STAGE/Behavision.exe" .)
|
|
|
|
step "2. Agent and setup tool"
|
|
(cd agent && CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath \
|
|
-ldflags "-s -w -X main.version=$TAG" -o "../$STAGE/behavision-agent.exe" . \
|
|
&& CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath \
|
|
-ldflags "-s -w -X main.version=$TAG" -o "../$STAGE/behavision-setup.exe" ./cmd/behavision-setup)
|
|
|
|
step "3. Engine source and wheel"
|
|
# The wheel is built with the checkout's own interpreter; requires-python is a
|
|
# statement about the SHOP PC, which setup enforces when it finds Python there.
|
|
.venv/bin/python -m pip wheel --no-deps --ignore-requires-python -q -w "$STAGE/engine-src" . 2>&1 | grep -v "DEPRECATION\|WARNING: Ignoring" || true
|
|
ls "$STAGE"/engine-src/behavision-*.whl >/dev/null || { echo "wheel was not built" >&2; exit 1; }
|
|
cp pyproject.toml requirements.txt "$STAGE/engine-src/"
|
|
mkdir -p "$STAGE/engine-src/config" && cp config/default.yaml "$STAGE/engine-src/config/"
|
|
rsync -a --exclude '__pycache__' behavision/ "$STAGE/engine-src/behavision/"
|
|
cp installer/INSTALL.txt installer/run-with-lan-head-office.cmd "$STAGE/"
|
|
if [ -n "${DEMO_PACK:-}" ]; then
|
|
case "$TAG" in *-demo*) ;; *) echo "a DEMO_PACK build must be tagged -demo" >&2; exit 1;; esac
|
|
cp "$DEMO_PACK" "$STAGE/engine-src/demo-cameras.enc" && echo " demo bundle: $(basename "$DEMO_PACK")"
|
|
fi
|
|
|
|
step "4. Package"
|
|
rm -f "$ZIP" && (cd dist && zip -qr "$(basename "$ZIP")" Behavision) && ls -la "$ZIP" | awk '{print " " $5 " bytes " $9}'
|
|
unzip -l "$ZIP" | grep -E "Behavision\.exe|agent\.exe|setup\.exe|\.whl|INSTALL" | awk '{print " " $4}'
|
|
|
|
[ "${PUBLISH:-1}" = "1" ] || { echo "built, not published"; exit 0; }
|
|
|
|
step "5. Tag and publish"
|
|
git rev-parse -q --verify "refs/tags/$TAG" >/dev/null || git tag -a "$TAG" -m "$TAG"
|
|
git push -q origin "$TAG"
|
|
# The notes come from a file so they are reviewed, not typed into a shell.
|
|
NOTES=${NOTES:-dist/RELEASE-NOTES-$TAG.md}
|
|
[ -f "$NOTES" ] || { echo "write the release notes to $NOTES first" >&2; exit 1; }
|
|
# Same credential git pushes with; Gitea accepts it as Basic auth for the API.
|
|
CRED=$(printf 'protocol=https\nhost=gitapp.workolik.com\n' | git credential fill)
|
|
USER=$(printf '%s' "$CRED" | sed -n 's/^username=//p'); PASS=$(printf '%s' "$CRED" | sed -n 's/^password=//p')
|
|
BODY=$(python3 -c 'import json,sys;print(json.dumps({"tag_name":sys.argv[1],"name":sys.argv[2],"body":open(sys.argv[3]).read(),"prerelease":True}))' "$TAG" "$TAG — $(head -1 "$NOTES" | sed 's/^#* *//')" "$NOTES")
|
|
REL=$(curl -sS -u "$USER:$PASS" -H 'content-type: application/json' -d "$BODY" "$REPO_API/releases")
|
|
ID=$(printf '%s' "$REL" | python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])')
|
|
curl -sS -u "$USER:$PASS" -F "attachment=@$ZIP" "$REPO_API/releases/$ID/assets?name=$(basename "$ZIP")" >/dev/null
|
|
echo " published: https://gitapp.workolik.com/Loyaly/Behavision/releases/tag/$TAG"
|