Skip to content

Commit d50d832

Browse files
authored
Merge branch 'dev' into fix/usb-restart
2 parents f8f6524 + 32dd326 commit d50d832

File tree

19 files changed

+1516
-831
lines changed

19 files changed

+1516
-831
lines changed

Makefile

Lines changed: 144 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ BRANCH := $(shell git rev-parse --abbrev-ref HEAD)
22
BUILDDATE := $(shell date -u +%FT%T%z)
33
BUILDTS := $(shell date -u +%s)
44
REVISION := $(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

88
PROMETHEUS_TAG := github.com/prometheus/common/version
99
KVM_PKG_NAME := github.com/jetkvm/kvm
1010

1111
BUILDKIT_FLAVOR := arm-rockchip830-linux-uclibcgnueabihf
1212
BUILDKIT_PATH ?= /opt/jetkvm-native-buildkit
13+
DOCKER_BUILD_TAG ?= ghcr.io/jetkvm/buildkit:latest
1314
SKIP_NATIVE_IF_EXISTS ?= 0
1415
SKIP_UI_BUILD ?= 0
1516
ENABLE_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
4243
endif
4344

@@ -47,6 +48,19 @@ BIN_DIR := $(shell pwd)/bin
4748

4849
TEST_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+
5064
build_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"

pkg/nmlite/resolvconf.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ func (rcm *ResolvConfManager) generateResolvConf(conf *types.ResolvConf) ([]byte
192192
mergeConfig(&nameservers, &searchList, &conf.ConfigIPv4)
193193
mergeConfig(&nameservers, &searchList, &conf.ConfigIPv6)
194194

195+
rcm.logger.Info().
196+
Interface("nameservers", nameservers).
197+
Interface("searchList", searchList).
198+
Msg("merged config")
199+
195200
flattenedSearchList := []string{}
196201
for search := range searchList {
197202
flattenedSearchList = append(flattenedSearchList, search)

scripts/deploy_cloud_app.sh

Lines changed: 39 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,113 +4,66 @@ set -e
44
SCRIPT_PATH=$(realpath "$(dirname $(realpath "${BASH_SOURCE[0]}"))")
55
source ${SCRIPT_PATH}/build_utils.sh
66

7-
function show_help() {
8-
echo "Usage: $0 [options]"
9-
echo "Options:"
10-
echo " -b, --branch <branch> Checkout branch"
11-
echo " --set-as-default Set as default"
12-
echo " --skip-confirmation Skip confirmation"
13-
echo " --help Show help"
14-
}
15-
16-
# Parse command line arguments
17-
CHECKOUT_BRANCH=
7+
VERSION=
188
SET_AS_DEFAULT=false
199
SKIP_CONFIRMATION=false
10+
2011
while [[ $# -gt 0 ]]; do
2112
case $1 in
22-
-b|--branch)
23-
CHECKOUT_BRANCH="$2"
24-
shift 2
25-
;;
26-
--set-as-default)
27-
SET_AS_DEFAULT=true
28-
shift
29-
;;
30-
--skip-confirmation)
31-
SKIP_CONFIRMATION=true
32-
shift
33-
;;
13+
-v|--version) VERSION="$2"; shift 2 ;;
14+
--set-as-default) SET_AS_DEFAULT=true; shift ;;
15+
--skip-confirmation) SKIP_CONFIRMATION=true; shift ;;
3416
--help)
35-
show_help
36-
exit 0
37-
;;
38-
*)
39-
echo "Unknown option: $1"
40-
show_help
41-
exit 1
42-
;;
17+
echo "Usage: $0 -v VERSION [--set-as-default] [--skip-confirmation]"
18+
echo " -v VERSION Version to deploy (required)"
19+
echo " --set-as-default Also deploy to root (production only)"
20+
echo " --skip-confirmation Skip confirmation prompt"
21+
exit 0 ;;
22+
*) echo "Unknown option: $1"; exit 1 ;;
4323
esac
4424
done
4525

