From 0200c9fe61daf00a8f2f1f1d5b5719d973f8aa94 Mon Sep 17 00:00:00 2001 From: Bhavesh Khandelwal Date: Tue, 18 Nov 2025 20:00:06 +0530 Subject: [PATCH] Add Docker configuration and GitHub Actions workflow Signed-off-by: Bhavesh Khandelwal --- .dockerignore | 15 ++++++++ .github/workflows/container-build.yml | 51 +++++++++++++++++++++++++++ Dockerfile | 23 ++++++++++++ README.md | 48 +++++++++++++++++-------- docker-compose.yml | 48 ++++++++++++++++++++----- 5 files changed, 162 insertions(+), 23 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/container-build.yml create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..8a4634687 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,15 @@ +/target +/build +/.mvn/wrapper/maven-wrapper.jar +/.idea +/.vscode +/.git +/.gitignore +README.md +LICENSE.txt +docker-compose.yml +k8s +node_modules +*.iml +*.log + diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml new file mode 100644 index 000000000..16cc56af7 --- /dev/null +++ b/.github/workflows/container-build.yml @@ -0,0 +1,51 @@ +name: Container Build and Publish + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up JDK 25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + cache: maven + + - name: Build and test with Maven + run: ./mvnw -B verify + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..dc2bfb3a1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM eclipse-temurin:25-jdk-alpine AS builder +WORKDIR /workspace + +COPY mvnw . +COPY .mvn .mvn +COPY pom.xml . +COPY src src + +RUN chmod +x mvnw \ + && ./mvnw -B -DskipTests package + +FROM eclipse-temurin:25-jre-alpine +WORKDIR /app + +ENV SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-mysql} +ENV JAVA_OPTS="" + +COPY --from=builder /workspace/target/*.jar app.jar + +EXPOSE 8080 + +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] + diff --git a/README.md b/README.md index 379bd42a6..6ce437a62 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Spring PetClinic Sample Application [![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)[![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml) +# Spring PetClinic Sample Application [![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/maven-build.yml)[![Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/gradle-build.yml)[![Container Build Status](https://github.com/spring-projects/spring-petclinic/actions/workflows/container-build.yml/badge.svg)](https://github.com/spring-projects/spring-petclinic/actions/workflows/container-build.yml) [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/spring-projects/spring-petclinic) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=7517918) @@ -37,14 +37,42 @@ Or you can run it from Maven directly using the Spring Boot Maven plugin. If you > NOTE: If you prefer to use Gradle, you can build the app using `./gradlew build` and look for the jar file in `build/libs`. -## Building a Container +## Containerized Deployment -There is no `Dockerfile` in this project. You can build a container image (if you have a docker daemon) using the Spring Boot build plugin: +Spring PetClinic now ships with a first-class Dockerfile and Compose stack. + +### Build a local image ```bash -./mvnw spring-boot:build-image +docker build -t spring-petclinic . +docker run --rm -p 8080:8080 \ + -e SPRING_PROFILES_ACTIVE=mysql \ + -e SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/petclinic?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC \ + -e SPRING_DATASOURCE_USERNAME=petclinic \ + -e SPRING_DATASOURCE_PASSWORD=petclinic \ + spring-petclinic ``` +### Run the full stack with Docker Compose + +```bash +docker compose up --build +``` + +The default profile uses the MySQL service declared in `docker-compose.yml`. To stop the stack run `docker compose down`. + +To switch to PostgreSQL, enable the `postgres` profile and override the datasource values: + +```bash +SPRING_PROFILES_ACTIVE=postgres \ +SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/petclinic \ +docker compose --profile postgres up --build +``` + +## Continuous Integration and Deployment + +The repository now includes a `Container Build and Publish` workflow that complements the existing Maven and Gradle pipelines. Every push and pull request triggers unit and integration tests via Maven and builds a container image using Docker Buildx. Successful builds on `main` automatically publish a `ghcr.io//spring-petclinic:latest` container image using the repository `GITHUB_TOKEN`. + ## In case you find a bug/suggested improvement for Spring Petclinic Our issue tracker is available [here](https://github.com/spring-projects/spring-petclinic/issues). @@ -72,17 +100,7 @@ docker run -e POSTGRES_USER=petclinic -e POSTGRES_PASSWORD=petclinic -e POSTGRES Further documentation is provided for [MySQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/mysql/petclinic_db_setup_mysql.txt) and [PostgreSQL](https://github.com/spring-projects/spring-petclinic/blob/main/src/main/resources/db/postgres/petclinic_db_setup_postgres.txt). -Instead of vanilla `docker` you can also use the provided `docker-compose.yml` file to start the database containers. Each one has a service named after the Spring profile: - -```bash -docker compose up mysql -``` - -or - -```bash -docker compose up postgres -``` +Instead of vanilla `docker` you can also rely on the provided `docker-compose.yml` file to start both the application and database containers together as shown above. ## Test Applications diff --git a/docker-compose.yml b/docker-compose.yml index 50c731a91..ed9c41c2e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,53 @@ services: + app: + build: + context: . + dockerfile: Dockerfile + image: spring-petclinic:latest + ports: + - "8080:8080" + environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-mysql} + SPRING_DATASOURCE_URL: ${SPRING_DATASOURCE_URL:-jdbc:mysql://mysql:3306/petclinic?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC} + SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME:-petclinic} + SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD:-petclinic} + depends_on: + mysql: + condition: service_healthy + restart: unless-stopped + mysql: image: mysql:9.2 ports: - "3306:3306" environment: - - MYSQL_ROOT_PASSWORD= - - MYSQL_ALLOW_EMPTY_PASSWORD=true - - MYSQL_USER=petclinic - - MYSQL_PASSWORD=petclinic - - MYSQL_DATABASE=petclinic + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root} + MYSQL_USER: ${SPRING_DATASOURCE_USERNAME:-petclinic} + MYSQL_PASSWORD: ${SPRING_DATASOURCE_PASSWORD:-petclinic} + MYSQL_DATABASE: ${MYSQL_DATABASE:-petclinic} + MYSQL_ALLOW_EMPTY_PASSWORD: "false" volumes: - "./conf.d:/etc/mysql/conf.d:ro" + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h localhost -p${MYSQL_ROOT_PASSWORD:-root}"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + postgres: image: postgres:18.0 ports: - "5432:5432" environment: - - POSTGRES_PASSWORD=petclinic - - POSTGRES_USER=petclinic - - POSTGRES_DB=petclinic + POSTGRES_PASSWORD: ${SPRING_DATASOURCE_PASSWORD:-petclinic} + POSTGRES_USER: ${SPRING_DATASOURCE_USERNAME:-petclinic} + POSTGRES_DB: ${POSTGRES_DB:-petclinic} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${SPRING_DATASOURCE_USERNAME:-petclinic}"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + profiles: + - postgres