303 lines
10 KiB
YAML
303 lines
10 KiB
YAML
version: '3'
|
|
|
|
vars:
|
|
BINARY: renewcms
|
|
DIST_DIR: internal/infrastructure/assets/dist
|
|
VERSION:
|
|
sh: git describe --tags --always --dirty
|
|
LDFLAGS: -s -w -X main.version={{.VERSION}}
|
|
RELEASE_TARGETS: [windows/amd64, windows/arm64, linux/amd64, linux/arm64, darwin/amd64, darwin/arm64]
|
|
|
|
env:
|
|
CGO_ENABLED: '0'
|
|
|
|
tasks:
|
|
default:
|
|
cmds:
|
|
- task --list
|
|
silent: true
|
|
|
|
setup:
|
|
desc: Install dev tools, Go modules and frontend dependencies
|
|
cmds:
|
|
- go install github.com/air-verse/air@latest
|
|
- go install github.com/go-gremlins/gremlins/cmd/gremlins@latest
|
|
- go mod download
|
|
- task: web:install
|
|
|
|
dev:
|
|
desc: Run the API (air, hot reload) and the SPA (Vite, HMR) together → http://localhost:5173
|
|
deps: [dev:api, dev:web]
|
|
|
|
dev:api:
|
|
desc: Run the API with hot reload on :8080 (SPA not embedded)
|
|
cmds:
|
|
- air
|
|
|
|
dev:web:
|
|
desc: Run the Vite dev server on :5173, proxying /v1 to the API
|
|
dir: web
|
|
deps: [web:install]
|
|
cmds:
|
|
- bun dev
|
|
|
|
test:
|
|
desc: Run the Go tests
|
|
cmds:
|
|
- go test -tags nofrontend ./test/... -count=1 {{.CLI_ARGS}}
|
|
|
|
cover:
|
|
desc: Go test coverage of the use cases → tmp/coverage.out
|
|
cmds:
|
|
- mkdir -p tmp
|
|
- go test -tags nofrontend ./test/... -count=1 -coverpkg=./internal/infrastructure/useCases/... -coverprofile=tmp/coverage.out
|
|
- go tool cover -func=tmp/coverage.out
|
|
|
|
mutate:
|
|
desc: Gremlins mutation testing of the use cases → tmp/gremlins.json
|
|
cmds:
|
|
- mkdir -p tmp
|
|
- gremlins unleash --integration --tags nofrontend --coverpkg=./internal/infrastructure/useCases/...,./api/middleware/...,./api/pkg/claims/... --timeout-coefficient 10 --output tmp/gremlins.json ./internal/infrastructure/useCases ./api/middleware ./api/pkg/claims
|
|
|
|
test:web:
|
|
desc: Run the SPA test suite (Vitest) in run mode
|
|
dir: web
|
|
deps: [web:install]
|
|
cmds:
|
|
- bun run vitest run
|
|
|
|
visual:check:
|
|
desc: Screenshot a design screen's states next to the built SPA (Playwright) → tmp/visual/<screen>/
|
|
dir: web
|
|
deps: [web:install]
|
|
cmds:
|
|
- bun visual/check.ts {{.CLI_ARGS}}
|
|
|
|
lint:
|
|
desc: gofmt check, go vet, eslint (--fix) and vue-tsc
|
|
cmds:
|
|
- task: lint:go
|
|
- task: lint:web
|
|
|
|
lint:go:
|
|
cmds:
|
|
- cmd: test -z "$(gofmt -l .)" || (gofmt -l . && echo "unformatted Go files, run 'task fmt'" && exit 1)
|
|
silent: true
|
|
- go vet -tags nofrontend ./...
|
|
|
|
lint:web:
|
|
dir: web
|
|
deps: [web:install]
|
|
cmds:
|
|
- bun lint
|
|
- bun run type-check
|
|
|
|
fmt:
|
|
desc: Format Go and frontend sources
|
|
cmds:
|
|
- gofmt -w .
|
|
- bun run --cwd web format
|
|
|
|
web:install:
|
|
internal: true
|
|
dir: web
|
|
sources: [package.json, bun.lock]
|
|
generates: ['node_modules/.bin/vite*']
|
|
cmds:
|
|
- bun install --frozen-lockfile
|
|
|
|
build:web:
|
|
desc: Type-check and build the SPA into {{.DIST_DIR}}
|
|
dir: web
|
|
deps: [web:install]
|
|
sources: [src/**/*, public/**/*, index.html, vite.config.ts, tsconfig*.json, package.json, bun.lock]
|
|
generates: ['../{{.DIST_DIR}}/index.html']
|
|
cmds:
|
|
- bun run build
|
|
|
|
build:
|
|
desc: Production binary for this machine → bin/{{.BINARY}}
|
|
deps: [build:web]
|
|
cmds:
|
|
- go build -trimpath -ldflags "{{.LDFLAGS}}" -o bin/{{.BINARY}}{{exeExt}} ./cmd/server
|
|
|
|
release:
|
|
desc: Cross-compile and sign production binaries for all release targets → bin/ (needs RELEASE_SIGNING_KEY)
|
|
cmds:
|
|
- task: release:check
|
|
- task: build:web
|
|
- for: { var: RELEASE_TARGETS }
|
|
task: build:target
|
|
vars:
|
|
GOOS: '{{index (splitList "/" .ITEM) 0}}'
|
|
GOARCH: '{{index (splitList "/" .ITEM) 1}}'
|
|
- go run ./cmd/releasesign checksums bin --match "{{.BINARY}}_{{.VERSION}}_*"
|
|
- go run ./cmd/releasesign sign bin/checksums.txt
|
|
- go run ./cmd/releasesign verify bin/checksums.txt
|
|
|
|
release:check:
|
|
internal: true
|
|
cmds:
|
|
- cmd: 'test -n "$RELEASE_SIGNING_KEY" || { echo "RELEASE_SIGNING_KEY is not set. Point it at the private release key file (see task release:keygen)." >&2; exit 1; }'
|
|
silent: true
|
|
- cmd: 'test -f "$RELEASE_SIGNING_KEY" || { echo "RELEASE_SIGNING_KEY points to a missing file: $RELEASE_SIGNING_KEY" >&2; exit 1; }'
|
|
silent: true
|
|
- mkdir -p tmp
|
|
- echo probe > tmp/release-probe.txt
|
|
- defer: rm -f tmp/release-probe.txt tmp/release-probe.txt.sig
|
|
- go run ./cmd/releasesign sign tmp/release-probe.txt
|
|
|
|
release:keygen:
|
|
desc: One-time release key pair. Usage, task release:keygen -- <private key path outside the repository>
|
|
cmds:
|
|
- cmd: 'test -n "{{.CLI_ARGS}}" || { echo "usage: task release:keygen -- <private key path outside the repository>" >&2; exit 1; }'
|
|
silent: true
|
|
- go run ./cmd/releasesign keygen {{.CLI_ARGS}}
|
|
|
|
build:target:
|
|
internal: true
|
|
vars:
|
|
EXT: '{{if eq .GOOS "windows"}}.exe{{end}}'
|
|
env:
|
|
GOOS: '{{.GOOS}}'
|
|
GOARCH: '{{.GOARCH}}'
|
|
cmds:
|
|
- go build -trimpath -ldflags "{{.LDFLAGS}}" -o bin/{{.BINARY}}_{{.VERSION}}_{{.GOOS}}_{{.GOARCH}}{{.EXT}} ./cmd/server
|
|
|
|
run:
|
|
desc: Build and run the production binary locally
|
|
deps: [build]
|
|
cmds:
|
|
- ./bin/{{.BINARY}}{{exeExt}}
|
|
|
|
docker:build:
|
|
desc: Build the Docker image tagged with the git describe version and latest
|
|
cmds:
|
|
- docker build --build-arg VERSION={{.VERSION}} -t renewcms:{{.VERSION}} -t renewcms:latest .
|
|
|
|
docker:run:
|
|
desc: Run the image once on a named volume, reading docker.env, on http://localhost:8080
|
|
cmds:
|
|
- docker run --rm --name renewcms -p 8080:8080 --env-file docker.env -e DB_FILE=/data/renewcms.db -e UPLOAD_DIR=/data/uploads -v renewcms-data:/data --stop-timeout 15 renewcms:latest
|
|
|
|
docker:up:
|
|
desc: Build from the local sources and start the Compose service in the background
|
|
env:
|
|
VERSION: '{{.VERSION}}'
|
|
cmds:
|
|
- docker compose -f docker-compose.yml -f docker-compose.build.yml up -d --build
|
|
|
|
docker:pull:
|
|
desc: Pull the published image (RENEWCMS_IMAGE, default the beta tag)
|
|
cmds:
|
|
- docker compose pull
|
|
|
|
docker:upgrade:
|
|
desc: Pull the published image and recreate the Compose service on it
|
|
cmds:
|
|
- docker compose pull
|
|
- docker compose up -d
|
|
|
|
docker:down:
|
|
desc: Stop and remove the Compose service (the data volume is kept)
|
|
cmds:
|
|
- docker compose down
|
|
|
|
docker:logs:
|
|
desc: Follow the Compose service logs
|
|
cmds:
|
|
- docker compose logs -f renewcms
|
|
|
|
docker:publish:
|
|
desc: 'Build and push the multi-arch image to the registry. Usage, task docker:publish [-- --dry-run]'
|
|
vars:
|
|
REGISTRY_IMAGE:
|
|
sh: echo "${REGISTRY_IMAGE-git.floriansylvain.fr/fsylvain/renewcms}"
|
|
cmds:
|
|
- cmd: |
|
|
IMAGE='{{.REGISTRY_IMAGE}}'
|
|
V='{{.VERSION}}'
|
|
ARGS='{{.CLI_ARGS}}'
|
|
fail() { echo "docker:publish refused: $1" >&2; exit 1; }
|
|
DRY=no
|
|
case "$ARGS" in
|
|
"") ;;
|
|
"--dry-run") DRY=yes ;;
|
|
*) fail "unknown argument '$ARGS' (only --dry-run is accepted)" ;;
|
|
esac
|
|
[ -n "$IMAGE" ] || fail "REGISTRY_IMAGE is empty"
|
|
case "$V" in
|
|
v*) ;;
|
|
*) fail "VERSION '$V' must be an exact tag vX.Y.Z or vX.Y.Z-<pre>" ;;
|
|
esac
|
|
R="${V#v}"
|
|
CORE="${R%%-*}"
|
|
PRE=""
|
|
[ "$CORE" = "$R" ] || PRE="${R#*-}"
|
|
MAJOR="${CORE%%.*}"
|
|
REST="${CORE#*.}"
|
|
MINOR="${REST%%.*}"
|
|
PATCH="${REST#*.}"
|
|
for N in "$MAJOR" "$MINOR" "$PATCH"; do
|
|
case "$N" in
|
|
""|*[!0-9]*) fail "VERSION '$V' must be an exact tag vX.Y.Z or vX.Y.Z-<pre>" ;;
|
|
esac
|
|
done
|
|
[ "$CORE" = "$MAJOR.$MINOR.$PATCH" ] || fail "VERSION '$V' must be an exact tag vX.Y.Z or vX.Y.Z-<pre>"
|
|
if [ "$R" != "$CORE" ]; then
|
|
case "$PRE" in
|
|
""|*[!0-9A-Za-z.-]*) fail "VERSION '$V' has an invalid pre-release part" ;;
|
|
esac
|
|
PRENAME="${PRE%%[!A-Za-z]*}"
|
|
[ -n "$PRENAME" ] || fail "VERSION '$V' pre-release must start with letters, for example -beta or -rc.1"
|
|
fi
|
|
if [ "$DRY" = no ]; then
|
|
[ -z "$(git status --porcelain)" ] || fail "the work tree has uncommitted changes"
|
|
[ "$(git describe --tags --exact-match 2>/dev/null)" = "$V" ] || fail "VERSION '$V' is not an exact git tag on HEAD (an override is accepted only with --dry-run)"
|
|
fi
|
|
TAGS="$V $R"
|
|
if [ -n "$PRE" ]; then
|
|
TAGS="$TAGS $PRENAME"
|
|
else
|
|
TAGS="$TAGS $MAJOR.$MINOR latest"
|
|
fi
|
|
ARGS_TAGS=""
|
|
LIST=""
|
|
for T in $TAGS; do
|
|
ARGS_TAGS="$ARGS_TAGS -t $IMAGE:$T"
|
|
LIST="$LIST, $T"
|
|
done
|
|
echo "image: $IMAGE"
|
|
echo "tags: ${LIST#, }"
|
|
if [ "$DRY" = yes ]; then
|
|
echo "dry run: building for the host platform only, loading locally, pushing nothing"
|
|
docker buildx build --load --build-arg VERSION="$V" $ARGS_TAGS .
|
|
exit 0
|
|
fi
|
|
BUILDER="$(docker buildx inspect 2>&1)" || fail "docker buildx is not available or no builder is selected (see docs/docker.md, Maintainer: publishing)"
|
|
STORE="$(docker info 2>&1)"
|
|
case "$BUILDER" in
|
|
*linux/amd64*) ;;
|
|
*) fail "the selected buildx builder does not support linux/amd64 (see docs/docker.md, Maintainer: publishing)" ;;
|
|
esac
|
|
case "$BUILDER" in
|
|
*linux/arm64*) ;;
|
|
*) fail "the selected buildx builder does not support linux/arm64: enable QEMU/binfmt (see docs/docker.md, Maintainer: publishing)" ;;
|
|
esac
|
|
case "$BUILDER" in
|
|
*"Driver:"*"docker-container"*|*"Driver:"*"kubernetes"*|*"Driver:"*"remote"*) ;;
|
|
*)
|
|
case "$STORE" in
|
|
*io.containerd.snapshotter*) ;;
|
|
*) fail "the selected builder uses the plain docker driver without the containerd image store, which cannot build multi-platform images: run 'docker buildx create --use' (see docs/docker.md, Maintainer: publishing)" ;;
|
|
esac
|
|
;;
|
|
esac
|
|
docker buildx build --platform linux/amd64,linux/arm64 --build-arg VERSION="$V" $ARGS_TAGS --push .
|
|
silent: true
|
|
|
|
clean:
|
|
desc: Remove build output, the built SPA and tmp/
|
|
cmds:
|
|
- rm -rf bin tmp {{.DIST_DIR}}
|