Initial commit: demo-java-api with CI/CD
Some checks failed
Build & Deploy Java API / build (push) Has been cancelled

This commit is contained in:
gitea-ci
2026-07-22 01:06:34 +08:00
commit 74a57f251e
9 changed files with 355 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
name: Build & Deploy Java API
on:
push:
branches: [main]
env:
REGISTRY: harbor.cjjhc.top/library
APP_NAME: demo-java-api
jobs:
build:
runs-on: self-hosted
steps:
- name: Prepare workspace
run: |
WORK=/root/.cache/${{ env.APP_NAME }}
rm -rf "$WORK"
mkdir -p "$WORK"
- name: Checkout code
uses: actions/checkout@v4
with:
path: ${{ env.APP_NAME }}
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Maven packages
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
- name: Login to Harbor
run: docker login ${{ env.REGISTRY }} -u admin -p "${{ secrets.HARBOR_PASSWORD }}"
- name: Build with Maven
run: |
cd ${{ env.APP_NAME }}
mvn clean package -DskipTests
- name: Build & push Docker image
run: |
cd ${{ env.APP_NAME }}
SHORT=${GITHUB_SHA::8}
TAG="${{ env.REGISTRY }}/${{ env.APP_NAME }}:${SHORT}"
LATEST="${{ env.REGISTRY }}/${{ env.APP_NAME }}:latest"
docker build -t "${TAG}" -t "${LATEST}" .
docker push "${TAG}"
docker push "${LATEST}"
echo "✓ Pushed ${TAG}"
- name: Update image tag in values.yaml (commit-back GitOps)
run: |
cd ${{ env.APP_NAME }}
SHORT=${GITHUB_SHA::8}
# Update tag in values.yaml
sed -i "s/^ tag:.*/ tag: \"${SHORT}\"/" deploy/helm/values.yaml
# Configure git
git config user.email "ci@local"
git config user.name "gitea-ci"
# Commit and push
git add deploy/helm/values.yaml
git commit -m "ci: update image tag to ${SHORT}" || echo "nothing to commit"
git push origin main
echo "✓ Pushed values.yaml with tag ${SHORT}"

19
Dockerfile Normal file
View File

@@ -0,0 +1,19 @@
# Multi-stage build for Spring Boot
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# Production stage
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s CMD wget -q -O- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]

44
README.md Normal file
View File

@@ -0,0 +1,44 @@
# Demo Java API
A simple Spring Boot API for CI/CD demonstration.
## API Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/` | GET | Returns API info with timestamp |
| `/healthz` | GET | Health check endpoint |
## Local Development
```bash
./mvnw spring-boot:run
```
## Build Docker Image
```bash
docker build -t demo-java-api .
docker run -p 8080:8080 demo-java-api
```
## Project Structure
```
demo-java-api/
├── pom.xml # Maven config
├── Dockerfile # Multi-stage Docker build
├── src/
│ └── main/
│ ├── java/com/example/demo/
│ │ └── DemoJavaApiApplication.java
│ └── resources/
│ └── application.properties
├── deploy/helm/
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
│ └── deployment.yaml
└── .gitea/workflows/
└── build.yml # Gitea CI workflow
```

6
deploy/helm/Chart.yaml Normal file
View File

@@ -0,0 +1,6 @@
apiVersion: v2
name: demo-java-api
description: A Helm chart for demo-java-api
type: application
version: 1.0.0
appVersion: "1.0.0"

View File

@@ -0,0 +1,88 @@
{{- define "fullname" -}}
{{- .Chart.Name }}
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Chart.Name }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.registry }}/{{ .Values.image.name }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- containerPort: {{ .Values.service.port }}
name: http
livenessProbe:
{{- if .Values.probes.liveness.enabled }}
httpGet:
path: {{ .Values.probes.liveness.path }}
port: {{ .Values.probes.liveness.port }}
initialDelaySeconds: 30
periodSeconds: 10
{{- else }}
exec:
command: ["true"]
{{- end }}
readinessProbe:
{{- if .Values.probes.readiness.enabled }}
httpGet:
path: {{ .Values.probes.readiness.path }}
port: {{ .Values.probes.readiness.port }}
initialDelaySeconds: 5
periodSeconds: 5
{{- else }}
exec:
command: ["true"]
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
{{- toYaml .Values.env | nindent 12 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Chart.Name }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app: {{ .Chart.Name }}
{{- if .Values.ingress.enabled }}
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Chart.Name }}
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
spec:
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
- path: {{ .Values.ingress.path }}
pathType: Prefix
backend:
service:
name: {{ .Chart.Name }}
port:
number: {{ .Values.service.port }}
{{- end }}

38
deploy/helm/values.yaml Normal file
View File

@@ -0,0 +1,38 @@
# Default values
image:
registry: harbor.cjjhc.top/library
name: demo-java-api
tag: "latest"
pullPolicy: Always
replicaCount: 2
service:
type: ClusterIP
port: 8080
ingress:
enabled: true
className: traefik
host: demo-java-api.local
path: /
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
env: []
probes:
liveness:
enabled: true
path: /healthz
port: 8080
readiness:
enabled: true
path: /healthz
port: 8080

43
pom.xml Normal file
View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>demo-java-api</artifactId>
<version>1.0.0</version>
<name>demo-java-api</name>
<description>Demo Java API for CI/CD testing</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,35 @@
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
@RestController
public class DemoJavaApiApplication {
public static void main(String[] args) {
SpringApplication.run(DemoJavaApiApplication.class, args);
}
@GetMapping("/")
public Map<String, Object> home() {
Map<String, Object> response = new HashMap<>();
response.put("message", "Hello from Java API");
response.put("version", "1.0.0");
response.put("timestamp", Instant.now().toString());
return response;
}
@GetMapping("/healthz")
public Map<String, String> health() {
Map<String, String> response = new HashMap<>();
response.put("status", "OK");
return response;
}
}

View File

@@ -0,0 +1,5 @@
server.port=8080
spring.application.name=demo-java-api
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always