From 80f38f41e05124bbe504276fece38c21db754225 Mon Sep 17 00:00:00 2001 From: podlLev Date: Mon, 20 Jul 2026 09:54:23 +0300 Subject: [PATCH 1/5] ci: add GitHub Actions workflow for build and test --- .github/workflows/ci.yml | 63 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..98a9695 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,63 @@ +name: CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main", "dev" ] + +permissions: + contents: read + +jobs: + + # --------------------------------------------------------------------- + # 1. Build + test on every push and every PR + # --------------------------------------------------------------------- + test: + name: Build & Test + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '17' + cache: maven + + - name: Run tests + # Tests run against the in-memory H2 DB and spring.cache.type=simple, + # so no Postgres/Redis service containers are needed here. + # WEATHER_API_KEY just needs to be non-empty; tests mock the HTTP client. + env: + WEATHER_API_KEY: dummy-ci-key + run: ./mvnw -B test + + - name: Publish test report + if: always() + uses: dorny/test-reporter@v1 + with: + name: Maven Tests + path: target/surefire-reports/*.xml + reporter: java-junit + + - name: Upload JaCoCo coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: jacoco-report + path: target/site/jacoco/ + + - name: Build jar + env: + WEATHER_API_KEY: dummy-ci-key + run: ./mvnw -B package -DskipTests + + - name: Upload jar artifact + uses: actions/upload-artifact@v4 + with: + name: app-jar + path: target/*.jar \ No newline at end of file From 7569906d576a2880bbbb3def4e439db2cb3febc5 Mon Sep 17 00:00:00 2001 From: podlLev Date: Mon, 20 Jul 2026 09:54:50 +0300 Subject: [PATCH 2/5] ci: add Docker Hub build and push job --- .github/workflows/ci.yml | 51 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98a9695..e9fd80e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,4 +60,53 @@ jobs: uses: actions/upload-artifact@v4 with: name: app-jar - path: target/*.jar \ No newline at end of file + path: target/*.jar + + # --------------------------------------------------------------------- + # 2. Build + push a versioned image to Docker Hub + # - runs ONLY on: pushes to main + # - every push to main -> : :latest + # - pushes to dev, and all PRs -> this job does not run at all + # (dev/PRs are still covered by the `test` job above) + # --------------------------------------------------------------------- + docker: + name: Build & Push Docker Image + needs: test + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Read version from pom.xml + id: pom + run: | + VERSION=$(./mvnw -q -B help:evaluate -Dexpression=project.version -DforceStdout) + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata (tags) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ secrets.DOCKERHUB_USERNAME }}/weatherviewer + tags: | + type=raw,value=${{ steps.pom.outputs.version }} + type=raw,value=latest + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file From 5b8adec9bf8f7565a00a8deef792750edee155ed Mon Sep 17 00:00:00 2001 From: podlLev Date: Mon, 20 Jul 2026 10:13:41 +0300 Subject: [PATCH 3/5] fix: make mvnw executable --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e9fd80e..2ef7a9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,9 @@ jobs: java-version: '17' cache: maven + - name: Make mvnw executable + run: chmod +x ./mvnw + - name: Run tests # Tests run against the in-memory H2 DB and spring.cache.type=simple, # so no Postgres/Redis service containers are needed here. From 1d1a5f372cf99de83bba8449d69f78d3cf140de3 Mon Sep 17 00:00:00 2001 From: podlLev Date: Mon, 20 Jul 2026 10:35:52 +0300 Subject: [PATCH 4/5] ci: bump actions to Node 24-compatible versions --- .github/workflows/ci.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ef7a9a..270625d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: branches: [ "main" ] pull_request: - branches: [ "main", "dev" ] + branches: [ "main", "dev" ] permissions: contents: read @@ -19,10 +19,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK 17 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: distribution: temurin java-version: '17' @@ -41,7 +41,7 @@ jobs: - name: Publish test report if: always() - uses: dorny/test-reporter@v1 + uses: dorny/test-reporter@v3 with: name: Maven Tests path: target/surefire-reports/*.xml @@ -49,7 +49,7 @@ jobs: - name: Upload JaCoCo coverage report if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: jacoco-report path: target/site/jacoco/ @@ -60,7 +60,7 @@ jobs: run: ./mvnw -B package -DskipTests - name: Upload jar artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: app-jar path: target/*.jar @@ -79,11 +79,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Read version from pom.xml id: pom run: | + chmod +x ./mvnw VERSION=$(./mvnw -q -B help:evaluate -Dexpression=project.version -DforceStdout) echo "version=$VERSION" >> "$GITHUB_OUTPUT" From 4fe641e01ab6aa9e3c635b18afb184275eccb533 Mon Sep 17 00:00:00 2001 From: podlLev Date: Mon, 20 Jul 2026 10:43:00 +0300 Subject: [PATCH 5/5] fix: explicitly ignore auto-generated/default fields in mappers --- .../java/com/weatherviewer/mapper/LocationMapper.java | 9 ++++++++- src/main/java/com/weatherviewer/mapper/UserMapper.java | 4 ++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/weatherviewer/mapper/LocationMapper.java b/src/main/java/com/weatherviewer/mapper/LocationMapper.java index 4a66169..68e2d65 100644 --- a/src/main/java/com/weatherviewer/mapper/LocationMapper.java +++ b/src/main/java/com/weatherviewer/mapper/LocationMapper.java @@ -35,9 +35,16 @@ public abstract class LocationMapper { */ @Mapping(target = "user", expression = "java(userRepository.getReferenceById(addLocationDto.getUserId()))") @Mapping(target = "favorite", expression = "java(addLocationDto.getFavorite() != null ? addLocationDto.getFavorite() : false)") + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) public abstract Location fromDto(AddLocationDto addLocationDto); - /** Builds a {@link Location} from its full DTO representation (e.g. for updates). */ + /** + * Builds a {@link Location} from its full DTO representation (e.g. for updates). + * The owning {@code user} is intentionally left unset here — callers that need + * a persisted, owned entity set it separately after this conversion. + */ + @Mapping(target = "user", ignore = true) public abstract Location fromDto(LocationDto locationDto); /** diff --git a/src/main/java/com/weatherviewer/mapper/UserMapper.java b/src/main/java/com/weatherviewer/mapper/UserMapper.java index d4a3e23..caf54e5 100644 --- a/src/main/java/com/weatherviewer/mapper/UserMapper.java +++ b/src/main/java/com/weatherviewer/mapper/UserMapper.java @@ -22,6 +22,10 @@ public interface UserMapper { */ @Mapping(target = "status", expression = "java(com.weatherviewer.model.enums.UserStatus.ACTIVE)") @Mapping(target = "role", expression = "java(com.weatherviewer.model.enums.Role.USER)") + @Mapping(target = "id", ignore = true) + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "units", ignore = true) + @Mapping(target = "locations", ignore = true) User fromRecord(CreateUserDto createUserDto); /**