diff --git a/.github/workflows/deploy-api-docs.yml b/.github/workflows/deploy-api-docs.yml index 0f6335f..bcfa49d 100644 --- a/.github/workflows/deploy-api-docs.yml +++ b/.github/workflows/deploy-api-docs.yml @@ -2,10 +2,14 @@ name: Publish API Docs (GitHub Pages) on: push: - branches: [ main ] - paths: - - 'src/main/resources/api/**' + tags: + - "v*" workflow_dispatch: + inputs: + tag: + description: "Tag to deploy (must start with v, e.g. v1.0.0)" + required: true + type: string permissions: contents: read @@ -19,9 +23,24 @@ concurrency: jobs: build: runs-on: ubuntu-latest + # Use the pushed tag ref, or the manually provided tag ref + env: + REF: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag) || github.ref }} + steps: - - name: Checkout + - name: Validate ref is a v* tag + run: | + echo "Ref: $REF" + if [[ "$REF" != refs/tags/v* ]]; then + echo "Ref must be a tag starting with 'v' (example: v1.0.0)." + exit 1 + fi + + - name: Checkout tag uses: actions/checkout@v4 + with: + ref: ${{ env.REF }} + fetch-depth: 0 - name: Setup Node uses: actions/setup-node@v4 @@ -96,4 +115,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e157364 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,79 @@ +name: Release (GHCR + GitHub Pre-release Assets) + +on: + push: + tags: + - "v*" + +permissions: + contents: write + packages: write + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout tag + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Extract version from tag + id: ver + run: | + TAG="${GITHUB_REF_NAME}" + VERSION="${TAG#v}" + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + # ----------------------------- + # Build & publish Docker image + # ----------------------------- + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build & push image to GHCR + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ghcr.io/2rk-dev/edt-app:${{ steps.ver.outputs.version }} + cache-from: type=registry,ref=ghcr.io/2rk-dev/edt-app:cache + cache-to: type=registry,ref=ghcr.io/2rk-dev/edt-app:cache,mode=max + + # ----------------------------------------- + # Zip deploy files as a GitHub Release asset + # ----------------------------------------- + - name: Create deployment bundle (compose + env) + run: | + test -f docker-compose-prod.yml + test -f .env.example + + mkdir -p dist + sed "s|image: ghcr.io/2rk-dev/edt-app:latest|image: ghcr.io/2rk-dev/edt-app:${{ steps.ver.outputs.version }}|" docker-compose-prod.yml > dist/docker-compose.yml + cp .env.example dist/.env.example + + cd dist + zip -r "deploy-bundle-${{ steps.ver.outputs.tag }}.zip" docker-compose.yml .env.example + + # ----------------------------------------- + # Create a pre-release and upload the bundle + # ----------------------------------------- + - name: Create GitHub pre-release and upload assets + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.ver.outputs.tag }} + name: ${{ steps.ver.outputs.tag }} + prerelease: true + generate_release_notes: true + files: | + dist/deploy-bundle-${{ steps.ver.outputs.tag }}.zip \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 76be7dd..90ae46d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,12 @@ FROM eclipse-temurin:21-jdk-alpine AS builder WORKDIR /app + COPY gradle/ gradle/ -COPY gradlew settings.gradle build.gradle .env gradle.properties* ./ -COPY src ./src +COPY gradlew settings.gradle build.gradle gradle.properties* ./ RUN chmod +x gradlew + +COPY src ./src + RUN --mount=type=cache,target=/root/.gradle \ ./gradlew bootJar -x test --no-daemon @@ -14,6 +17,10 @@ RUN java -Djarmode=layertools -jar app.jar extract FROM eclipse-temurin:21-jre-alpine WORKDIR /app + +RUN addgroup -S app && adduser -S app -G app +USER app + COPY --from=layers /app/dependencies/ ./ COPY --from=layers /app/spring-boot-loader/ ./ COPY --from=layers /app/snapshot-dependencies/ ./ diff --git a/build.gradle b/build.gradle index e41094c..e655f4c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,7 @@ -buildscript { - dependencies { - classpath 'org.flywaydb:flyway-database-postgresql:11.15.0' - } -} - plugins { id 'java' id 'org.springframework.boot' version '3.5.9' id 'io.spring.dependency-management' version '1.1.7' - id 'org.flywaydb.flyway' version '11.15.0' id 'co.uzzu.dotenv.gradle' version '4.0.0' } @@ -31,14 +24,6 @@ repositories { mavenCentral() } -flyway { - url = env.SPRING_DB_URL.orElse('SPRING_DB_URL') - user = env.SPRING_DB_USERNAME.orElse('SPRING_DB_USERNAME') - password = env.SPRING_DB_PASSWORD.orElse('SPRING_DB_PASSWORD') - locations = ['classpath:db/migration'] - baselineOnMigrate = true -} - dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml new file mode 100644 index 0000000..3dc022a --- /dev/null +++ b/docker-compose-prod.yml @@ -0,0 +1,24 @@ +services: + app: + image: ghcr.io/2rk-dev/edt-app:latest + container_name: spring_app + ports: + - "${APP_PORT}:${APP_PORT}" + env_file: + - .env + depends_on: + - postgres + postgres: + image: postgres:16 + container_name: postgres_db + environment: + POSTGRES_DB: ${POSTGRES_DB} + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + ports: + - "${POSTGRES_PORT}:5432" + volumes: + - db_data:/var/lib/postgresql/data + +volumes: + db_data: