Skip to content

Commit 6d3aa90

Browse files
Merge pull request #2 from amigoscode/ci-cd-with-github-action
Ci cd with GitHub action
2 parents 40e6c72 + 4675886 commit 6d3aa90

25 files changed

+1292
-1
lines changed

.github/workflows/build.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Build workflow
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
jobs:
7+
build:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Checkout
11+
uses: actions/checkout@v4
12+
- name: JDK
13+
uses: actions/setup-java@v4
14+
with:
15+
distribution: 'temurin'
16+
java-version: '21'
17+
- name: Maven Clean Verify
18+
run: mvn -B -ntp clean verify

Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Build argument for Java version
2+
ARG JAVA_VERSION=21
3+
4+
# Stage 1: Build the application
5+
FROM maven:3.9.4 AS builder
6+
WORKDIR /app
7+
COPY pom.xml .
8+
COPY src ./src
9+
RUN mvn clean package
10+
11+
# Stage 2: Run the application
12+
FROM openjdk:${JAVA_VERSION}-jdk-slim
13+
WORKDIR /app
14+
COPY --from=builder /app/target/product-service.jar app.jar
15+
EXPOSE 8080
16+
ENTRYPOINT ["java", "-jar", "app.jar"]

docker-compose-db-only.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
services:
2+
db-local-postgres:
3+
container_name: jfs-postgres-local
4+
image: postgres
5+
environment:
6+
POSTGRES_USER: amigoscode
7+
POSTGRES_PASSWORD: password
8+
POSTGRES_DB: jfs
9+
ports:
10+
- "5333:5432"
11+
restart: unless-stopped
12+
volumes:
13+
- db-local:/data/postgres
14+
networks:
15+
- amigos
16+
17+
networks:
18+
amigos:
19+
driver: bridge
20+
21+
volumes:
22+
db-local:

docker-compose.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
services:
2+
product-service:
3+
container_name: product
4+
# image: amigoscode/product-service:jibMaven
5+
build:
6+
context: .
7+
dockerfile: Dockerfile
8+
environment:
9+
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/jfs
10+
SPRING_DATASOURCE_USERNAME: amigoscode
11+
SPRING_DATASOURCE_PASSWORD: password
12+
ports:
13+
- "8090:8080"
14+
networks:
15+
- amigos
16+
db:
17+
container_name: jfs-postgres
18+
image: postgres
19+
environment:
20+
POSTGRES_USER: amigoscode
21+
POSTGRES_PASSWORD: password
22+
POSTGRES_DB: jfs
23+
ports:
24+
- "5333:5432"
25+
restart: unless-stopped
26+
volumes:
27+
- db:/data/postgres
28+
networks:
29+
- amigos
30+
31+
networks:
32+
amigos:
33+
driver: bridge
34+
35+
volumes:
36+
db:

pom.xml

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<groupId>com.amigoscode</groupId>
1212
<artifactId>jfs</artifactId>
1313
<version>0.0.1-SNAPSHOT</version>
14+
<packaging>jar</packaging>
1415
<name>java-springboot-full-stack</name>
1516
<description>java-springboot-full-stack</description>
1617
<url/>
@@ -28,6 +29,9 @@
2829
</scm>
2930
<properties>
3031
<java.version>21</java.version>
32+
<docker.username>amigoscode</docker.username>
33+
<docker.image.name>product-service</docker.image.name>
34+
<docker.image.tag/>
3135
</properties>
3236
<dependencies>
3337
<dependency>
@@ -40,14 +44,103 @@
4044
<artifactId>spring-boot-starter-test</artifactId>
4145
<scope>test</scope>
4246
</dependency>
47+
<dependency>
48+
<groupId>org.springframework.boot</groupId>
49+
<artifactId>spring-boot-starter-web</artifactId>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.postgresql</groupId>
53+
<artifactId>postgresql</artifactId>
54+
<scope>runtime</scope>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.springframework.boot</groupId>
58+
<artifactId>spring-boot-starter-data-jpa</artifactId>
59+
</dependency>
60+
<dependency>
61+
<groupId>org.springframework.boot</groupId>
62+
<artifactId>spring-boot-starter-validation</artifactId>
63+
</dependency>
64+
<dependency>
65+
<groupId>org.flywaydb</groupId>
66+
<artifactId>flyway-core</artifactId>
67+
</dependency>
68+
<dependency>
69+
<groupId>org.flywaydb</groupId>
70+
<artifactId>flyway-database-postgresql</artifactId>
71+
</dependency>
72+
<dependency>
73+
<groupId>org.testcontainers</groupId>
74+
<artifactId>junit-jupiter</artifactId>
75+
</dependency>
76+
<dependency>
77+
<groupId>org.testcontainers</groupId>
78+
<artifactId>postgresql</artifactId>
79+
</dependency>
80+
<dependency>
81+
<groupId>org.springframework.boot</groupId>
82+
<artifactId>spring-boot-testcontainers</artifactId>
83+
</dependency>
84+
<dependency>
85+
<groupId>org.springframework.boot</groupId>
86+
<artifactId>spring-boot-starter-webflux</artifactId>
87+
<scope>test</scope>
88+
</dependency>
4389
</dependencies>
4490

