Commit 6789d26b0c9569013bfef97d761023356c8dd755
1 parent
f9ea66fc
chore: Dockerfile, docker-compose, GitHub Actions, Makefile, dev volume scaffolding
Showing
11 changed files
with
325 additions
and
0 deletions
.github/workflows/build.yaml
0 → 100644
| 1 | +# vibe_erp — main CI build. | ||
| 2 | +# | ||
| 3 | +# Runs the Gradle build and tests on every PR and on pushes to main. | ||
| 4 | +# A separate job builds (but does not push) the Docker image on main. | ||
| 5 | + | ||
| 6 | +name: build | ||
| 7 | + | ||
| 8 | +on: | ||
| 9 | + push: | ||
| 10 | + branches: [main] | ||
| 11 | + pull_request: | ||
| 12 | + | ||
| 13 | +jobs: | ||
| 14 | + gradle: | ||
| 15 | + name: gradle build | ||
| 16 | + runs-on: ubuntu-latest | ||
| 17 | + steps: | ||
| 18 | + - name: Checkout | ||
| 19 | + uses: actions/checkout@v4 | ||
| 20 | + | ||
| 21 | + - name: Set up JDK 21 | ||
| 22 | + uses: actions/setup-java@v4 | ||
| 23 | + with: | ||
| 24 | + distribution: temurin | ||
| 25 | + java-version: "21" | ||
| 26 | + | ||
| 27 | + - name: Cache Gradle | ||
| 28 | + uses: actions/cache@v4 | ||
| 29 | + with: | ||
| 30 | + path: | | ||
| 31 | + ~/.gradle/caches | ||
| 32 | + ~/.gradle/wrapper | ||
| 33 | + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle/libs.versions.toml') }} | ||
| 34 | + restore-keys: | | ||
| 35 | + gradle-${{ runner.os }}- | ||
| 36 | + | ||
| 37 | + - name: Build | ||
| 38 | + run: ./gradlew build --no-daemon | ||
| 39 | + | ||
| 40 | + - name: api-v1 tests | ||
| 41 | + run: ./gradlew :api:api-v1:test --no-daemon | ||
| 42 | + | ||
| 43 | + - name: Upload test reports on failure | ||
| 44 | + if: failure() | ||
| 45 | + uses: actions/upload-artifact@v4 | ||
| 46 | + with: | ||
| 47 | + name: test-reports | ||
| 48 | + path: | | ||
| 49 | + **/build/reports/tests/ | ||
| 50 | + **/build/test-results/ | ||
| 51 | + | ||
| 52 | + docker: | ||
| 53 | + name: docker image | ||
| 54 | + runs-on: ubuntu-latest | ||
| 55 | + needs: gradle | ||
| 56 | + if: github.event_name == 'push' && github.ref == 'refs/heads/main' | ||
| 57 | + steps: | ||
| 58 | + - name: Checkout | ||
| 59 | + uses: actions/checkout@v4 | ||
| 60 | + | ||
| 61 | + - name: Set up Docker Buildx | ||
| 62 | + uses: docker/setup-buildx-action@v3 | ||
| 63 | + | ||
| 64 | + - name: Build image (no push) | ||
| 65 | + uses: docker/build-push-action@v6 | ||
| 66 | + with: | ||
| 67 | + context: . | ||
| 68 | + file: Dockerfile | ||
| 69 | + push: false | ||
| 70 | + tags: vibeerp/vibe-erp:ci |
.github/workflows/check-architecture.yaml
0 → 100644
| 1 | +# vibe_erp — architectural guardrail CI. | ||
| 2 | +# | ||
| 3 | +# Defends two rules from CLAUDE.md guardrails #9 and #10: | ||
| 4 | +# - PBCs may NEVER import other PBCs | ||
| 5 | +# - api-v1 and reference-customer code may only see api.v1.* — never | ||
| 6 | +# org.vibeerp.platform.* or org.vibeerp.pbc.* | ||
| 7 | +# | ||
| 8 | +# Primary defense: the root build.gradle.kts afterEvaluate hooks reject | ||
| 9 | +# illegal Gradle dependencies. This workflow simply runs the build so | ||
| 10 | +# violations fail CI. | ||
| 11 | +# | ||
| 12 | +# Secondary defense: a source-tree grep for forbidden imports inside the | ||
| 13 | +# api-v1 and reference-customer trees. This catches anyone reaching past | ||
| 14 | +# the build system (e.g. via reflection-friendly imports). | ||
| 15 | + | ||
| 16 | +name: check-architecture | ||
| 17 | + | ||
| 18 | +on: | ||
| 19 | + pull_request: | ||
| 20 | + | ||
| 21 | +jobs: | ||
| 22 | + build-guard: | ||
| 23 | + name: gradle dependency rule | ||
| 24 | + runs-on: ubuntu-latest | ||
| 25 | + steps: | ||
| 26 | + - name: Checkout | ||
| 27 | + uses: actions/checkout@v4 | ||
| 28 | + | ||
| 29 | + - name: Set up JDK 21 | ||
| 30 | + uses: actions/setup-java@v4 | ||
| 31 | + with: | ||
| 32 | + distribution: temurin | ||
| 33 | + java-version: "21" | ||
| 34 | + | ||
| 35 | + - name: Cache Gradle | ||
| 36 | + uses: actions/cache@v4 | ||
| 37 | + with: | ||
| 38 | + path: | | ||
| 39 | + ~/.gradle/caches | ||
| 40 | + ~/.gradle/wrapper | ||
| 41 | + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'gradle/libs.versions.toml') }} | ||
| 42 | + | ||
| 43 | + - name: Build (enforces dependency rule via afterEvaluate hooks) | ||
| 44 | + run: ./gradlew build --no-daemon | ||
| 45 | + | ||
| 46 | + import-guard: | ||
| 47 | + name: forbidden-import grep | ||
| 48 | + runs-on: ubuntu-latest | ||
| 49 | + steps: | ||
| 50 | + - name: Checkout | ||
| 51 | + uses: actions/checkout@v4 | ||
| 52 | + | ||
| 53 | + - name: Reject internal imports from api-v1 and reference-customer | ||
| 54 | + shell: bash | ||
| 55 | + run: | | ||
| 56 | + set -euo pipefail | ||
| 57 | + violations=0 | ||
| 58 | + for path in api/api-v1/src reference-customer; do | ||
| 59 | + if [ -d "$path" ]; then | ||
| 60 | + if grep -RInE 'import[[:space:]]+org\.vibeerp\.(platform|pbc)' "$path"; then | ||
| 61 | + echo "::error::Forbidden internal import found under $path" | ||
| 62 | + violations=$((violations + 1)) | ||
| 63 | + fi | ||
| 64 | + fi | ||
| 65 | + done | ||
| 66 | + if [ "$violations" -gt 0 ]; then | ||
| 67 | + echo "Architectural violation: api-v1 and reference-customer may only depend on org.vibeerp.api.v1.*" | ||
| 68 | + exit 1 | ||
| 69 | + fi | ||
| 70 | + echo "OK — no forbidden imports." |
Dockerfile
0 → 100644
| 1 | +# vibe_erp — single shipping image (architecture spec section 10). | ||
| 2 | +# | ||
| 3 | +# This produces ghcr.io/vibeerp/vibe-erp:<tag>: one Spring Boot fat-jar, | ||
| 4 | +# one mounted volume at /opt/vibe-erp, one mandatory dependency | ||
| 5 | +# (PostgreSQL). Customer extensions live OUTSIDE the image. | ||
| 6 | + | ||
| 7 | +# ─── Stage 1: build ──────────────────────────────────────────────────── | ||
| 8 | +# NOTE: this stage assumes ./gradlew and gradle/wrapper/* exist at the | ||
| 9 | +# repo root. They are generated by Gradle itself (not hand-written) and | ||
| 10 | +# will be added before anyone tries to build this image. | ||
| 11 | +FROM eclipse-temurin:21-jdk-alpine AS build | ||
| 12 | + | ||
| 13 | +WORKDIR /workspace | ||
| 14 | +COPY . . | ||
| 15 | + | ||
| 16 | +RUN chmod +x ./gradlew \ | ||
| 17 | + && ./gradlew :distribution:bootJar --no-daemon | ||
| 18 | + | ||
| 19 | +# ─── Stage 2: runtime ────────────────────────────────────────────────── | ||
| 20 | +FROM eclipse-temurin:21-jre-alpine AS runtime | ||
| 21 | + | ||
| 22 | +# Non-root user — never run the JVM as root inside the container. | ||
| 23 | +RUN addgroup -S vibeerp -g 10001 \ | ||
| 24 | + && adduser -S -u 10001 -G vibeerp vibeerp | ||
| 25 | + | ||
| 26 | +# Customer-mounted volume layout from architecture spec section 10. | ||
| 27 | +RUN mkdir -p /opt/vibe-erp/config \ | ||
| 28 | + /opt/vibe-erp/plugins \ | ||
| 29 | + /opt/vibe-erp/i18n-overrides \ | ||
| 30 | + /opt/vibe-erp/files \ | ||
| 31 | + /opt/vibe-erp/logs \ | ||
| 32 | + && chown -R vibeerp:vibeerp /opt/vibe-erp \ | ||
| 33 | + && mkdir -p /app \ | ||
| 34 | + && chown -R vibeerp:vibeerp /app | ||
| 35 | + | ||
| 36 | +COPY --from=build --chown=vibeerp:vibeerp \ | ||
| 37 | + /workspace/distribution/build/libs/vibe-erp.jar /app/vibe-erp.jar | ||
| 38 | + | ||
| 39 | +WORKDIR /app | ||
| 40 | + | ||
| 41 | +ENV VIBEERP_PLUGINS_DIR=/opt/vibe-erp/plugins \ | ||
| 42 | + VIBEERP_FILES_DIR=/opt/vibe-erp/files | ||
| 43 | + | ||
| 44 | +EXPOSE 8080 | ||
| 45 | +VOLUME ["/opt/vibe-erp"] | ||
| 46 | + | ||
| 47 | +USER vibeerp | ||
| 48 | + | ||
| 49 | +HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ | ||
| 50 | + CMD wget -q -O /dev/null http://localhost:8080/actuator/health || exit 1 | ||
| 51 | + | ||
| 52 | +ENTRYPOINT ["java", \ | ||
| 53 | + "-XX:+UseContainerSupport", \ | ||
| 54 | + "-XX:MaxRAMPercentage=75", \ | ||
| 55 | + "-Dfile.encoding=UTF-8", \ | ||
| 56 | + "-jar", "/app/vibe-erp.jar"] |
Makefile
0 → 100644
| 1 | +# vibe_erp — developer convenience targets. | ||
| 2 | +# | ||
| 3 | +# Thin wrappers over Gradle and docker compose. See `make help`. | ||
| 4 | + | ||
| 5 | +.PHONY: help build run image up down clean test | ||
| 6 | + | ||
| 7 | +help: | ||
| 8 | + @echo "vibe_erp — developer targets" | ||
| 9 | + @echo "" | ||
| 10 | + @echo " make build Compile and assemble all modules (./gradlew build)" | ||
| 11 | + @echo " make run Run the distribution locally (./gradlew :distribution:bootRun)" | ||
| 12 | + @echo " make image Build the local Docker image (vibeerp/vibe-erp:dev)" | ||
| 13 | + @echo " make up Start the local stack (docker compose up --build)" | ||
| 14 | + @echo " make down Tear down the local stack and volumes" | ||
| 15 | + @echo " make clean ./gradlew clean" | ||
| 16 | + @echo " make test ./gradlew test" | ||
| 17 | + | ||
| 18 | +build: | ||
| 19 | + ./gradlew build | ||
| 20 | + | ||
| 21 | +run: | ||
| 22 | + ./gradlew :distribution:bootRun | ||
| 23 | + | ||
| 24 | +image: | ||
| 25 | + docker build -t vibeerp/vibe-erp:dev . | ||
| 26 | + | ||
| 27 | +up: | ||
| 28 | + docker compose up --build | ||
| 29 | + | ||
| 30 | +down: | ||
| 31 | + docker compose down -v | ||
| 32 | + | ||
| 33 | +clean: | ||
| 34 | + ./gradlew clean | ||
| 35 | + | ||
| 36 | +test: | ||
| 37 | + ./gradlew test |
docker-compose.yaml
0 → 100644
| 1 | +# vibe_erp — local development compose stack. | ||
| 2 | +# | ||
| 3 | +# Brings up a Postgres + the vibe_erp image built from the local | ||
| 4 | +# Dockerfile. Mirrors the architecture spec section 10 install story | ||
| 5 | +# (one DB, one image, one mounted volume). | ||
| 6 | + | ||
| 7 | +services: | ||
| 8 | + db: | ||
| 9 | + image: postgres:16-alpine | ||
| 10 | + environment: | ||
| 11 | + POSTGRES_DB: vibeerp | ||
| 12 | + POSTGRES_USER: vibeerp | ||
| 13 | + POSTGRES_PASSWORD: vibeerp | ||
| 14 | + volumes: | ||
| 15 | + - pgdata:/var/lib/postgresql/data | ||
| 16 | + ports: | ||
| 17 | + - "5432:5432" | ||
| 18 | + healthcheck: | ||
| 19 | + test: ["CMD-SHELL", "pg_isready -U vibeerp"] | ||
| 20 | + interval: 5s | ||
| 21 | + timeout: 5s | ||
| 22 | + retries: 10 | ||
| 23 | + | ||
| 24 | + app: | ||
| 25 | + build: | ||
| 26 | + context: . | ||
| 27 | + dockerfile: Dockerfile | ||
| 28 | + depends_on: | ||
| 29 | + db: | ||
| 30 | + condition: service_healthy | ||
| 31 | + environment: | ||
| 32 | + VIBEERP_DB_URL: jdbc:postgresql://db:5432/vibeerp | ||
| 33 | + VIBEERP_DB_USER: vibeerp | ||
| 34 | + VIBEERP_DB_PASSWORD: vibeerp | ||
| 35 | + VIBEERP_INSTANCE_MODE: self-hosted | ||
| 36 | + ports: | ||
| 37 | + - "8080:8080" | ||
| 38 | + volumes: | ||
| 39 | + - ./local-vibeerp:/opt/vibe-erp | ||
| 40 | + | ||
| 41 | +volumes: | ||
| 42 | + pgdata: |
local-vibeerp/.gitkeep
0 → 100644
| 1 | +# Mounted into the dev container at /opt/vibe-erp |
local-vibeerp/config/.gitkeep
0 → 100644
local-vibeerp/config/vibe-erp.yaml
0 → 100644
| 1 | +# vibe_erp — example mounted config (architecture spec section 10). | ||
| 2 | +# | ||
| 3 | +# This file documents the closed key set of vibe-erp.yaml. It is NOT | ||
| 4 | +# what the dev container actually reads at runtime — application-dev.yaml | ||
| 5 | +# baked into the image is. It is kept here as living documentation so | ||
| 6 | +# operators can copy it as a starting point for a real deployment. | ||
| 7 | + | ||
| 8 | +instance: | ||
| 9 | + mode: self-hosted | ||
| 10 | + default-tenant: default | ||
| 11 | + | ||
| 12 | +database: | ||
| 13 | + url: jdbc:postgresql://db:5432/vibeerp | ||
| 14 | + username: vibeerp | ||
| 15 | + password: vibeerp | ||
| 16 | + | ||
| 17 | +files: | ||
| 18 | + backend: local | ||
| 19 | + local-path: /opt/vibe-erp/files | ||
| 20 | + | ||
| 21 | +auth: | ||
| 22 | + jwt: | ||
| 23 | + issuer: vibe-erp | ||
| 24 | + # secret is read from VIBEERP_JWT_SECRET in real deployments | ||
| 25 | + oidc: | ||
| 26 | + enabled: false | ||
| 27 | + # issuer-uri: https://keycloak.example.com/realms/vibeerp | ||
| 28 | + # client-id: vibe-erp | ||
| 29 | + # client-secret: ${VIBEERP_OIDC_CLIENT_SECRET} | ||
| 30 | + | ||
| 31 | +i18n: | ||
| 32 | + default-locale: en-US | ||
| 33 | + fallback-locale: en-US | ||
| 34 | + available-locales: | ||
| 35 | + - en-US | ||
| 36 | + - zh-CN | ||
| 37 | + - de-DE | ||
| 38 | + - ja-JP | ||
| 39 | + - es-ES | ||
| 40 | + | ||
| 41 | +plugins: | ||
| 42 | + directory: /opt/vibe-erp/plugins | ||
| 43 | + auto-load: true | ||
| 44 | + | ||
| 45 | +observability: | ||
| 46 | + metrics: | ||
| 47 | + enabled: true | ||
| 48 | + logging: | ||
| 49 | + level: INFO |
local-vibeerp/files/.gitkeep
0 → 100644
local-vibeerp/i18n-overrides/.gitkeep
0 → 100644
local-vibeerp/plugins/.gitkeep
0 → 100644