@@ -2,14 +2,15 @@ BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
22BUILDDATE := $(shell date -u +% FT% T% z)
33BUILDTS := $(shell date -u +% s)
44REVISION := $(shell git rev-parse HEAD)
5- VERSION_DEV := 0.5.0-dev $( shell date + % Y % m % d % H % M)
6- VERSION := 0.4.9
5+ VERSION := 0.5.1
6+ VERSION_DEV := $( VERSION ) -dev $( shell date -u + % Y % m % d % H % M)
77
88PROMETHEUS_TAG := github.com/prometheus/common/version
99KVM_PKG_NAME := github.com/jetkvm/kvm
1010
1111BUILDKIT_FLAVOR := arm-rockchip830-linux-uclibcgnueabihf
1212BUILDKIT_PATH ?= /opt/jetkvm-native-buildkit
13+ DOCKER_BUILD_TAG ?= ghcr.io/jetkvm/buildkit:latest
1314SKIP_NATIVE_IF_EXISTS ?= 0
1415SKIP_UI_BUILD ?= 0
1516ENABLE_SYNC_TRACE ?= 0
@@ -37,7 +38,7 @@ ifneq ($(wildcard $(BUILDKIT_PATH)),)
3738 CGO_LDFLAGS="-L$(BUILDKIT_PATH)/$(BUILDKIT_FLAVOR)/lib -L$(BUILDKIT_PATH)/$(BUILDKIT_FLAVOR)/sysroot/usr/lib -lrockit -lrockchip_mpp -lrga -lpthread -lm" \
3839 CC="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-gcc" \
3940 LD="$(BUILDKIT_PATH)/bin/$(BUILDKIT_FLAVOR)-ld" \
40- CGO_ENABLED=1
41+ CGO_ENABLED=1
4142 # GO_RELEASE_BUILD_ARGS := $(GO_RELEASE_BUILD_ARGS) -x -work
4243endif
4344
@@ -47,6 +48,19 @@ BIN_DIR := $(shell pwd)/bin
4748
4849TEST_DIRS := $(shell find . -name "* _test.go" -type f -exec dirname {} \; | sort -u)
4950
51+ test :
52+ go test ./...
53+
54+ test_e2e :
55+ @read -p " Device IP: " device_ip; \
56+ cd ui && npm install && npx playwright install --with-deps chromium && \
57+ NODE_NO_WARNINGS=1 JETKVM_URL=" http://$$ device_ip" npm run test:e2e
58+
59+ lint :
60+ go vet ./...
61+
62+ check : lint test
63+
5064build_native :
5165 @if [ " $( SKIP_NATIVE_IF_EXISTS) " = " 1" ] && [ -f " internal/native/cgo/lib/libjknative.a" ]; then \
5266 echo " libjknative.a already exists, skipping native build..." ; \
@@ -58,7 +72,21 @@ build_native:
5872 ./scripts/build_cgo.sh; \
5973 fi
6074
61- build_dev : build_native
75+ # NOTE: VERSION_DEV must be explicitly passed to nested make invocations.
76+ # VERSION_DEV contains $(shell date ...) which gets re-evaluated when a new make
77+ # process starts. Without passing it explicitly, a minute boundary crossed during
78+ # the build would cause version mismatch between what's displayed and what's built.
79+ build_dev :
80+ @if [ ! -d " $( BUILDKIT_PATH) " ]; then \
81+ echo " Toolchain not found, running build_dev in Docker..." ; \
82+ rm -rf internal/native/cgo/build; \
83+ docker run --rm -v " $$ (pwd):/build" \
84+ $(DOCKER_BUILD_TAG ) make _build_dev_inner VERSION_DEV=$(VERSION_DEV ) ; \
85+ else \
86+ $(MAKE ) _build_dev_inner VERSION_DEV=$(VERSION_DEV ) ; \
87+ fi
88+
89+ _build_dev_inner : build_native
6290 @echo " Building..."
6391 $(GO_CMD ) build \
6492 -ldflags=" $( GO_LDFLAGS) -X $( KVM_PKG_NAME) .builtAppVersion=$( VERSION_DEV) " \
@@ -114,26 +142,128 @@ frontend:
114142 \) -exec sh -c ' gzip -9 -kfv {}' \; ; \
115143 fi
116144
117- dev_release : frontend build_dev
118- @echo " Uploading release... $( VERSION_DEV) "
145+ git_check_dev :
146+ @if [ " $$ (git rev-parse --abbrev-ref HEAD)" != " dev" ]; then \
147+ echo " Error: Must be on 'dev' branch" ; exit 1; \
148+ fi
149+ @if [ -n " $$ (git status --porcelain)" ]; then \
150+ echo " Error: Working tree is dirty. Commit or stash changes." ; exit 1; \
151+ fi
152+ @git fetch origin dev
153+ @if [ " $$ (git rev-parse HEAD)" != " $$ (git rev-parse origin/dev)" ]; then \
154+ echo " Error: Local dev is not up-to-date with origin/dev" ; exit 1; \
155+ fi
156+ @command -v gh > /dev/null 2>&1 || { echo " Error: gh CLI not installed" ; exit 1; }
157+ @gh auth status > /dev/null 2>&1 || { echo " Error: gh CLI not authenticated. Run 'gh auth login'" ; exit 1; }
158+
159+ dev_release : git_check_dev
160+ @echo " ═══════════════════════════════════════════════════════"
161+ @echo " DEV Release"
162+ @echo " ═══════════════════════════════════════════════════════"
163+ @echo " Version: $( VERSION_DEV) "
164+ @echo " Tag: release/$( VERSION_DEV) "
165+ @echo " Branch: $$ (git rev-parse --abbrev-ref HEAD)"
166+ @echo " Commit: $$ (git rev-parse --short HEAD)"
167+ @echo " Time: $$ (date -u +%FT%T%z)"
168+ @echo " ═══════════════════════════════════════════════════════"
169+ @read -p " Proceed? [y/N] " confirm && [ " $$ confirm" = " y" ] || exit 1
170+ $(MAKE ) check frontend build_dev
171+ @read -p " Test on device before release? [y/N] " test_confirm; \
172+ if [ " $$ test_confirm" = " y" ]; then \
173+ read -p " Device IP: " device_ip; \
174+ echo " Installing Playwright dependencies..." ; \
175+ cd ui && npm ci && npx playwright install --with-deps chromium && cd ..; \
176+ ./scripts/test_release_on_device.sh " $$ device_ip" bin/jetkvm_app test $(VERSION_DEV ) || exit 1; \
177+ fi
178+ @echo " Uploading device app to R2..."
119179 @shasum -a 256 bin/jetkvm_app | cut -d ' ' -f 1 > bin/jetkvm_app.sha256
120180 rclone copyto bin/jetkvm_app r2://jetkvm-update/app/$(VERSION_DEV ) /jetkvm_app
121181 rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION_DEV ) /jetkvm_app.sha256
182+ ./scripts/deploy_cloud_app.sh -v $(VERSION_DEV ) --skip-confirmation
183+ @git tag release/$(VERSION_DEV )
184+ @git push origin release/$(VERSION_DEV )
185+ gh release create release/$(VERSION_DEV ) bin/jetkvm_app bin/jetkvm_app.sha256 --prerelease --generate-notes
186+ @echo " ✓ Released: release/$( VERSION_DEV) "
187+
188+ # NOTE: VERSION is passed explicitly for consistency with build_dev (see comment above).
189+ # While VERSION is static, passing it explicitly ensures the pattern is consistent
190+ # and prevents issues if VERSION ever becomes dynamic.
191+ build_release :
192+ @if [ ! -d " $( BUILDKIT_PATH) " ]; then \
193+ echo " Toolchain not found, running build_release in Docker..." ; \
194+ rm -rf internal/native/cgo/build; \
195+ docker run --rm -v " $$ (pwd):/build" \
196+ $(DOCKER_BUILD_TAG ) make _build_release_inner VERSION=$(VERSION ) ; \
197+ else \
198+ $(MAKE ) _build_release_inner VERSION=$(VERSION ) ; \
199+ fi
122200
123- build_release : frontend build_native
201+ _build_release_inner : build_native
124202 @echo " Building release..."
125203 $(GO_CMD ) build \
126204 -ldflags=" $( GO_LDFLAGS) -X $( KVM_PKG_NAME) .builtAppVersion=$( VERSION) " \
127205 $(GO_RELEASE_BUILD_ARGS ) \
128206 -o bin/jetkvm_app cmd/main.go
129207
130- release :
131- @if rclone lsf r2://jetkvm-update/app/$(VERSION ) / | grep -q " jetkvm_app" ; then \
132- echo " Error: Version $( VERSION) already exists. Please update the VERSION variable." ; \
133- exit 1; \
208+ release : git_check_dev
209+ @if rclone lsf r2://jetkvm-update/app/$(VERSION ) / 2> /dev/null | grep -q " jetkvm_app" ; then \
210+ echo " Error: Version $( VERSION) already exists in R2" ; exit 1; \
134211 fi
135- make build_release
136- @echo " Uploading release..."
212+ @latest_dev=$$(curl -s "https://api.jetkvm.com/releases?deviceId=123&prerelease=true" | jq -r '.appVersion // ""' ) ; \
213+ if ! echo " $$ latest_dev" | grep -q " ^$( VERSION) -dev" ; then \
214+ echo " " ; \
215+ echo " ⚠️ Warning: No dev release found for $( VERSION) " ; \
216+ echo " Latest pre-release: $$ latest_dev" ; \
217+ echo " " ; \
218+ read -p " Release production without prior dev release? [y/N] " confirm && [ " $$ confirm" = " y" ] || exit 1; \
219+ fi
220+ @echo " ═══════════════════════════════════════════════════════"
221+ @echo " PRODUCTION Release"
222+ @echo " ═══════════════════════════════════════════════════════"
223+ @echo " Version: $( VERSION) "
224+ @echo " Tag: release/$( VERSION) "
225+ @echo " Branch: $$ (git rev-parse --abbrev-ref HEAD)"
226+ @echo " Commit: $$ (git rev-parse --short HEAD)"
227+ @echo " Time: $$ (date -u +%FT%T%z)"
228+ @echo " ═══════════════════════════════════════════════════════"
229+ @read -p " Proceed with PRODUCTION release? [y/N] " confirm && [ " $$ confirm" = " y" ] || exit 1
230+ $(MAKE ) check frontend build_release
231+ @read -p " Test on device before release? [y/N] " test_confirm; \
232+ if [ " $$ test_confirm" = " y" ]; then \
233+ read -p " Device IP: " device_ip; \
234+ echo " Installing Playwright dependencies..." ; \
235+ cd ui && npm ci && npx playwright install --with-deps chromium && cd ..; \
236+ ./scripts/test_release_on_device.sh " $$ device_ip" bin/jetkvm_app test $(VERSION ) || exit 1; \
237+ fi
238+ @echo " Uploading device app to R2..."
137239 @shasum -a 256 bin/jetkvm_app | cut -d ' ' -f 1 > bin/jetkvm_app.sha256
138240 rclone copyto bin/jetkvm_app r2://jetkvm-update/app/$(VERSION ) /jetkvm_app
139- rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION ) /jetkvm_app.sha256
241+ rclone copyto bin/jetkvm_app.sha256 r2://jetkvm-update/app/$(VERSION ) /jetkvm_app.sha256
242+ ./scripts/deploy_cloud_app.sh -v $(VERSION ) --set-as-default --skip-confirmation
243+ @git tag release/$(VERSION )
244+ @git push origin release/$(VERSION )
245+ prev_prod=$$(gh release list --exclude-drafts --exclude-pre-releases --limit 1 --json tagName --jq '.[0].tagName' ) ; \
246+ gh release create release/$(VERSION ) bin/jetkvm_app bin/jetkvm_app.sha256 \
247+ --title " $( VERSION) " \
248+ --generate-notes \
249+ --notes-start-tag " $$ prev_prod" \
250+ --draft
251+ @echo " "
252+ @echo " ✓ Released: release/$( VERSION) "
253+ @echo " "
254+ @echo " Next: Run 'make bump-version' to prepare for next release cycle"
255+
256+ bump-version :
257+ @next_default=$$(echo $(VERSION ) | awk -F. '{print $$1"."$$2"."$$3+1}' ) ; \
258+ echo " Current version: $( VERSION) " ; \
259+ read -p " Next version [$$ next_default]: " next_ver; \
260+ next_ver=$$ {next_ver:-$$ next_default}; \
261+ if ! echo " $$ next_ver" | grep -qE ' ^[0-9]+\.[0-9]+\.[0-9]+$$' ; then \
262+ echo " Error: Invalid version '$$ next_ver'. Must be semver format (e.g., 1.2.3)" ; \
263+ exit 1; \
264+ fi ; \
265+ sed -i ' s/^VERSION := .*/VERSION := ' " $$ next_ver" ' /' Makefile && \
266+ git add Makefile && \
267+ git commit -m " Bump version to $$ next_ver" && \
268+ git push && \
269+ echo " ✓ Bumped to $$ next_ver"
0 commit comments