fix: Improve deployment script (#1043)

* chore: update Makefile for improved build process and add testing commands

* chore: add git checks to dev_release for branch and status validation

* chore: enhance dev_release with gh CLI checks and release creation

* chore: update dev_release to use versioned tags for Git and GitHub releases

* chore: refactor deploy script to use version argument and improve deployment process

* chore: update Makefile to version 0.5.0 and enhance release process with confirmation prompts and version bumping

* chore: enhance release process in Makefile with pre-release checks and user confirmation for production releases

* chore: refactor build process in Makefile to streamline development builds with a dedicated inner target

* chore: simplify build_release target in Makefile by removing frontend dependency

* chore: enable automatic version bumping in Makefile by uncommenting git commands

* chore: add pre-release testing prompts in Makefile for both development and production releases

* chore: update Makefile and test_release_on_device.sh to implement a new testing flow for pre-release validation

* chore: update Makefile to ensure consistent version handling in build and release processes
This commit is contained in:
Adam Shiervani
2025-12-05 16:09:05 +01:00
committed by GitHub
parent 29f7fe46cf
commit d89231aeec
3 changed files with 235 additions and 102 deletions
+130 -14
View File
@@ -2,14 +2,15 @@ BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
BUILDDATE := $(shell date -u +%FT%T%z)
BUILDTS := $(shell date -u +%s)
REVISION := $(shell git rev-parse HEAD)
VERSION_DEV := 0.5.0-dev$(shell date +%Y%m%d%H%M)
VERSION := 0.4.9
VERSION := 0.5.0
VERSION_DEV := $(VERSION)-dev$(shell date -u +%Y%m%d%H%M)
PROMETHEUS_TAG := github.com/prometheus/common/version
KVM_PKG_NAME := github.com/jetkvm/kvm
BUILDKIT_FLAVOR := arm-rockchip830-linux-uclibcgnueabihf
BUILDKIT_PATH ?= /opt/jetkvm-native-buildkit
DOCKER_BUILD_TAG ?= ghcr.io/jetkvm/buildkit:latest
SKIP_NATIVE_IF_EXISTS ?= 0
SKIP_UI_BUILD ?= 0
ENABLE_SYNC_TRACE ?= 0
@@ -37,7 +38,7 @@ ifneq ($(wildcard $(BUILDKIT_PATH)),)
CGO_LDFLAGS="-L$(BUILDKIT_PATH)/$(BUILDKIT_FLAVOR)/lib -L$(BUILDKIT_PATH)/$(BUILDKIT_FLAVOR)/sysroot/usr/lib -lrockit -lrockchip_mpp -lrga -lpthread -lm" \
CC="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-gcc" \
LD="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-ld" \
CGO_ENABLED=1
CGO_ENABLED=1
# GO_RELEASE_BUILD_ARGS := $(GO_RELEASE_BUILD_ARGS) -x -work
endif
@@ -47,6 +48,14 @@ BIN_DIR := $(shell pwd)/bin
TEST_DIRS := $(shell find . -name "*_test.go" -type f -exec dirname {} \; | sort -u)
test:
go test ./...
lint:
go vet ./...
check: lint test
build_native:
@if [ "$(SKIP_NATIVE_IF_EXISTS)" = "1" ] && [ -f "internal/native/cgo/lib/libjknative.a" ]; then \
echo "libjknative.a already exists, skipping native build..."; \
@@ -58,7 +67,21 @@ build_native:
./scripts/build_cgo.sh; \
fi
build_dev: build_native
# NOTE: VERSION_DEV must be explicitly passed to nested make invocations.
# VERSION_DEV contains $(shell date ...) which gets re-evaluated when a new make
# process starts. Without passing it explicitly, a minute boundary crossed during
# the build would cause version mismatch between what's displayed and what's built.
build_dev:
@if [ ! -d "$(BUILDKIT_PATH)" ]; then \
echo "Toolchain not found, running build_dev in Docker..."; \
rm -rf internal/native/cgo/build; \
docker run --rm -v "$$(pwd):/build" \
$(DOCKER_BUILD_TAG) make _build_dev_inner VERSION_DEV=$(VERSION_DEV); \
else \
$(MAKE) _build_dev_inner VERSION_DEV=$(VERSION_DEV); \
fi
_build_dev_inner: build_native
@echo "Building..."
$(GO_CMD) build \
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION_DEV)" \
@@ -114,26 +137,119 @@ frontend:
\) -exec sh -c 'gzip -9 -kfv {}' \; ;\
fi
dev_release: frontend build_dev
@echo "Uploading release... $(VERSION_DEV)"
git_check_dev:
@if [ "$$(git rev-parse --abbrev-ref HEAD)" != "dev" ]; then \
echo "Error: Must be on 'dev' branch"; exit 1; \
fi
@if [ -n "$$(git status --porcelain)" ]; then \
echo "Error: Working tree is dirty. Commit or stash changes."; exit 1; \
fi
@git fetch origin dev
@if [ "$$(git rev-parse HEAD)" != "$$(git rev-parse origin/dev)" ]; then \
echo "Error: Local dev is not up-to-date with origin/dev"; exit 1; \
fi
@command -v gh >/dev/null 2>&1 || { echo "Error: gh CLI not installed"; exit 1; }
@gh auth status >/dev/null 2>&1 || { echo "Error: gh CLI not authenticated. Run 'gh auth login'"; exit 1; }
dev_release: git_check_dev
@echo "═══════════════════════════════════════════════════════"
@echo " DEV Release"
@echo "═══════════════════════════════════════════════════════"
@echo " Version: $(VERSION_DEV)"
@echo " Tag: release/$(VERSION_DEV)"
@echo " Branch: $$(git rev-parse --abbrev-ref HEAD)"
@echo " Commit: $$(git rev-parse --short HEAD)"
@echo " Time: $$(date -u +%FT%T%z)"
@echo "═══════════════════════════════════════════════════════"
@read -p "Proceed? [y/N] " confirm && [ "$$confirm" = "y" ] || exit 1
$(MAKE) check frontend build_dev
@read -p "Test on device before release? [y/N] " test_confirm; \
if [ "$$test_confirm" = "y" ]; then \
read -p "Device IP: " device_ip; \
./scripts/test_release_on_device.sh "$$device_ip" bin/jetkvm_app test $(VERSION_DEV) || exit 1; \
fi
@echo "Uploading device app to R2..."
@shasum -a 256 bin/jetkvm_app | cut -d ' ' -f 1 > bin/jetkvm_app.sha256
rclone copyto bin/jetkvm_app r2://jetkvm-update/app/$(VERSION_DEV)/jetkvm_app
rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION_DEV)/jetkvm_app.sha256
./scripts/deploy_cloud_app.sh -v $(VERSION_DEV) --skip-confirmation
@git tag release/$(VERSION_DEV)
@git push origin release/$(VERSION_DEV)
gh release create release/$(VERSION_DEV) bin/jetkvm_app bin/jetkvm_app.sha256 --prerelease --generate-notes
@echo "✓ Released: release/$(VERSION_DEV)"
build_release: frontend build_native
# NOTE: VERSION is passed explicitly for consistency with build_dev (see comment above).
# While VERSION is static, passing it explicitly ensures the pattern is consistent
# and prevents issues if VERSION ever becomes dynamic.
build_release:
@if [ ! -d "$(BUILDKIT_PATH)" ]; then \
echo "Toolchain not found, running build_release in Docker..."; \
rm -rf internal/native/cgo/build; \
docker run --rm -v "$$(pwd):/build" \
$(DOCKER_BUILD_TAG) make _build_release_inner VERSION=$(VERSION); \
else \
$(MAKE) _build_release_inner VERSION=$(VERSION); \
fi
_build_release_inner: build_native
@echo "Building release..."
$(GO_CMD) build \
-ldflags="$(GO_LDFLAGS) -X $(KVM_PKG_NAME).builtAppVersion=$(VERSION)" \
$(GO_RELEASE_BUILD_ARGS) \
-o bin/jetkvm_app cmd/main.go
release:
@if rclone lsf r2://jetkvm-update/app/$(VERSION)/ | grep -q "jetkvm_app"; then \
echo "Error: Version $(VERSION) already exists. Please update the VERSION variable."; \
exit 1; \
release: git_check_dev
@if rclone lsf r2://jetkvm-update/app/$(VERSION)/ 2>/dev/null | grep -q "jetkvm_app"; then \
echo "Error: Version $(VERSION) already exists in R2"; exit 1; \
fi
make build_release
@echo "Uploading release..."
@latest_dev=$$(curl -s "https://api.jetkvm.com/releases?deviceId=123&prerelease=true" | jq -r '.appVersion // ""'); \
if ! echo "$$latest_dev" | grep -q "^$(VERSION)-dev"; then \
echo ""; \
echo "⚠️ Warning: No dev release found for $(VERSION)"; \
echo " Latest pre-release: $$latest_dev"; \
echo ""; \
read -p "Release production without prior dev release? [y/N] " confirm && [ "$$confirm" = "y" ] || exit 1; \
fi
@echo "═══════════════════════════════════════════════════════"
@echo " PRODUCTION Release"
@echo "═══════════════════════════════════════════════════════"
@echo " Version: $(VERSION)"
@echo " Tag: release/$(VERSION)"
@echo " Branch: $$(git rev-parse --abbrev-ref HEAD)"
@echo " Commit: $$(git rev-parse --short HEAD)"
@echo " Time: $$(date -u +%FT%T%z)"
@echo "═══════════════════════════════════════════════════════"
@read -p "Proceed with PRODUCTION release? [y/N] " confirm && [ "$$confirm" = "y" ] || exit 1
$(MAKE) check frontend build_release
@read -p "Test on device before release? [y/N] " test_confirm; \
if [ "$$test_confirm" = "y" ]; then \
read -p "Device IP: " device_ip; \
./scripts/test_release_on_device.sh "$$device_ip" bin/jetkvm_app test $(VERSION) || exit 1; \
fi
@echo "Uploading device app to R2..."
@shasum -a 256 bin/jetkvm_app | cut -d ' ' -f 1 > bin/jetkvm_app.sha256
rclone copyto bin/jetkvm_app r2://jetkvm-update/app/$(VERSION)/jetkvm_app
rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION)/jetkvm_app.sha256
rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION)/jetkvm_app.sha256
./scripts/deploy_cloud_app.sh -v $(VERSION) --set-as-default --skip-confirmation
@git tag release/$(VERSION)
@git push origin release/$(VERSION)
gh release create release/$(VERSION) bin/jetkvm_app bin/jetkvm_app.sha256 --generate-notes
@echo ""
@echo "✓ Released: release/$(VERSION)"
@echo ""
@echo "Next: Run 'make bump-version' to prepare for next release cycle"
bump-version:
@next_default=$$(echo $(VERSION) | awk -F. '{print $$1"."$$2"."$$3+1}'); \
echo "Current version: $(VERSION)"; \
read -p "Next version [$$next_default]: " next_ver; \
next_ver=$${next_ver:-$$next_default}; \
if ! echo "$$next_ver" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$$'; then \
echo "Error: Invalid version '$$next_ver'. Must be semver format (e.g., 1.2.3)"; \
exit 1; \
fi; \
sed -i 's/^VERSION := .*/VERSION := '"$$next_ver"'/' Makefile && \
git add Makefile && \
git commit -m "Bump version to $$next_ver" && \
git push && \
echo "✓ Bumped to $$next_ver"
+41 -88
View File
@@ -4,113 +4,66 @@ set -e
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
source ${SCRIPT_PATH}/build_utils.sh
function show_help() {
echo "Usage: $0 [options]"
echo "Options:"
echo " -b, --branch <branch> Checkout branch"
echo " --set-as-default Set as default"
echo " --skip-confirmation Skip confirmation"
echo " --help Show help"
}
# Parse command line arguments
CHECKOUT_BRANCH=
VERSION=
SET_AS_DEFAULT=false
SKIP_CONFIRMATION=false
while [[ $# -gt 0 ]]; do
case $1 in
-b|--branch)
CHECKOUT_BRANCH="$2"
shift 2
;;
--set-as-default)
SET_AS_DEFAULT=true
shift
;;
--skip-confirmation)
SKIP_CONFIRMATION=true
shift
;;
-v|--version) VERSION="$2"; shift 2 ;;
--set-as-default) SET_AS_DEFAULT=true; shift ;;
--skip-confirmation) SKIP_CONFIRMATION=true; shift ;;
--help)
show_help
exit 0
;;
*)
echo "Unknown option: $1"
show_help
exit 1
;;
echo "Usage: $0 -v VERSION [--set-as-default] [--skip-confirmation]"
echo " -v VERSION Version to deploy (required)"
echo " --set-as-default Also deploy to root (production only)"
echo " --skip-confirmation Skip confirmation prompt"
exit 0 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
# Checkout current branch in a new temporary directory
# only popd when exiting the script
TMP_DIR=$(mktemp -d)
trap 'popd > /dev/null && rm -rf ${TMP_DIR}' EXIT
msg_info "Copying repository to a new temporary directory ${TMP_DIR} ..."
# git fetch origin ${CH}ECKOUT_BRANCH:${CHECKOUT_BRANCH}
git clone . ${TMP_DIR}
cp ${SCRIPT_PATH}/versioned.patch ${TMP_DIR}
msg_info "Checking out branch ${CHECKOUT_BRANCH} ..."
pushd ${TMP_DIR} > /dev/null
git checkout ${CHECKOUT_BRANCH}
# CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
# # Verify branch name matches release/x.x.x or release/x.x.x-dev...
# if [[ ! $CURRENT_BRANCH =~ ^(release|release-cloud-app)/[0-9]+\.[0-9]+\.[0-9]+(-dev[0-9]+)?$ ]]; then
# msg_err "Current branch '$CURRENT_BRANCH' does not match required pattern"
# msg_err "Expected: release/x.x.x OR release/x.x.x-dev20241104123632"
# exit 1
# fi
CURRENT_BRANCH=release/0.5.0
[ -z "$VERSION" ] && { msg_err "Version required. Use -v VERSION"; exit 1; }
GIT_COMMIT=$(git rev-parse HEAD)
BUILD_TIMESTAMP=$(date -u +%FT%T%z)
VERSION=${CURRENT_BRANCH#release/}
VERSION=${VERSION#release-cloud-app/}
if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-dev[0-9]+)?$ ]]; then
msg_err "Version '$VERSION' does not match required pattern"
msg_err "Expected: x.x.x OR x.x.x-dev20241104123632"
exit 1
fi
# Change to ui directory
cd ui
if [ "$SET_AS_DEFAULT" = true ]; then
# Build for root dist
msg_info "Building for root dist..."
npm ci
npm run build:prod
fi
# Build for versioned dist/v/VERSION
msg_info "Building for dist/v/${VERSION}..."
npm ci
# Build versioned app
msg_info "Building cloud app /v/${VERSION}/..."
npm run build:prod -- --base=/v/${VERSION}/ --outDir dist/v/${VERSION}
# Ask for confirmation
if [ "$SKIP_CONFIRMATION" = false ]; then
read -p "Do you want to deploy the cloud app to production? (y/N): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
msg_err "Deployment cancelled."
exit 0
fi
# Build root app if --set-as-default
if [ "$SET_AS_DEFAULT" = true ]; then
msg_info "Building root cloud app..."
npm run build:prod -- --outDir dist/root
fi
# Deploy to production
msg_info "Deploying to r2://jetkvm-cloud-app..."
rclone copyto \
--progress \
--stats=1s \
# Confirmation
if [ "$SKIP_CONFIRMATION" = false ]; then
read -p "Deploy cloud app v${VERSION}? [y/N] " -n 1 -r
echo ""
[[ $REPLY =~ ^[Yy]$ ]] || { msg_err "Cancelled."; exit 0; }
fi
# Deploy versioned
msg_info "Deploying /v/${VERSION}/ to r2://jetkvm-cloud-app/v/${VERSION}..."
rclone copyto --progress \
--header-upload="x-amz-meta-jetkvm-version: ${VERSION}" \
--header-upload="x-amz-meta-jetkvm-build-ref: ${GIT_COMMIT}" \
--header-upload="x-amz-meta-jetkvm-build-timestamp: ${BUILD_TIMESTAMP}" \
dist \
r2://jetkvm-cloud-app
dist/v/${VERSION} r2://jetkvm-cloud-app/v/${VERSION}
msg_ok "Successfully deployed v${VERSION} to production"
# Deploy root if --set-as-default
if [ "$SET_AS_DEFAULT" = true ]; then
msg_info "Deploying root to r2://jetkvm-cloud-app..."
rclone copyto --progress \
--header-upload="x-amz-meta-jetkvm-version: ${VERSION}" \
--header-upload="x-amz-meta-jetkvm-build-ref: ${GIT_COMMIT}" \
--header-upload="x-amz-meta-jetkvm-build-timestamp: ${BUILD_TIMESTAMP}" \
dist/root r2://jetkvm-cloud-app
fi
msg_ok "Deployed cloud app v${VERSION}"
+64
View File
@@ -0,0 +1,64 @@
#!/bin/bash
set -e
DEVICE_IP="$1"
BINARY_PATH="$2"
ACTION="$3" # "deploy", "restore", or "test"
VERSION="$4" # required for "test" action
REMOTE_USER="root"
REMOTE_BIN_PATH="/userdata/jetkvm/bin"
REMOTE_UPDATE_PATH="/userdata/jetkvm"
SSH_OPTS="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ConnectTimeout=10"
ssh_cmd() { ssh $SSH_OPTS "${REMOTE_USER}@${DEVICE_IP}" "$@"; }
case "$ACTION" in
deploy)
echo "Backing up current binary..."
ssh_cmd "cp ${REMOTE_BIN_PATH}/jetkvm_app ${REMOTE_BIN_PATH}/jetkvm_app.pre_release_backup 2>/dev/null || true"
echo "Deploying new binary via OTA update mechanism..."
ssh_cmd "cat > ${REMOTE_UPDATE_PATH}/jetkvm_app.update" < "$BINARY_PATH"
echo "Rebooting device..."
ssh_cmd "reboot" || true
;;
restore)
echo "Restoring backup..."
ssh_cmd "cp ${REMOTE_BIN_PATH}/jetkvm_app.pre_release_backup ${REMOTE_BIN_PATH}/jetkvm_app"
echo "Rebooting device..."
ssh_cmd "reboot" || true
;;
test)
# Full interactive test flow
[ -z "$VERSION" ] && { echo "Error: VERSION required for test action"; exit 1; }
echo ""
echo "Deploying $VERSION to $DEVICE_IP..."
"$0" "$DEVICE_IP" "$BINARY_PATH" deploy
echo ""
echo "═══════════════════════════════════════════════════════"
echo " Device is rebooting. Please verify:"
echo "═══════════════════════════════════════════════════════"
echo " Expected version: $VERSION"
echo " Settings page: http://$DEVICE_IP/settings/general"
echo ""
echo " Check that the version shown in the UI matches above."
echo "═══════════════════════════════════════════════════════"
echo ""
read -p "Does the version match and binary work correctly? [y/n] " works
echo "Restoring device to previous binary..."
"$0" "$DEVICE_IP" "$BINARY_PATH" restore
if [ "$works" != "y" ]; then
echo "Test failed."
exit 1
fi
echo "Test passed."
;;
*)
echo "Usage: $0 <device_ip> <binary_path> <deploy|restore|test> [version]"
exit 1
;;
esac