Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 130 additions & 14 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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..."; \
Expand All @@ -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)" \
Expand Down Expand Up @@ -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)"

# 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: frontend build_native
_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
@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
make build_release
@echo "Uploading release..."
@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"
125 changes: 39 additions & 86 deletions scripts/deploy_cloud_app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
npm ci

# Build versioned app
msg_info "Building cloud app /v/${VERSION}/..."
npm run build:prod -- --base=/v/${VERSION}/ --outDir dist/v/${VERSION}

# Build root app if --set-as-default
if [ "$SET_AS_DEFAULT" = true ]; then
# Build for root dist
msg_info "Building for root dist..."
npm ci
npm run build:prod
msg_info "Building root cloud app..."
npm run build:prod -- --outDir dist/root
fi

# Build for versioned dist/v/VERSION
msg_info "Building for dist/v/${VERSION}..."
npm ci
npm run build:prod -- --base=/v/${VERSION}/ --outDir dist/v/${VERSION}

# Ask for confirmation
# 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
read -p "Deploy cloud app v${VERSION}? [y/N] " -n 1 -r
echo ""
[[ $REPLY =~ ^[Yy]$ ]] || { msg_err "Cancelled."; exit 0; }
fi

# Deploy to production
msg_info "Deploying to r2://jetkvm-cloud-app..."
rclone copyto \
--progress \
--stats=1s \
# 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}

# 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 "Successfully deployed v${VERSION} to production"
msg_ok "Deployed cloud app v${VERSION}"
Loading