Skip to content

Commit df60ecd

Browse files
committed
Update README, Docker configuration, and CI/CD pipeline; enhance email settings and application startup
1 parent f7f6e0a commit df60ecd

7 files changed

Lines changed: 230 additions & 25 deletions

File tree

.github/workflows/ci-cd.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: CI/CD
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build-test:
11+
runs-on: ubuntu-latest
12+
services:
13+
postgres:
14+
image: postgres:15.1
15+
env:
16+
POSTGRES_DB: testdb
17+
POSTGRES_USER: postgres
18+
POSTGRES_PASSWORD: postgres
19+
ports:
20+
- 5432:5432
21+
options: >-
22+
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
23+
redis:
24+
image: redis:7.0
25+
ports:
26+
- 6379:6379
27+
options: >-
28+
--health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
29+
mailpit:
30+
image: axllent/mailpit:latest
31+
ports:
32+
- 3025:1025
33+
- 8025:8025
34+
options: >-
35+
--health-cmd "nc -z localhost 1025" --health-interval 10s --health-timeout 5s --health-retries 5
36+
steps:
37+
- uses: actions/checkout@v4
38+
- name: Setup .NET
39+
uses: actions/setup-dotnet@v4
40+
with:
41+
dotnet-version: "8.0.x"
42+
- name: Restore dependencies
43+
run: dotnet restore
44+
- name: Build
45+
run: dotnet build --no-restore --configuration Release
46+
- name: Run Unit Tests
47+
run: dotnet test ./tests/BookLibraryAPI.UnitTests/BookLibraryAPI.UnitTests.csproj --no-build --configuration Release --logger "trx;LogFileName=unit_tests.trx"
48+
- name: Run Integration Tests
49+
run: dotnet test ./tests/BookLibraryAPI.IntegrationTests/BookLibraryAPI.IntegrationTests.csproj --no-build --configuration Release --logger "trx;LogFileName=integration_tests.trx"
50+
- name: Upload Test Results
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: test-results
54+
path: tests/**/*.trx
55+
56+
docker-deploy:
57+
needs: build-test
58+
runs-on: ubuntu-latest
59+
steps:
60+
- uses: actions/checkout@v4
61+
- name: Set up Docker Buildx
62+
uses: docker/setup-buildx-action@v3
63+
- name: Login to DockerHub
64+
uses: docker/login-action@v3
65+
with:
66+
username: abdulwaisa
67+
password: ${{ secrets.DOCKERHUB_TOKEN }}
68+
- name: Build and push app image
69+
uses: docker/build-push-action@v5
70+
with:
71+
context: .
72+
file: src/Presentation/BookLibraryAPI.Presentation/Dockerfile
73+
push: true
74+
tags: abdulwaisa/booklibraryapi:latest
75+
- name: Deploy with Docker Compose
76+
run: |
77+
docker compose -f compose.yaml up -d

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,58 @@ Pull requests are welcome! Please follow the existing code style and add tests f
209209
210210
## License
211211
MIT
212+
213+
## Recent Changes & Improvements
214+
215+
### Docker & HTTPS
216+
- The API now supports HTTPS in Docker using a development certificate.
217+
- Default HTTPS port is `9443` (see `compose.yaml` and `appsettings.json`).
218+
- Access the API in Docker at: `https://localhost:9443/swagger`
219+
220+
### Email Notification Reliability
221+
- The `Email` section in `appsettings.json` now includes a required `To` field to avoid null recipient errors.
222+
- SMTP configuration keys in code were fixed to match the settings (`SmtpHost`, `SmtpPort`).
223+
224+
### CI/CD Pipeline
225+
- The GitHub Actions workflow (`.github/workflows/ci-cd.yml`) now includes:
226+
- Build, test (unit & integration), and artifact upload.
227+
- Docker image build and push to DockerHub.
228+
- Docker Compose deployment.
229+
- Mailpit service for email integration tests, ensuring all tests pass in CI.
230+
231+
## How to Check if the Application is Running
232+
233+
### Locally (Docker)
234+
1. Run:
235+
```sh
236+
docker compose up -d
237+
```
238+
2. Visit:
239+
- API: `https://localhost:9443/swagger`
240+
- Mailpit UI: `http://localhost:8025`
241+
3. Check logs:
242+
```sh
243+
docker compose logs app
244+
```
245+
246+
### In CI/CD (GitHub Actions)
247+
- On every push/PR to `main`, the workflow will:
248+
- Build and test the app.
249+
- Deploy the latest image with Docker Compose.
250+
- You can view workflow status and logs in the GitHub Actions tab.
251+
252+
## Testing
253+
254+
The project includes comprehensive unit and integration tests:
255+
256+
- **Unit Tests:** Located in `tests/BookLibraryAPI.UnitTests`. Run with:
257+
```sh
258+
dotnet test tests/BookLibraryAPI.UnitTests/BookLibraryAPI.UnitTests.csproj
259+
```
260+
- **Integration Tests:** Located in `tests/BookLibraryAPI.IntegrationTests`. These use Testcontainers for PostgreSQL, Redis, and Mailpit. Run with:
261+
```sh
262+
dotnet test tests/BookLibraryAPI.IntegrationTests/BookLibraryAPI.IntegrationTests.csproj
263+
```
264+
- **CI/CD:** All tests are automatically run in GitHub Actions on every push and pull request to `main`. Test results are uploaded as artifacts.
265+
266+
You can view test results in the GitHub Actions tab or by running the above commands locally.

