Commit 6789d26b0c9569013bfef97d761023356c8dd755

Authored by vibe_erp
1 parent f9ea66fc

chore: Dockerfile, docker-compose, GitHub Actions, Makefile, dev volume scaffolding

.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