Clean up build tooling and Dockerfile

- Dockerfile cross-compiles Go for the target platform (amd64/arm64
  without emulation), uses templ pinned in go.mod, runs as uid 1000 on
  alpine and lets FRONTEND_IMAGE pin the frontend version
- justfile: tools via go tool, `test` (unit) and `test-integration`
  (uses the active docker context), no hardcoded paths, no tailwind or
  frontend recipes
- Removed app.yaml, init.sh, tailwind.config.js and the tracked .idea
  folder
This commit is contained in:
Sansan committed 2026-10-06 15:21:46 +02:00
1 parent 7b144ae419
commit 59dfff38bc
13 files changed
+58 -304

No files matched your search

+3 -9
View File
@@ -14,18 +14,12 @@ compose.yaml
*.pprof
main
conf.yaml
output.css
tailwindcss
.env
# Node.js (frontend build artifacts)
node_modules
package.json
package-lock.json
# Test files
# Test files and fixtures
*_test.go
integration_test.go
internal/testutil
*_dump.sql
# OS
.DS_Store
-5
View File
@@ -3,13 +3,8 @@ tmp
.idea
*templ.go
conf.yaml
output.css
compose.yaml
tailwindcss
.env
node_modules
package.json
package-lock.json
cpu.pprof
main
-8
View File
@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
-16
View File
@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="Go" enabled="true">
<buildTags>
<option name="customFlags">
<array>
<option value="prod" />
</array>
</option>
</buildTags>
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
-5
View File
@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>
-36
View File
@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="music_prod@192.168.86.100" uuid="e63caf71-2b1a-400d-9cc4-4b87b1f9b807">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://192.168.86.100:5432/music_prod</jdbc-url>
</data-source>
<data-source source="LOCAL" name="music_test@192.168.86.100" uuid="d063e147-d7f0-48db-9f04-57a108261ef1">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://192.168.86.100:5432/music_test</jdbc-url>
</data-source>
<data-source source="LOCAL" name="music_test@ssh.sanplex.xyz" uuid="a9cf3eb8-abc2-479b-9f27-12b4cbbfc321">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://ssh.sanplex.xyz:9432/music_test</jdbc-url>
</data-source>
<data-source source="LOCAL" name="music_prod@ssh.sanplex.xyz" uuid="20917ace-4e9a-4273-bdb7-13ebc774cbba">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://ssh.sanplex.xyz:9432/music_prod</jdbc-url>
</data-source>
<data-source source="LOCAL" name="music_test2@localhost" uuid="a423ab0a-55b0-42e1-8070-25d8ef34bfac">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://localhost:5432/music_test2</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/MusicServer.iml" filepath="$PROJECT_DIR$/.idea/MusicServer.iml" />
</modules>
</component>
</project>
Generated
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+28 -25
View File
@@ -1,36 +1,39 @@
# Stage 1: Build backend
FROM golang:1.25-alpine as build_go
RUN apk add --no-cache curl
# The frontend is built and published separately; override to pin a version.
ARG FRONTEND_IMAGE=gitea.sanplex.xyz/sansan/musicfrontend:latest
# Stage 1: Build the backend on the build machine and cross-compile for the target platform,
# so multi-platform builds need no emulation.
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS build
ARG TARGETOS TARGETARCH
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY cmd/ ./cmd/
COPY internal/ ./internal/
RUN find . -name "*_test.go" -delete && \
find . -name "integration_test.go" -delete
RUN go install github.com/a-h/templ/cmd/templ@latest
RUN templ generate
RUN go build -o main cmd/main.go
RUN go tool templ generate
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
go build -trimpath -ldflags="-s -w" -tags timetzdata -o main ./cmd
# Stage 2: Final image
FROM golang:1.25-alpine
# Stage 2: Frontend static files (the same for every platform)
FROM --platform=$BUILDPLATFORM ${FRONTEND_IMAGE} AS frontend
# Empty API_HOSTNAME makes the frontend use relative paths
RUN echo "window.__RUNTIME_CONFIG__ = { API_HOSTNAME: '' };" > /usr/share/nginx/html/config.js
# Stage 3: Final image. No RUN steps here, so no emulation is needed for other architectures.
FROM alpine:3.22
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
WORKDIR /app
COPY --from=build /app/main .
COPY --from=frontend /usr/share/nginx/html /frontend
COPY ./songs/ ./songs/
# The music volume must be readable by uid 1000, and writable for the .id file cleanup during sync.
USER 1000:1000
EXPOSE 8080
VOLUME /sorted
VOLUME /characters
COPY --from=build_go /app/main .
COPY --from=gitea.sanplex.xyz/sansan/musicfrontend:latest /usr/share/nginx/html /frontend
# Generate config.js with empty API_HOSTNAME (relative paths)
RUN echo "window.__RUNTIME_CONFIG__ = { API_HOSTNAME: '' };" > /frontend/config.js
COPY ./songs/ ./songs/
# Required: DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_NAME, MUSIC_PATH, CHARACTERS_PATH
ENV PORT=8080
ENV PORT 8080
ENV DB_HOST ""
ENV DB_PORT ""
ENV DB_USERNAME ""
ENV DB_PASSWORD ""
ENV DB_NAME ""
ENV MUSIC_PATH ""
ENV CHARACTERS_PATH ""
CMD ./main
CMD ["./main"]
-8
View File
@@ -1,8 +0,0 @@
application: musicserver
version: 2.2.0
runtime: go115
api_version: go1
handlers:
- url: /.*
script: _go_app
-17
View File
@@ -1,17 +0,0 @@
#! /bin/sh
#envsubst < ./frontend/src/arne.js
sed -i 's^$HOSTNAME^'"$HOSTNAME"'^g' ./frontend/src/arne.js
cat ./frontend/src/arne.js
cd ./frontend/
npm install
npm run build
cd ..
./MusicServer
+27 -150
View File
@@ -1,104 +1,50 @@
set dotenv-load
# Build the application
# Build and run the unit tests
all: build test
templ-install:
@if ! command -v templ > /dev/null; then \
read -p "Go's 'templ' is not installed on your machine. Do you want to install it? [Y/n] " choice; \
if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \
go install github.com/a-h/templ/cmd/templ@latest; \
if [ ! -x "$$(command -v templ)" ]; then \
echo "templ installation failed. Exiting..."; \
exit 1; \
fi; \
else \
echo "You chose not to install templ. Exiting..."; \
exit 1; \
fi; \
fi
templ-build: templ-install
# Generate templ components (version pinned in go.mod)
templ-build:
@echo "Building templ..."
@templ generate
tailwind-macos:
@if [ ! -f tailwindcss ]; then curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-macos-arm64 -o tailwindcss; fi
@chmod +x tailwindcss
tailwind-linux:
@if [ ! -f tailwindcss ]; then curl -sL https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 -o tailwindcss; fi
@chmod +x tailwindcss
tailwind-build:
@echo "Building tailwind..."
@./tailwindcss -i cmd/web/assets/css/input.css -o cmd/web/assets/css/output.css
@go tool templ generate
sqlc-generate:
@echo "Generating sqlc schema..."
@echo "Generating sqlc code..."
@sqlc generate
migrate-create name:
@migrate create -ext sql -dir internal/db/migrations -seq {{name}}
swag-install:
@if ! command -v swag > /dev/null; then \
read -p "Swag is not installed on your machine. Do you want to install it? [Y/n] " choice; \
if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \
go install github.com/swaggo/swag/cmd/swag@latest; \
if [ ! -x "$$(command -v swag)" ]; then \
echo "swag installation failed. Exiting..."; \
exit 1; \
fi; \
else \
echo "You chose not to install swag. Exiting..."; \
exit 1; \
fi; \
fi
swag-generate: swag-install
# Generate OpenAPI docs (version pinned in go.mod)
swag-generate:
@echo "Generating OpenAPI docs..."
@swag init -g internal/server/routes.go -o cmd/docs
@go tool swag init -g internal/server/routes.go -o cmd/docs --parseInternal
frontend-install:
@if ! command -v npm > /dev/null; then \
echo "npm is not installed on your machine. Please install Node.js first."; \
exit 1; \
fi
@cd cmd/frontend && npm install
frontend-build: frontend-install
@echo "Building frontend..."
@cd cmd/frontend && npm run build
[no-cd]
build: sqlc-generate templ-build swag-generate
@echo "Building..."
@go build -o main cmd/main.go
@echo "Building..."
@go build -o main cmd/main.go
run:
@templ generate
run: templ-build
@go run cmd/main.go
build-run: build
@go run cmd/main.go
@./main
test: build
@echo "Starting test database container..."
@docker compose -f compose.test.yaml up -d
@sleep 10
@echo "Running integration tests..."
@just test-integration
@echo "Stopping test database container..."
@just test-integration-down
# Unit tests: no database or Docker needed
test:
@go test -race ./...
# Integration tests against PostgreSQL in Docker (testcontainers), using the active docker context.
# Pass a pattern to run a subset, e.g. `just test-integration TestSQLDumps`
test-integration pattern=".":
@DOCKER_HOST="${DOCKER_HOST:-$(docker context inspect --format '{{{{.Endpoints.docker.Host}}')}" \
go test -tags integration -race -timeout 15m -run '{{pattern}}' ./...
# Clean the binary
clean:
@echo "Cleaning..."
@rm -f main
docker-build:
@echo "Building Docker image with docker..."
@docker build -t music-server .
docker-run:
@@ -107,82 +53,13 @@ docker-run:
docker-down:
@docker compose down
# Run integration tests with docker
# Starts a test PostgreSQL container, runs tests, then cleans up
test-integration:
@echo "Cleaning old test database..."
@docker compose -f compose.test.yaml down -v
@echo "Starting fresh test database container..."
@docker compose -f compose.test.yaml up -d
@sleep 10
@echo "Running integration tests..."
@DOCKER_HOST=unix:///Users/sebastian/.docker/run/docker.sock TESTCONTAINERS_RYUK_CONTAINER_NETWORK=testcontainers-test-net DB_HOST=localhost DB_PORT=5433 DB_USERNAME=testuser DB_PASSWORD=testpass DB_NAME=music_server_test MUSIC_PATH=/Users/sebastian/projects/MusicServer/testMusic CHARACTERS_PATH=/Users/sebastian/projects/MusicServer/testCharacters PORT=8081 LOG_LEVEL=debug LOG_JSON=false go test -v -timeout 30m -p 1 -parallel 1 ./internal/...
# Alternative: Run integration tests using testcontainers with docker provider
test-integration-tc:
@echo "Creating test network (if it doesn't exist)..."
@docker network inspect testcontainers-test-net > /dev/null 2>&1 || docker network create testcontainers-test-net
@echo "Running integration tests with testcontainers (docker provider)..."
@DOCKER_HOST=unix:///Users/sebastian/.docker/run/docker.sock TESTCONTAINERS_PROVIDER=docker TESTCONTAINERS_RYUK_CONTAINER_NETWORK=testcontainers-test-net go test -v -timeout 30m .
# Run SQL dump migration tests
# Tests that the application can start with each SQL dump and migrations work correctly
test-sql-dump:
@echo "Creating test network (if it doesn't exist)..."
@docker network inspect testcontainers-test-net > /dev/null 2>&1 || docker network create testcontainers-test-net
@echo "Running SQL dump migration tests..."
@DOCKER_HOST=unix:///Users/sebastian/.docker/run/docker.sock TESTCONTAINERS_PROVIDER=docker TESTCONTAINERS_RYUK_CONTAINER_NETWORK=testcontainers-test-net go test -v -timeout 15m -run "TestMusic.*Dump" ./internal/server/
# Run individual SQL dump tests
test-sql-dump-ref:
@echo "Creating test network (if it doesn't exist)..."
@docker network inspect testcontainers-test-net > /dev/null 2>&1 || docker network create testcontainers-test-net
@echo "Running SQL dump migration test for ref dump..."
@DOCKER_HOST=unix:///Users/sebastian/.docker/run/docker.sock TESTCONTAINERS_PROVIDER=docker TESTCONTAINERS_RYUK_CONTAINER_NETWORK=testcontainers-test-net go test -v -timeout 5m -run "TestMusicRefDump" ./internal/server/
test-sql-dump-test:
@echo "Creating test network (if it doesn't exist)..."
@docker network inspect testcontainers-test-net > /dev/null 2>&1 || docker network create testcontainers-test-net
@echo "Running SQL dump migration test for test dump..."
@DOCKER_HOST=unix:///Users/sebastian/.docker/run/docker.sock TESTCONTAINERS_PROVIDER=docker TESTCONTAINERS_RYUK_CONTAINER_NETWORK=testcontainers-test-net go test -v -timeout 5m -run "TestMusicTestDump" ./internal/server/
test-sql-dump-prod:
@echo "Creating test network (if it doesn't exist)..."
@docker network inspect testcontainers-test-net > /dev/null 2>&1 || docker network create testcontainers-test-net
@echo "Running SQL dump migration test for prod dump..."
@DOCKER_HOST=unix:///Users/sebastian/.docker/run/docker.sock TESTCONTAINERS_PROVIDER=docker TESTCONTAINERS_RYUK_CONTAINER_NETWORK=testcontainers-test-net go test -v -timeout 5m -run "TestMusicProdDump" ./internal/server/
# Stop and remove test database container
test-integration-down:
@echo "Stopping test database container..."
@docker compose -f compose.test.yaml down -v
# Build and run with Docker Compose
build-run-docker:
@echo "Building and running with Docker Compose..."
@docker compose up --build
# Shutdown containers
stop-docker:
@echo "Stopping containers..."
@docker compose down
watch-templ:
templ generate --watch --proxy="http://localhost:8080" --cmd="go run ."
@go tool templ generate --watch --proxy="http://localhost:8080" --cmd="go run ./cmd"
# Live Reload
# Live reload
watch:
@if command -v air > /dev/null; then \
air; \
echo "Watching...";\
else \
read -p "Go's 'air' is not installed on your machine. Do you want to install it? [Y/n] " choice; \
if [ "$$choice" != "n" ] && [ "$$choice" != "N" ]; then \
go install github.com/air-verse/air@latest; \
air; \
echo "Watching...";\
else \
echo "You chose not to install air. Exiting..."; \
exit 1; \
fi; \
@if ! command -v air > /dev/null; then \
echo "air is not installed. Install it with: go install github.com/air-verse/air@latest"; \
exit 1; \
fi
@air
-11
View File
@@ -1,11 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./cmd/web/**/*.html", "./cmd/web/**/*.templ",
],
theme: {
extend: {},
},
plugins: [],
}