46-
47-
# Checkout current branch in a new temporary directory
48-
# only popd when exiting the script
49-
TMP_DIR=$(mktemp -d)
50-
trap 'popd > /dev/null && rm -rf ${TMP_DIR}' EXIT
51-
msg_info "Copying repository to a new temporary directory ${TMP_DIR} ..."
52-
# git fetch origin ${CH}ECKOUT_BRANCH:${CHECKOUT_BRANCH}
53-
git clone . ${TMP_DIR}
54-
cp ${SCRIPT_PATH}/versioned.patch ${TMP_DIR}
55-
msg_info "Checking out branch ${CHECKOUT_BRANCH} ..."
56-
pushd ${TMP_DIR} > /dev/null
57-
git checkout ${CHECKOUT_BRANCH}
58-
59-
60-
# CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
61-
# # Verify branch name matches release/x.x.x or release/x.x.x-dev...
62-
# if [[ ! $CURRENT_BRANCH =~ ^(release|release-cloud-app)/[0-9]+\.[0-9]+\.[0-9]+(-dev[0-9]+)?$ ]]; then
63-
# msg_err "Current branch '$CURRENT_BRANCH' does not match required pattern"
64-
# msg_err "Expected: release/x.x.x OR release/x.x.x-dev20241104123632"
65-
# exit 1
66-
# fi
67-
68-
CURRENT_BRANCH=release/0.5.0
26+
[ -z "$VERSION" ] && { msg_err "Version required. Use -v VERSION"; exit 1; }
6927

7028
GIT_COMMIT=$(git rev-parse HEAD)
7129
BUILD_TIMESTAMP=$(date -u +%FT%T%z)
72-
VERSION=${CURRENT_BRANCH#release/}
73-
VERSION=${VERSION#release-cloud-app/}
74-
if [[ ! $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+(-dev[0-9]+)?$ ]]; then
75-
msg_err "Version '$VERSION' does not match required pattern"
76-
msg_err "Expected: x.x.x OR x.x.x-dev20241104123632"
77-
exit 1
78-
fi
7930

80-
# Change to ui directory
8131
cd ui
32+
npm ci
33+
34+
# Build versioned app
35+
msg_info "Building cloud app /v/${VERSION}/..."
36+
VITE_CLOUD_ENABLE_VERSIONED_UI=true npm run build:prod -- --base=/v/${VERSION}/ --outDir dist/v/${VERSION}
8237

38+
# Build root app if --set-as-default
8339
if [ "$SET_AS_DEFAULT" = true ]; then
84-
# Build for root dist
85-
msg_info "Building for root dist..."
86-
npm ci
87-
npm run build:prod
40+
msg_info "Building root cloud app..."
41+
VITE_CLOUD_ENABLE_VERSIONED_UI=true npm run build:prod -- --outDir dist/root
8842
fi
8943

90-
# Build for versioned dist/v/VERSION
91-
msg_info "Building for dist/v/${VERSION}..."
92-
npm ci
93-
npm run build:prod -- --base=/v/${VERSION}/ --outDir dist/v/${VERSION}
94-
95-
# Ask for confirmation
44+
# Confirmation
9645
if [ "$SKIP_CONFIRMATION" = false ]; then
97-
read -p "Do you want to deploy the cloud app to production? (y/N): " -n 1 -r
98-
echo ""
99-
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
100-
msg_err "Deployment cancelled."
101-
exit 0
102-
fi
46+
read -p "Deploy cloud app v${VERSION}? [y/N] " -n 1 -r
47+
echo ""
48+
[[ $REPLY =~ ^[Yy]$ ]] || { msg_err "Cancelled."; exit 0; }
10349
fi
10450

105-
# Deploy to production
106-
msg_info "Deploying to r2://jetkvm-cloud-app..."
107-
rclone copyto \
108-
--progress \
109-
--stats=1s \
51+
# Deploy versioned
52+
msg_info "Deploying /v/${VERSION}/ to r2://jetkvm-cloud-app/v/${VERSION}..."
53+
rclone copyto --progress \
11054
--header-upload="x-amz-meta-jetkvm-version: ${VERSION}" \
11155
--header-upload="x-amz-meta-jetkvm-build-ref: ${GIT_COMMIT}" \
11256
--header-upload="x-amz-meta-jetkvm-build-timestamp: ${BUILD_TIMESTAMP}" \
113-
dist \
114-
r2://jetkvm-cloud-app
57+
dist/v/${VERSION} r2://jetkvm-cloud-app/v/${VERSION}
58+
59+
# Deploy root if --set-as-default
60+
if [ "$SET_AS_DEFAULT" = true ]; then
61+
msg_info "Deploying root to r2://jetkvm-cloud-app..."
62+
rclone copyto --progress \
63+
--header-upload="x-amz-meta-jetkvm-version: ${VERSION}" \
64+
--header-upload="x-amz-meta-jetkvm-build-ref: ${GIT_COMMIT}" \
65+
--header-upload="x-amz-meta-jetkvm-build-timestamp: ${BUILD_TIMESTAMP}" \
66+
dist/root r2://jetkvm-cloud-app
67+
fi
11568

116-
msg_ok "Successfully deployed v${VERSION} to production"
69+
msg_ok "Deployed cloud app v${VERSION}"

0 commit comments

Comments
 (0)