Started without a command, keeporg now shows a padlock in the system tray with a menu like midi-webhook's: a status line, the mirror folder, each database with its state, Sync Now, browser sign-in and sign-out, the web interface, the configuration, the log, Start at Login, About and Quit. The commands are unchanged. config.json now carries every client setting (device_label, request_timeout_seconds, ca_file, log_file, notifications), is written in full on first start and is reloaded by the tray when saved. A lock in the mirror directory admits one agent at a time, since the tray, `keeporg run` and a service unit could otherwise race each other. The tray uses fyne.io/systray: pure Go on Linux, BSD and Windows, cgo on macOS only. The server keeps no third-party dependencies. Desktop versions go to 0.5.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
295 lines
12 KiB
Makefile
295 lines
12 KiB
Makefile
# KeepOrg: build and publish entrypoint.
|
|
#
|
|
# Every target is a real file where it can be, so make rebuilds only what
|
|
# changed. Run `make help` for the list.
|
|
|
|
include versions.mk
|
|
|
|
GO := go
|
|
BUILD := build
|
|
DIST := dist
|
|
GOFLAGS := -trimpath
|
|
HOSTOS := $(shell $(GO) env GOOS)
|
|
HOSTARCH := $(shell $(GO) env GOARCH)
|
|
GO_SRC := $(shell find . -name '*.go' -not -path './$(DIST)/*' -not -path './$(BUILD)/*') go.mod
|
|
|
|
# The version stamped into a binary depends on the platform it is built for.
|
|
version_for = $(strip \
|
|
$(if $(filter linux,$(1)),$(VERSION_LINUX),\
|
|
$(if $(filter darwin,$(1)),$(VERSION_MACOS),\
|
|
$(if $(filter windows,$(1)),$(VERSION_WINDOWS),\
|
|
$(if $(filter freebsd openbsd netbsd,$(1)),$(VERSION_BSD),0.0.0)))))
|
|
|
|
# So is the application identifier, which names the macOS login item.
|
|
appid_for = $(strip \
|
|
$(if $(filter linux,$(1)),$(APPID_LINUX),\
|
|
$(if $(filter darwin,$(1)),$(APPID_MACOS),\
|
|
$(if $(filter windows,$(1)),$(APPID_WINDOWS),$(APPID_BSD)))))
|
|
|
|
client_ldflags = -s -w -X main.Version=$(call version_for,$(1)) -X main.AppID=$(call appid_for,$(1))
|
|
|
|
os_of = $(word 1,$(subst -, ,$(1)))
|
|
arch_of = $(word 2,$(subst -, ,$(1)))
|
|
|
|
HOST_CLIENT := $(BUILD)/$(HOSTOS)-$(HOSTARCH)/keeporg
|
|
HOST_SERVER := $(BUILD)/$(HOSTOS)-$(HOSTARCH)/keeporgd
|
|
|
|
.DEFAULT_GOAL := help
|
|
APP_NAME := keeporg
|
|
|
|
# ---------------------------------------------------------------- developing
|
|
|
|
help: ## show this help
|
|
@printf 'KeepOrg %s\n\n' "$(VERSION_LINUX)"
|
|
@grep -hE '^[a-zA-Z0-9_%/.-]+:.*?## ' $(MAKEFILE_LIST) \
|
|
| sort | awk 'BEGIN{FS=":.*?## "}{printf " \033[1m%-22s\033[0m %s\n", $$1, $$2}'
|
|
@printf '\nVersions: server %s linux %s macos %s windows %s bsd %s android %s (code %s)\n' \
|
|
"$(VERSION_SERVER)" "$(VERSION_LINUX)" "$(VERSION_MACOS)" "$(VERSION_WINDOWS)" \
|
|
"$(VERSION_BSD)" "$(VERSION_ANDROID)" "$(ANDROID_VERSION_CODE)"
|
|
|
|
fmt: ## format the Go sources
|
|
gofmt -w $(filter %.go,$(GO_SRC))
|
|
|
|
vet: ## run go vet
|
|
$(GO) vet ./...
|
|
|
|
test: ## run the whole test suite
|
|
$(GO) test ./...
|
|
|
|
test-race: ## run the test suite under the race detector
|
|
$(GO) test -race ./...
|
|
|
|
check: fmt vet test ## format, vet and test
|
|
|
|
clean: ## remove build and dist output
|
|
rm -rf $(BUILD) $(DIST)
|
|
|
|
# ------------------------------------------------------------------ building
|
|
|
|
.PHONY: all server client
|
|
all: server client ## build the server and client for this host
|
|
server: $(HOST_SERVER) ## build the server for this host
|
|
client: $(HOST_CLIENT) ## build the desktop client for this host
|
|
|
|
# The macOS menu bar needs Cocoa, so the client links it with cgo when this
|
|
# host is a Mac. Built anywhere else it has every command but `tray`. This
|
|
# rule comes before the generic one because make 3.81, which macOS ships,
|
|
# takes the first pattern that matches.
|
|
DARWIN_CGO := $(if $(filter darwin,$(HOSTOS)),1,0)
|
|
|
|
$(BUILD)/darwin-%/keeporg: $(GO_SRC)
|
|
@mkdir -p $(dir $@)
|
|
GOOS=darwin GOARCH=$* CGO_ENABLED=$(DARWIN_CGO) \
|
|
$(GO) build $(GOFLAGS) -ldflags "$(call client_ldflags,darwin)" \
|
|
-o $@ ./cmd/keeporg
|
|
|
|
# Linux and the BSDs talk to the tray over D-Bus, which is pure Go.
|
|
$(BUILD)/%/keeporg: $(GO_SRC)
|
|
@mkdir -p $(dir $@)
|
|
GOOS=$(call os_of,$*) GOARCH=$(call arch_of,$*) CGO_ENABLED=0 \
|
|
$(GO) build $(GOFLAGS) -ldflags "$(call client_ldflags,$(call os_of,$*))" \
|
|
-o $@ ./cmd/keeporg
|
|
|
|
$(BUILD)/%/keeporgd: $(GO_SRC)
|
|
@mkdir -p $(dir $@)
|
|
GOOS=$(call os_of,$*) GOARCH=$(call arch_of,$*) CGO_ENABLED=0 \
|
|
$(GO) build $(GOFLAGS) \
|
|
-ldflags "-s -w -X main.Version=$(VERSION_SERVER)" \
|
|
-o $@ ./cmd/keeporgd
|
|
|
|
# Windows gets the program twice: keeporg.exe is a console program for the
|
|
# commands, keeporg-tray.exe the same code linked as a GUI program, so starting
|
|
# the tray opens no console window.
|
|
$(BUILD)/windows-%/keeporg.exe: $(GO_SRC)
|
|
@mkdir -p $(dir $@)
|
|
GOOS=windows GOARCH=$* CGO_ENABLED=0 \
|
|
$(GO) build $(GOFLAGS) -ldflags "$(call client_ldflags,windows)" \
|
|
-o $@ ./cmd/keeporg
|
|
|
|
$(BUILD)/windows-%/keeporg-tray.exe: $(GO_SRC)
|
|
@mkdir -p $(dir $@)
|
|
GOOS=windows GOARCH=$* CGO_ENABLED=0 \
|
|
$(GO) build $(GOFLAGS) -ldflags "$(call client_ldflags,windows) -H windowsgui" \
|
|
-o $@ ./cmd/keeporg
|
|
|
|
# The application icon, drawn by internal/icon, for the Linux icon theme and
|
|
# the macOS bundle.
|
|
ICONS_HICOLOR := $(BUILD)/icons/hicolor/.stamp
|
|
ICONSET := $(BUILD)/icons/KeepOrg.iconset/.stamp
|
|
|
|
$(ICONS_HICOLOR): internal/icon/icon.go tools/genicon/main.go
|
|
rm -rf $(dir $@) && $(GO) run ./tools/genicon -hicolor $(dir $@) && touch $@
|
|
|
|
$(ICONSET): internal/icon/icon.go tools/genicon/main.go
|
|
rm -rf $(dir $@) && $(GO) run ./tools/genicon -iconset $(dir $@) && touch $@
|
|
|
|
.PHONY: cross
|
|
cross: ## build every desktop and server binary
|
|
cross: $(BUILD)/linux-amd64/keeporg $(BUILD)/linux-arm64/keeporg \
|
|
$(BUILD)/linux-amd64/keeporgd $(BUILD)/linux-arm64/keeporgd \
|
|
$(BUILD)/darwin-amd64/keeporg $(BUILD)/darwin-arm64/keeporg \
|
|
$(BUILD)/freebsd-amd64/keeporg $(BUILD)/freebsd-arm64/keeporg \
|
|
$(BUILD)/windows-amd64/keeporg.exe $(BUILD)/windows-amd64/keeporg-tray.exe
|
|
|
|
# ------------------------------------------------------------------ packaging
|
|
|
|
DEB_ARCH ?= $(HOSTARCH)
|
|
DEB_CLIENT := $(DIST)/keeporg_$(VERSION_LINUX)_$(DEB_ARCH).deb
|
|
DEB_SERVER := $(DIST)/keeporgd_$(VERSION_SERVER)_$(DEB_ARCH).deb
|
|
|
|
.PHONY: deb deb-client deb-server
|
|
deb: deb-client deb-server ## build both Debian packages
|
|
deb-client: $(DEB_CLIENT) ## build the client .deb
|
|
deb-server: $(DEB_SERVER) ## build the server .deb
|
|
|
|
$(DEB_CLIENT): $(BUILD)/linux-$(DEB_ARCH)/keeporg packaging/deb/client/control.in \
|
|
packaging/deb/client/changelog packaging/systemd/keeporg.service \
|
|
packaging/linux/keeporg.desktop $(ICONS_HICOLOR)
|
|
scripts/build-deb.sh client $(VERSION_LINUX) $(DEB_ARCH) $@
|
|
|
|
$(DEB_SERVER): $(BUILD)/linux-$(DEB_ARCH)/keeporgd packaging/deb/server/control.in \
|
|
packaging/deb/server/changelog packaging/systemd/keeporgd.service
|
|
scripts/build-deb.sh server $(VERSION_SERVER) $(DEB_ARCH) $@
|
|
|
|
DMG_ARCH ?= $(HOSTARCH)
|
|
DMG := $(DIST)/KeepOrg-$(VERSION_MACOS)-$(DMG_ARCH).dmg
|
|
|
|
.PHONY: dmg
|
|
dmg: $(DMG) ## build the macOS DMG with an /Applications symlink (macOS only)
|
|
|
|
$(DMG): $(BUILD)/darwin-$(DMG_ARCH)/keeporg packaging/macos/Info.plist.in $(ICONSET)
|
|
scripts/build-dmg.sh $(VERSION_MACOS) $(DMG_ARCH) $(APPID_MACOS) $@
|
|
|
|
ZIP := $(DIST)/keeporg-$(VERSION_WINDOWS)-windows-amd64.zip
|
|
|
|
.PHONY: windows
|
|
windows: $(ZIP) ## build the Windows zip
|
|
|
|
$(ZIP): $(BUILD)/windows-amd64/keeporg.exe $(BUILD)/windows-amd64/keeporg-tray.exe \
|
|
packaging/windows/README.txt packaging/windows/keeporg-task.xml
|
|
@mkdir -p $(DIST)
|
|
rm -rf $(BUILD)/windows-stage && mkdir -p $(BUILD)/windows-stage
|
|
cp $(BUILD)/windows-amd64/keeporg.exe $(BUILD)/windows-amd64/keeporg-tray.exe $(BUILD)/windows-stage/
|
|
cp packaging/windows/README.txt $(BUILD)/windows-stage/
|
|
cp packaging/windows/keeporg-task.xml $(BUILD)/windows-stage/
|
|
cd $(BUILD)/windows-stage && zip -q -r ../../$@ .
|
|
|
|
# -------------------------------------------------------------------- android
|
|
|
|
GRADLE ?= $(shell test -x android/gradlew && echo ./gradlew || echo gradle)
|
|
# Gradle 8.9+ and JDK 17+ are required by the Android Gradle plugin.
|
|
ANDROID_HOME ?= $(HOME)/android-sdk
|
|
APK_BUILT := android/app/build/outputs/apk/release/app-release-unsigned.apk
|
|
APK := $(DIST)/$(APPID_ANDROID)_$(ANDROID_VERSION_CODE).apk
|
|
|
|
.PHONY: android
|
|
android: $(APK) ## build the unsigned release APK under its publishing name
|
|
|
|
.PHONY: android-lint
|
|
android-lint: ## run Android lint; any finding fails the build
|
|
cd android && ANDROID_HOME="$(ANDROID_HOME)" $(GRADLE) --no-daemon :app:lintRelease
|
|
@cat android/app/build/reports/lint-results.txt
|
|
|
|
$(APK): $(shell find android/app/src -type f 2>/dev/null) android/app/build.gradle.kts
|
|
@test -d "$(ANDROID_HOME)/platforms" || { \
|
|
printf 'no Android SDK at %s\nset ANDROID_HOME, or see android/README.md\n' "$(ANDROID_HOME)" >&2; \
|
|
exit 1; }
|
|
cd android && ANDROID_HOME="$(ANDROID_HOME)" $(GRADLE) --no-daemon assembleRelease \
|
|
-PversionName=$(VERSION_ANDROID) -PversionCode=$(ANDROID_VERSION_CODE)
|
|
@mkdir -p $(DIST)
|
|
cp $(APK_BUILT) $@
|
|
@printf 'built %s\n' "$@"
|
|
|
|
# ------------------------------------------------------------------- container
|
|
|
|
# Either engine will do; podman needs no daemon and no root.
|
|
CONTAINER ?= $(shell command -v podman 2>/dev/null || command -v docker 2>/dev/null || echo docker)
|
|
IMAGE ?= keeporgd
|
|
IMAGE_TAG ?= $(VERSION_SERVER)
|
|
# podman defaults to the OCI image format, which has no HEALTHCHECK field.
|
|
IMAGE_FORMAT := $(if $(findstring podman,$(CONTAINER)),--format docker,)
|
|
IMAGE_TAR := $(DIST)/keeporgd-$(VERSION_SERVER)-oci.tar
|
|
|
|
.PHONY: docker
|
|
docker: ## build the server container image
|
|
$(CONTAINER) build $(IMAGE_FORMAT) --build-arg VERSION=$(VERSION_SERVER) \
|
|
-t $(IMAGE):$(IMAGE_TAG) -t $(IMAGE):latest .
|
|
@printf 'built %s:%s\n' "$(IMAGE)" "$(IMAGE_TAG)"
|
|
|
|
.PHONY: docker-run
|
|
docker-run: docker ## run the image on 127.0.0.1:8765 with throwaway state
|
|
$(CONTAINER) run --rm -it \
|
|
-p 127.0.0.1:8765:8765 \
|
|
-e KEEPORG_MASTER_KEY="$$(head -c 32 /dev/urandom | base64)" \
|
|
-e KEEPORG_ADMIN_PASSWORD="$${KEEPORG_ADMIN_PASSWORD:-dev-password-1}" \
|
|
$(IMAGE):$(IMAGE_TAG) -addr :8765 -data /var/lib/keeporg -url http://127.0.0.1:8765
|
|
|
|
.PHONY: docker-save
|
|
docker-save: $(IMAGE_TAR) ## save the image as a tarball under dist/
|
|
|
|
$(IMAGE_TAR): docker
|
|
@mkdir -p $(DIST)
|
|
$(CONTAINER) save --format oci-archive -o $@ $(IMAGE):$(IMAGE_TAG) 2>/dev/null || \
|
|
$(CONTAINER) save -o $@ $(IMAGE):$(IMAGE_TAG)
|
|
@printf 'saved %s\n' "$@"
|
|
|
|
# --------------------------------------------------------------------- publish
|
|
|
|
FDROID_HOST ?= rcmd@garage.rcmd.space
|
|
FDROID_UNSIGNED ?= /var/lib/fdroid/unsigned
|
|
FDROID_METADATA ?= /var/lib/fdroid/metadata
|
|
|
|
.PHONY: android-publish
|
|
android-publish: $(APK) ## upload the APK and metadata to the private F-Droid repo (manual)
|
|
@printf 'publishing %s to %s:%s\n' "$(notdir $(APK))" "$(FDROID_HOST)" "$(FDROID_UNSIGNED)"
|
|
scp $(APK) $(FDROID_HOST):$(FDROID_UNSIGNED)/
|
|
scp android/metadata/$(APPID_ANDROID).yml $(FDROID_HOST):$(FDROID_METADATA)/
|
|
@printf 'uploaded. Sign and publish on the repo host with `fdroid publish && fdroid update`.\n'
|
|
|
|
.PHONY: dist
|
|
dist: deb windows ## build every artifact this host can build
|
|
@printf '\nartifacts in %s:\n' "$(DIST)"
|
|
@ls -1 $(DIST)
|
|
|
|
# -------------------------------------------------------------------- versions
|
|
|
|
.PHONY: bump
|
|
bump: ## bump a version: make bump PLATFORM=android PART=minor
|
|
scripts/bump.sh "$(PLATFORM)" "$(PART)"
|
|
|
|
.PHONY: changelog
|
|
changelog: ## add a changelog entry: make changelog PKG=server MSG="what changed"
|
|
scripts/changelog.sh "$(PKG)" \
|
|
"$(if $(filter server,$(PKG)),$(VERSION_SERVER),$(VERSION_LINUX))" "$(MSG)"
|
|
|
|
# ------------------------------------------------------------------ local runs
|
|
|
|
.PHONY: run-server
|
|
run-server: $(HOST_SERVER) ## run a server on 127.0.0.1:8765 with data in ./.devdata
|
|
KEEPORG_ADMIN_PASSWORD=$${KEEPORG_ADMIN_PASSWORD:-dev-password-1} \
|
|
$(HOST_SERVER) -addr 127.0.0.1:8765 -data .devdata -url http://127.0.0.1:8765
|
|
|
|
.PHONY: run-client
|
|
run-client: $(HOST_CLIENT) ## run the tray application with the local configuration
|
|
$(HOST_CLIENT) tray
|
|
|
|
nginx:
|
|
cat config/variables/main.json | tpl -f config/nginx/$(APP_NAME).conf.tmpl > /etc/nginx/sites-available/$(APP_NAME).conf
|
|
ln -sf /etc/nginx/sites-available/$(APP_NAME).conf /etc/nginx/sites-enabled/$(APP_NAME).conf
|
|
nginx -t
|
|
systemctl restart nginx
|
|
|
|
build-server: nginx
|
|
podman build \
|
|
--build-arg VERSION=${VERSION_SERVER} \
|
|
--platform=linux/arm64 \
|
|
-t repo.rcmd.space/$(APP_NAME):$(VERSION_SERVER) \
|
|
.
|
|
podman push repo.rcmd.space/$(APP_NAME):$(VERSION_SERVER)
|
|
podman image prune -f
|
|
|
|
deploy-server:
|
|
helm upgrade --install --namespace "apps" --create-namespace $(APP_NAME) rcmd/rcmd-base -f ./config/variables/server.values.yaml --set "image.version=$(VERSION_SERVER)"
|
|
|
|
# www
|