compose.yaml

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,61 @@
1-
services:
2-
1+
version: '3.8'
2+
services:
3+
app:
4+
build:
5+
context: .
6+
dockerfile: src/Presentation/BookLibraryAPI.Presentation/Dockerfile
7+
container_name: booklibraryapi_app
8+
environment:
9+
- ASPNETCORE_ENVIRONMENT=Production
10+
- ConnectionStrings__LibraryDbConnection=Host=book-lib-postgres;Port=5432;Database=librarydb;Username=postgres;Password=postgres
11+
- ConnectionStrings__Redis=book-lib-redis:6379
12+
- Email__SmtpHost=mailpit
13+
- Email__SmtpPort=1025
14+
- Email__From=admin@example.com
15+
- ASPNETCORE_URLS=https://+:9443;http://+:8080
16+
- ASPNETCORE_Kestrel__Certificates__Default__Password=Password123!
17+
- ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnet-dev-cert.pfx
18+
ports:
19+
- "8080:8080"
20+
- "9443:9443"
21+
depends_on:
22+
- book-lib-postgres
23+
- book-lib-redis
24+
- mailpit
25+
restart: unless-stopped
26+
27+
book-lib-postgres:
28+
image: postgres:15.1
29+
container_name: BookLibrary.Postgres
30+
environment:
31+
POSTGRES_DB: librarydb
32+
POSTGRES_USER: postgres
33+
POSTGRES_PASSWORD: postgres
34+
ports:
35+
- "5432:5432"
36+
healthcheck:
37+
test: ["CMD-SHELL", "pg_isready -U postgres"]
38+
interval: 10s
39+
timeout: 5s
40+
retries: 5
41+
restart: unless-stopped
42+
43+
book-lib-redis:
44+
image: redis:7.0
45+
container_name: BookLibrary.Redis
46+
ports:
47+
- "6379:6379"
48+
healthcheck:
49+
test: ["CMD", "redis-cli", "ping"]
50+
interval: 10s
51+
timeout: 5s
52+
retries: 5
53+
restart: unless-stopped
54+
355
mailpit:
456
image: axllent/mailpit:latest
557
container_name: mailpit
658
ports:
759
- "8025:8025" # Web UI
860
- "3025:1025" # SMTP (host:container)
961
restart: unless-stopped
10-
11-
book-lib-redis:
12-
image: redis:latest
13-
container_name: BookLibrary.Redis
14-
restart: always
15-
ports:
16-
- "6379:6379"

