feat: Set up CI/CD pipeline with embedded webapp and Woodpecker configuration
This commit is contained in:
parent
ebbb2bac41
commit
6400eb9513
5 changed files with 163 additions and 3 deletions
39
.gitignore
vendored
39
.gitignore
vendored
|
@ -1 +1,40 @@
|
||||||
.aider*
|
.aider*
|
||||||
|
|
||||||
|
# Go build artifacts
|
||||||
|
/inventory
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# SQLite database files
|
||||||
|
*.sqlite
|
||||||
|
*.db
|
||||||
|
|
||||||
|
# Node.js dependencies
|
||||||
|
node_modules/
|
||||||
|
web/node_modules/
|
||||||
|
web/dist/
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS specific files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
/artifacts/
|
||||||
|
|
58
.woodpecker.yml
Normal file
58
.woodpecker.yml
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
pipeline:
|
||||||
|
backend-lint:
|
||||||
|
image: golang:1.21
|
||||||
|
group: lint
|
||||||
|
commands:
|
||||||
|
- go vet ./...
|
||||||
|
- go fmt ./...
|
||||||
|
|
||||||
|
frontend-lint:
|
||||||
|
image: node:20
|
||||||
|
group: lint
|
||||||
|
commands:
|
||||||
|
- cd web
|
||||||
|
- npm ci
|
||||||
|
- npm run lint
|
||||||
|
|
||||||
|
frontend-build:
|
||||||
|
image: node:20
|
||||||
|
commands:
|
||||||
|
- cd web
|
||||||
|
- npm ci
|
||||||
|
- npm run build
|
||||||
|
when:
|
||||||
|
status: [success]
|
||||||
|
|
||||||
|
backend-build:
|
||||||
|
image: golang:1.21
|
||||||
|
commands:
|
||||||
|
- go mod download
|
||||||
|
- go generate ./...
|
||||||
|
# Build with embedded frontend
|
||||||
|
- go build -o inventory -ldflags="-s -w" .
|
||||||
|
when:
|
||||||
|
status: [success]
|
||||||
|
|
||||||
|
# Create artifacts
|
||||||
|
artifacts:
|
||||||
|
image: alpine:latest
|
||||||
|
commands:
|
||||||
|
- mkdir -p artifacts
|
||||||
|
- cp inventory artifacts/
|
||||||
|
- tar -czvf artifacts/inventory.tar.gz inventory
|
||||||
|
when:
|
||||||
|
status: [success]
|
||||||
|
|
||||||
|
# Optional step for creating a Docker image
|
||||||
|
docker-build:
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: ${DRONE_REPO_OWNER}/inventory
|
||||||
|
tags:
|
||||||
|
- latest
|
||||||
|
- ${DRONE_TAG##v}
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
when:
|
||||||
|
branch: main
|
||||||
|
event: [push, tag]
|
||||||
|
status: [success]
|
32
Dockerfile
Normal file
32
Dockerfile
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
FROM golang:1.21-alpine as builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
RUN apk add --no-cache git build-base
|
||||||
|
|
||||||
|
# Build the frontend
|
||||||
|
RUN apk add --no-cache nodejs npm
|
||||||
|
RUN cd web && npm ci && npm run build
|
||||||
|
|
||||||
|
# Build the backend with embedded frontend
|
||||||
|
RUN go mod download
|
||||||
|
RUN go generate ./...
|
||||||
|
RUN go build -o inventory -ldflags="-s -w" .
|
||||||
|
|
||||||
|
# Create minimal runtime image
|
||||||
|
FROM alpine:latest
|
||||||
|
RUN apk --no-cache add ca-certificates tzdata
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=builder /app/inventory .
|
||||||
|
COPY --from=builder /app/config.yaml ./config.yaml
|
||||||
|
|
||||||
|
# Create a non-root user to run the application
|
||||||
|
RUN adduser -D -H -h /app appuser
|
||||||
|
RUN chown -R appuser:appuser /app
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
EXPOSE 8088
|
||||||
|
CMD ["./inventory"]
|
34
main.go
34
main.go
|
@ -26,7 +26,7 @@ import (
|
||||||
db "git.entr0py.de/garionion/inventory/database/sqlite/generated"
|
db "git.entr0py.de/garionion/inventory/database/sqlite/generated"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed static/*
|
//go:embed static/* web/dist
|
||||||
var staticFiles embed.FS
|
var staticFiles embed.FS
|
||||||
|
|
||||||
//go:embed database/sqlite/migration/*.sql
|
//go:embed database/sqlite/migration/*.sql
|
||||||
|
@ -239,7 +239,37 @@ func main() {
|
||||||
apiHandler.RegisterRoutes(e, oauthMiddleware)
|
apiHandler.RegisterRoutes(e, oauthMiddleware)
|
||||||
|
|
||||||
// Serve static files - must be after API routes
|
// Serve static files - must be after API routes
|
||||||
e.GET("/*", echo.WrapHandler(http.FileServer(http.FS(staticFiles))))
|
// First try to serve from web/dist (for the Vue app)
|
||||||
|
e.GET("/*", func(c echo.Context) error {
|
||||||
|
path := c.Request().URL.Path
|
||||||
|
|
||||||
|
// Try to serve from web/dist first
|
||||||
|
content, err := staticFiles.ReadFile("web/dist" + path)
|
||||||
|
if err == nil {
|
||||||
|
// Determine content type
|
||||||
|
contentType := http.DetectContentType(content)
|
||||||
|
if strings.HasSuffix(path, ".css") {
|
||||||
|
contentType = "text/css"
|
||||||
|
} else if strings.HasSuffix(path, ".js") {
|
||||||
|
contentType = "application/javascript"
|
||||||
|
} else if strings.HasSuffix(path, ".html") {
|
||||||
|
contentType = "text/html"
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Blob(http.StatusOK, contentType, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the path is a directory or doesn't exist, try index.html for SPA routing
|
||||||
|
if path == "/" || err != nil {
|
||||||
|
indexContent, err := staticFiles.ReadFile("web/dist/index.html")
|
||||||
|
if err == nil {
|
||||||
|
return c.HTMLBlob(http.StatusOK, indexContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the static directory
|
||||||
|
return echo.WrapHandler(http.FileServer(http.FS(staticFiles)))(c)
|
||||||
|
})
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
addr := fmt.Sprintf(":%d", cfg.Server.Port)
|
addr := fmt.Sprintf(":%d", cfg.Server.Port)
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Welcome to Vuetify 3</title>
|
<title>Inventory Management System</title>
|
||||||
|
<base href="/">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|
Loading…
Add table
Reference in a new issue