mirror of
https://github.com/oxynote/oxynote.git
synced 2026-10-17 10:59:20 +02:00
* overhaul the notif inbox * make hook summary abstract --------- Co-authored-by: David <SE.David@protonmail.com> Co-authored-by: david <58231312+davseby@users.noreply.github.com>
242 lines
10 KiB
Makefile
242 lines
10 KiB
Makefile
# where CI layers its build cache, kept out of the definitions below so
|
|
# that config never leaks into a local build. The compose one is an extra
|
|
# file (e2e/docker-compose.dev.ci.yaml); the prod one is docker build
|
|
# flags, because compose has no part in that image.
|
|
E2E_DEV_COMPOSE_EXTRA ?=
|
|
PROD_BUILD_EXTRA ?=
|
|
|
|
# one compose invocation per stack. Only the dev stack is a project name
|
|
# away from the others; the e2e stacks are named by their own files.
|
|
COMPOSE := docker-compose -p oxynote -f docker/docker-compose.dev.yaml
|
|
E2E_DEV_COMPOSE := docker compose -f e2e/docker-compose.dev.yaml $(E2E_DEV_COMPOSE_EXTRA)
|
|
E2E_PROD_COMPOSE := docker compose -f e2e/docker-compose.prod.yaml
|
|
PROD_COMPOSE := docker compose -f docker/prod/docker-compose.example.yaml \
|
|
-f docker/prod/docker-compose.local.yaml
|
|
PROD_NO_EMAIL_COMPOSE := docker compose -f docker/prod/docker-compose.example.yaml
|
|
|
|
# a tag names the stack an image belongs to. The e2e targets override these
|
|
# to :e2e-dev and :e2e-prod so a run cannot replace the image `make start`
|
|
# is serving from.
|
|
CORE_IMAGE_TAG ?= dev
|
|
PROD_IMAGE_TAG ?= prod
|
|
|
|
# the architecture of the Docker engine that runs the containers: on macOS
|
|
# and Windows that is its Linux VM's, not the host's. Images built here
|
|
# target it so the stacks run natively instead of under emulation.
|
|
DOCKER_ARCH ?= $(shell docker version --format '{{.Server.Arch}}' 2>/dev/null)
|
|
|
|
# the version a published image is tagged with, without a leading v. Set
|
|
# only by the release workflow; prod-publish refuses to run without it.
|
|
RELEASE_VERSION ?=
|
|
|
|
QUIET := scripts/run-quietly.sh
|
|
|
|
# backend containers + web dev server on the host with hot reload. Ctrl-c
|
|
# stops the web dev server; `make stop` stops the containers.
|
|
.PHONY: dev
|
|
dev: check-env build-go sync-changedetection-key
|
|
$(COMPOSE) up --build -d
|
|
cd web && NUXT_PUBLIC_APP_BASE_URL=http://localhost:3000 pnpm run start:dev:web
|
|
|
|
# foreground: streams all logs, ctrl-c stops the stack
|
|
.PHONY: run
|
|
run: check-env build-go sync-changedetection-key
|
|
$(COMPOSE) --profile web up --build
|
|
|
|
# background
|
|
.PHONY: start
|
|
start: check-env build-go sync-changedetection-key
|
|
$(COMPOSE) --profile web up --build -d
|
|
|
|
.PHONY: stop
|
|
stop:
|
|
$(COMPOSE) --profile web down
|
|
|
|
# the e2e stack runs on its own ports with throwaway state, so it can be
|
|
# up at the same time as the dev stack.
|
|
#
|
|
# the playwright config owns the whole cycle: globalSetup builds the stack
|
|
# and brings it up, globalTeardown stops it and drops its volumes. That is
|
|
# what makes the play button in the Playwright VS Code extension do exactly
|
|
# what this target does, so this target only starts the run. The build
|
|
# stays a make target because the hook cannot express it — goreleaser
|
|
# produces core's image, which compose cannot build at all — and the hook
|
|
# shells back out to e2e-dev-stack-build, which is also what keeps
|
|
# E2E_DEV_COMPOSE_EXTRA working from CI.
|
|
#
|
|
# Every run is therefore a full build and teardown. To iterate without
|
|
# paying that, run e2e-dev-stack-build once and drive the tests directly.
|
|
.PHONY: e2e-dev
|
|
e2e-dev:
|
|
@( cd e2e && pnpm run test:dev )
|
|
|
|
.PHONY: e2e-dev-stack-build
|
|
e2e-dev-stack-build:
|
|
@$(QUIET) "building go images" $(MAKE) build-go CORE_IMAGE_TAG=e2e-dev
|
|
@$(QUIET) "building stack images" $(E2E_DEV_COMPOSE) build
|
|
|
|
.PHONY: e2e-dev-stack-stop
|
|
e2e-dev-stack-stop:
|
|
@$(QUIET) "stopping the stack" $(E2E_DEV_COMPOSE) down -v
|
|
|
|
# the same suite against the production all-in-one image, plus the
|
|
# trust-boundary tests that only mean something there. It publishes its own
|
|
# ports, so it coexists with both the dev stack and the e2e stack.
|
|
.PHONY: e2e-prod
|
|
e2e-prod:
|
|
@( cd e2e && pnpm run test:prod )
|
|
|
|
# only the all-in-one image is built here; the backing services are pinned
|
|
# third-party images that `up` pulls on its own. Pulling them here would
|
|
# also try to pull the image just built, which has no registry to come from.
|
|
.PHONY: e2e-prod-stack-build
|
|
e2e-prod-stack-build:
|
|
@$(QUIET) "building the prod image" $(MAKE) prod-build PROD_IMAGE_TAG=e2e-prod
|
|
|
|
.PHONY: e2e-prod-stack-stop
|
|
e2e-prod-stack-stop:
|
|
@$(QUIET) "stopping the stack" $(E2E_PROD_COMPOSE) down -v
|
|
|
|
# lint gates of every component, from one place. Fixing and checking mirror
|
|
# each component's own vocabulary: web, auth-realtime and e2e pair
|
|
# lint/check-lint, and core and datagen wrap golangci-lint.
|
|
.PHONY: lint
|
|
lint:
|
|
@$(QUIET) "linting web" sh -c 'cd web && pnpm run lint'
|
|
@$(QUIET) "linting auth-realtime" sh -c 'cd server/auth-realtime && pnpm run lint'
|
|
@$(QUIET) "linting core" sh -c 'cd server/core && make lint'
|
|
@$(QUIET) "linting datagen" sh -c 'cd datagen && make lint'
|
|
@$(QUIET) "linting e2e" sh -c 'cd e2e && pnpm run lint'
|
|
@$(QUIET) "linting launcher" sh -c 'cd docker/prod/launcher && pnpm run lint'
|
|
|
|
.PHONY: check-lint
|
|
check-lint:
|
|
@$(QUIET) "lint-checking web" sh -c 'cd web && pnpm run check-lint'
|
|
@$(QUIET) "lint-checking auth-realtime" sh -c 'cd server/auth-realtime && pnpm run check-lint'
|
|
@$(QUIET) "lint-checking core" sh -c 'cd server/core && make check-lint'
|
|
@$(QUIET) "lint-checking datagen" sh -c 'cd datagen && make check-lint'
|
|
@$(QUIET) "lint-checking e2e" sh -c 'cd e2e && pnpm run check-lint'
|
|
@$(QUIET) "lint-checking launcher" sh -c 'cd docker/prod/launcher && pnpm run check-lint'
|
|
|
|
# unit tests of every component, with the coverage gates CI applies.
|
|
.PHONY: test
|
|
test:
|
|
@$(QUIET) "testing web" sh -c 'cd web && pnpm run test'
|
|
@$(QUIET) "testing auth-realtime" sh -c 'cd server/auth-realtime && pnpm run test'
|
|
@$(QUIET) "testing core" sh -c 'cd server/core && make check-coverage'
|
|
@$(QUIET) "testing datagen" sh -c 'cd datagen && make check-coverage'
|
|
@$(QUIET) "testing launcher" sh -c 'cd docker/prod/launcher && pnpm run test'
|
|
|
|
# everything CI checks, to run before a commit. Nothing here modifies a
|
|
# file.
|
|
.PHONY: check-all
|
|
check-all:
|
|
@$(MAKE) check-lint
|
|
@$(MAKE) test
|
|
@$(MAKE) e2e-prod
|
|
|
|
.PHONY: setup
|
|
setup:
|
|
cd web && pnpm run setup
|
|
cd server/auth-realtime && pnpm install
|
|
cd docker/prod/launcher && pnpm install
|
|
cd e2e && pnpm run setup
|
|
cd server/core && go mod download
|
|
cd datagen && go mod download
|
|
for f in core auth-realtime web; do \
|
|
test -f docker/env/$$f.local.env || cp docker/env/$$f.example.env docker/env/$$f.local.env; \
|
|
done
|
|
test -f web/.env || cp docker/env/web.example.env web/.env
|
|
|
|
.PHONY: setup-force
|
|
setup-force: setup
|
|
for f in core auth-realtime web; do \
|
|
cp docker/env/$$f.example.env docker/env/$$f.local.env; \
|
|
done
|
|
cp docker/env/web.example.env web/.env
|
|
|
|
.PHONY: deps
|
|
deps:
|
|
cd web && pnpm run deps
|
|
cd server/auth-realtime && pnpm install
|
|
cd docker/prod/launcher && pnpm install
|
|
cd e2e && pnpm install
|
|
cd server/core && go mod download
|
|
cd datagen && go mod download
|
|
|
|
# goreleaser snapshot builds the go binaries + their images
|
|
.PHONY: build-go
|
|
build-go:
|
|
cd server/core && make build IMAGE_TAG=$(CORE_IMAGE_TAG)
|
|
cd datagen && make build
|
|
|
|
# the all-in-one image, for the Docker engine's own architecture. Core must
|
|
# come from goreleaser, so the binary is built first and staged where the
|
|
# Dockerfile's COPY expects it — a bare `docker build` is not supported.
|
|
.PHONY: prod-build
|
|
prod-build:
|
|
mkdir -p docker/prod/.build/$(DOCKER_ARCH)
|
|
@$(QUIET) "building the core binary" sh -c 'cd server/core && make build-binary-snapshot DOCKER_ARCH=$(DOCKER_ARCH) OUTPUT=$(CURDIR)/docker/prod/.build/$(DOCKER_ARCH)/oxynote-core'
|
|
docker build --platform linux/$(DOCKER_ARCH) $(PROD_BUILD_EXTRA) -f docker/prod/Dockerfile -t ghcr.io/oxynote/oxynote:$(PROD_IMAGE_TAG) .
|
|
|
|
# the published image: the same Dockerfile as prod-build, but core's binary
|
|
# is built from the tag instead of as a snapshot — a snapshot would report
|
|
# itself as version dev in a dev environment — and the result is pushed for
|
|
# linux/amd64 and linux/arm64 as latest and the version rather than loaded
|
|
# locally. These are the only two tags the registry gets, and a pull of
|
|
# either resolves to the puller's own architecture. The version and commit
|
|
# also become OCI labels, which is what links the package to this
|
|
# repository on ghcr.
|
|
.PHONY: prod-publish
|
|
prod-publish:
|
|
@test -n "$(RELEASE_VERSION)" || { echo "RELEASE_VERSION is required, e.g. 1.2.3"; exit 1; }
|
|
mkdir -p docker/prod/.build/amd64 docker/prod/.build/arm64
|
|
@$(QUIET) "building the amd64 core binary" sh -c 'cd server/core && make build-binary DOCKER_ARCH=amd64 OUTPUT=$(CURDIR)/docker/prod/.build/amd64/oxynote-core'
|
|
@$(QUIET) "building the arm64 core binary" sh -c 'cd server/core && make build-binary DOCKER_ARCH=arm64 OUTPUT=$(CURDIR)/docker/prod/.build/arm64/oxynote-core'
|
|
docker buildx build --platform linux/amd64,linux/arm64 $(PROD_BUILD_EXTRA) \
|
|
-f docker/prod/Dockerfile \
|
|
--build-arg IMAGE_VERSION=$(RELEASE_VERSION) \
|
|
--build-arg IMAGE_REVISION=$$(git rev-parse HEAD) \
|
|
-t ghcr.io/oxynote/oxynote:latest \
|
|
-t ghcr.io/oxynote/oxynote:$(RELEASE_VERSION) \
|
|
--push .
|
|
|
|
# build the image, then run the example deployment against it on
|
|
# http://localhost:8080 (ctrl-c stops it). The local override adds the mail
|
|
# sink the example only describes in a comment, so the product runs with
|
|
# email. Read the delivered mail at http://localhost:8025.
|
|
.PHONY: prod-run
|
|
prod-run: prod-build
|
|
OXYNOTE_IMAGE_TAG=$(PROD_IMAGE_TAG) $(PROD_COMPOSE) up
|
|
|
|
# the same without the local override: the example as it stands, which runs
|
|
# the product without email. Both targets share one compose project and its
|
|
# data volume, so prod-stop stops either, and --remove-orphans takes down a
|
|
# mailpit an earlier prod-run left behind.
|
|
.PHONY: prod-run-no-email
|
|
prod-run-no-email: prod-build
|
|
OXYNOTE_IMAGE_TAG=$(PROD_IMAGE_TAG) $(PROD_NO_EMAIL_COMPOSE) up --remove-orphans
|
|
|
|
.PHONY: prod-stop
|
|
prod-stop:
|
|
$(PROD_COMPOSE) down
|
|
|
|
# changedetection.io generates its API key on first boot; start it alone and
|
|
# copy the key into core.local.env before the rest of the stack (core reads
|
|
# its env only at container creation).
|
|
.PHONY: sync-changedetection-key
|
|
sync-changedetection-key:
|
|
$(COMPOSE) up -d changedetection
|
|
./docker/sync-changedetection-key.sh
|
|
|
|
# a variable renamed in a template never reaches an env file that already
|
|
# exists, so the stack starts against a name nobody reads. check-env fails
|
|
# before that happens; sync-env rewrites the local files from the
|
|
# templates, keeping the value of every variable they still list.
|
|
.PHONY: check-env
|
|
check-env:
|
|
./docker/sync-env.sh
|
|
|
|
.PHONY: sync-env
|
|
sync-env:
|
|
./docker/sync-env.sh --fix
|