4591
<build>
92+
<finalName>product-service</finalName>
4693
<plugins>
4794
<plugin>
4895
<groupId>org.springframework.boot</groupId>
4996
<artifactId>spring-boot-maven-plugin</artifactId>
5097
</plugin>
98+
<plugin>
99+
<groupId>com.google.cloud.tools</groupId>
100+
<artifactId>jib-maven-plugin</artifactId>
101+
<version>3.3.2</version>
102+
<configuration>
103+
<from>
104+
<image>eclipse-temurin:21-jre</image>
105+
<platforms>
106+
<platrom>
107+
<architecture>amd64</architecture>
108+
<os>linux</os>
109+
</platrom>
110+
<platrom>
111+
<architecture>arm64</architecture>
112+
<os>linux</os>
113+
</platrom>
114+
</platforms>
115+
</from>
116+
<to>
117+
<image>docker.io/${docker.username}/${docker.image.name}:${docker.image.tag}</image>
118+
<tags>
119+
<tag>latest</tag>
120+
</tags>
121+
</to>
122+
</configuration>
123+
</plugin>
124+
<plugin>
125+
<groupId>org.apache.maven.plugins</groupId>
126+
<artifactId>maven-surefire-plugin</artifactId>
127+
<configuration>
128+
<excludes>
129+
<exclude>**/*IntegrationTest.java</exclude>
130+
<exclude>**/*IT.java</exclude>
131+
</excludes>
132+
</configuration>
133+
</plugin>
134+
<plugin>
135+
<groupId>org.apache.maven.plugins</groupId>
136+
<artifactId>maven-failsafe-plugin</artifactId>
137+
<configuration>
138+
<includes>
139+
<include>**/*IntegrationTest.java</include>
140+
<include>**/*IT.java</include>
141+
</includes>
142+
</configuration>
143+
</plugin>
51144
</plugins>
52145
</build>
53146

src/main/java/com/amigoscode/Main.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package com.amigoscode;
22

3+
import com.amigoscode.product.Product;
4+
import com.amigoscode.product.ProductRepository;
5+
import org.springframework.boot.CommandLineRunner;
36
import org.springframework.boot.SpringApplication;
47
import org.springframework.boot.autoconfigure.SpringBootApplication;
8+
import org.springframework.context.annotation.Bean;
9+
10+
import java.math.BigDecimal;
11+
import java.util.UUID;
512

