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*
|
||||
|
||||
# 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"
|
||||
)
|
||||
|
||||
//go:embed static/*
|
||||
//go:embed static/* web/dist
|
||||
var staticFiles embed.FS
|
||||
|
||||
//go:embed database/sqlite/migration/*.sql
|
||||
|
@ -239,7 +239,37 @@ func main() {
|
|||
apiHandler.RegisterRoutes(e, oauthMiddleware)
|
||||
|
||||
// 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
|
||||
addr := fmt.Sprintf(":%d", cfg.Server.Port)
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<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>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
Loading…
Add table
Reference in a new issue