src/Infrastructure/BookLibraryAPI.Infrastructure/Adapters/Email/EmailNotificationAdapter.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
namespace BookLibraryAPI.Infrastructure.Adapters.Email;
88

99
public class EmailNotificationAdapter(
10-
IConfiguration configuration,
11-
ILogger<EmailNotificationAdapter> logger)
10+
IConfiguration configuration,
11+
ILogger<EmailNotificationAdapter> logger)
1212
: IEmailNotificationPort
1313
{
1414
public async Task SendWelcomeEmailAsync(string bookTitle, string author,
@@ -39,7 +39,7 @@ private async Task SendNotificationAsync(string to, string subject, string body,
3939
var message = new MimeMessage();
4040
var from = configuration["Email:From"];
4141
message.From.Add(new MailboxAddress("Library Management System", from));
42-
42+
4343
message.To.Add(new MailboxAddress("", to));
4444
message.Subject = subject;
4545

@@ -52,8 +52,8 @@ private async Task SendNotificationAsync(string to, string subject, string body,
5252

5353
using var client = new SmtpClient();
5454
await client.ConnectAsync(
55-
configuration["Email:Host"],
56-
int.Parse(configuration["Email:Port"] ?? "8025"),
55+
configuration["Email:SmtpHost"],
56+
int.Parse(configuration["Email:SmtpPort"] ?? "1025"),
5757
false,
5858
cancellationToken);
5959

src/Presentation/BookLibraryAPI.Presentation/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
2-
USER $APP_UID
32
WORKDIR /app
43
EXPOSE 8080
5-
EXPOSE 8081
4+
EXPOSE 9443
5+
6+
# Copy dev certificate
7+
COPY ./src/Presentation/BookLibraryAPI.Presentation/aspnet-dev-cert.pfx /https/aspnet-dev-cert.pfx
68

79
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
810
ARG BUILD_CONFIGURATION=Release

src/Presentation/BookLibraryAPI.Presentation/Program.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,12 @@
2424

2525
var app = builder.Build();
2626

27-
if (app.Environment.IsDevelopment())
28-
{
29-
app.UseSwagger();
30-
app.UseSwaggerUI();
31-
32-
app.ApplyMigrations();
33-
34-
app.Seed();
35-
}
27+
app.UseSwagger();
28+
app.UseSwaggerUI();
29+
30+
app.ApplyMigrations();
31+
app.Seed();
32+
3633

3734
app.UseExceptionHandler();
3835
app.UseCustomExceptionHandler();

src/Presentation/BookLibraryAPI.Presentation/appsettings.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,33 @@
11
{
2+
"Jwt": {
3+
"Key": "ThisIsASecretKeyForJWTTokenGenerationThatShouldBeAtLeast32CharactersLongForSecurity",
4+
"Issuer": "BookLibraryAPI",
5+
"Audience": "BookLibraryAPIUsers"
6+
},
7+
"ConnectionStrings": {
8+
"LibraryDbConnection": "Host=book-lib-postgres;Port=5432;Database=librarydb;Username=postgres;Password=postgres",
9+
"Redis": "book-lib-redis:6379"
10+
},
11+
"Email": {
12+
"SmtpHost": "mailpit",
13+
"SmtpPort": 1025,
14+
"From": "admin@example.com",
15+
"To": "test@example.com"
16+
},
17+
"Kestrel": {
18+
"Endpoints": {
19+
"Http": {
20+
"Url": "http://0.0.0.0:8080"
21+
},
22+
"Https": {
23+
"Url": "https://0.0.0.0:9443",
24+
"Certificate": {
25+
"Path": "/https/aspnet-dev-cert.pfx",
26+
"Password": "Password123!"
27+
}
28+
}
29+
}
30+
},
231
"Logging": {
332
"LogLevel": {
433
"Default": "Information",

0 commit comments

Comments
 (0)