613
@SpringBootApplication
714
public class Main {
@@ -10,4 +17,31 @@ public static void main(String[] args) {
1017
SpringApplication.run(Main.class, args);
1118
}
1219

20+
// @Bean
21+
public CommandLineRunner commandLineRunner(
22+
ProductRepository productRepository) {
23+
return args -> {
24+
Product product1 = new Product();
25+
product1.setName("Macbook Pro");
26+
product1.setDescription("Macbook Pro M4");
27+
product1.setPrice(new BigDecimal(3000));
28+
product1.setId(UUID.fromString(
29+
"d95062e6-9f0b-4224-bc9d-d0723949848f")
30+
);
31+
product1.setStockLevel(100);
32+
productRepository.save(product1);
33+
34+
Product product2 = new Product();
35+
product2.setId(UUID.fromString(
36+
"94d2cc8a-ad09-4902-a321-a6bf658e2463"
37+
));
38+
product2.setName("Mouse");
39+
product2.setDescription("LG Mouse");
40+
product2.setPrice(new BigDecimal(78));
41+
product2.setStockLevel(1000);
42+
43+
productRepository.save(product2);
44+
};
45+
}
46+
1347
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.amigoscode.exception;
2+
3+
import java.time.Instant;
4+
import java.util.Map;
5+
6+
public record ErrorResponse(
7+
String message,
8+
String error,
9+
int statusCode,
10+
String path,
11+
Instant timestamp,
12+
Map<String, String> fieldErrors) {
13+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.amigoscode.exception;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import org.springframework.http.HttpStatus;
5+
import org.springframework.http.ResponseEntity;
6+
import org.springframework.validation.FieldError;
7+
import org.springframework.web.bind.MethodArgumentNotValidException;
8+
import org.springframework.web.bind.annotation.ControllerAdvice;
9+
import org.springframework.web.bind.annotation.ExceptionHandler;
10+
11+
import java.time.Instant;
12+
import java.util.Map;
13+
import java.util.Objects;
14+
import java.util.stream.Collectors;
15+
16+
@ControllerAdvice
17+
public class GlobalExceptionHandler {
18+
19+
@ExceptionHandler(ResourceNotFound.class)
20+
public ResponseEntity<ErrorResponse> handleResourceNotFound(
21+
ResourceNotFound ex,
22+
HttpServletRequest request
23+
) {
24+
ErrorResponse errorResponse = new ErrorResponse(
25+
ex.getMessage(),
26+
HttpStatus.NOT_FOUND.getReasonPhrase(),
27+
HttpStatus.NOT_FOUND.value(),
28+
request.getRequestURI(),
29+
Instant.now(),
30+
null
31+
);
32+
return new ResponseEntity<>(
33+
errorResponse,
34+
HttpStatus.NOT_FOUND
35+
);
36+
}
37+
38+
@ExceptionHandler(MethodArgumentNotValidException.class)
39+
public ResponseEntity<ErrorResponse> handleValidationException(
40+
MethodArgumentNotValidException ex,
41+
HttpServletRequest request
42+
) {
43+
44+
Map<String, String> errors = ex.getBindingResult()
45+
.getFieldErrors()
46+
.stream()
47+
.collect(Collectors.toMap(
48+
FieldError::getField,
49+
fieldError ->
50+
Objects.requireNonNullElse(
51+
fieldError.getDefaultMessage(),
52+
"no error available"
53+
)
54+
));
55+
ErrorResponse errorResponse = new ErrorResponse(
56+
ex.getMessage(),
57+
HttpStatus.BAD_REQUEST.getReasonPhrase(),
58+
HttpStatus.BAD_REQUEST.value(),
59+
request.getRequestURI(),
60+
Instant.now(),
61+
errors
62+
);
63+
return new ResponseEntity<>(
64+
errorResponse,
65+
HttpStatus.BAD_REQUEST
66+
);
67+
}
68+
69+
@ExceptionHandler(Exception.class)
70+
public ResponseEntity<ErrorResponse> handleGenericException(
71+
Exception ex,
72+
HttpServletRequest request
73+
) {
74+
ErrorResponse errorResponse = new ErrorResponse(
75+
ex.getMessage(),
76+
HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(),
77+
HttpStatus.INTERNAL_SERVER_ERROR.value(),
78+
request.getRequestURI(),
79+
Instant.now(),
80+
null
81+
);
82+
return new ResponseEntity<>(
83+
errorResponse,
84+
HttpStatus.INTERNAL_SERVER_ERROR
85+
);
86+
}
87+
88+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.amigoscode.exception;
2+
3+
public class ResourceNotFound extends RuntimeException {
4+
public ResourceNotFound(String message) {
5+
super(message);
6+
}
7+
}

0 commit comments

Comments
 (0)