mirror of
https://github.com/lukaszraczylo/gohoarder.git
synced 2026-07-22 06:20:09 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a1e16c989b | ||
|
|
955250650e | ||
|
|
85c76150f5 | ||
|
|
1baf0993de | ||
|
|
434a15076e | ||
|
|
4e7350363d | ||
|
|
38edd735b6 | ||
|
|
c207aa72e9 | ||
|
|
bf0925a4fc | ||
|
|
bc854aa183 | ||
|
|
c4bb2f6e3a | ||
|
|
8848656193 | ||
|
|
3ecff61114 | ||
|
|
f86943b884 | ||
|
|
448bb70ac8 | ||
|
|
d63ae21133 | ||
|
|
f71414bbb1 | ||
|
|
bdcacfc8db | ||
|
|
a36080c6de | ||
|
|
0e6510636d | ||
|
|
276d406fb6 | ||
|
|
fb79d82072 | ||
|
|
bbcbeeaf88 | ||
|
|
9f4d8fc777 | ||
|
|
1f781f6620 | ||
|
|
8a9d786b1a | ||
|
|
e1a02a6d69 | ||
|
|
5c8565367c | ||
|
|
9db929ed8b | ||
|
|
0a2c5f6c2c | ||
|
|
ae632a3dd7 | ||
|
|
c212077478 | ||
|
|
af5e08a864 | ||
|
|
adc7388ee1 | ||
|
|
116ba9b006 | ||
|
|
03860dcb49 | ||
|
|
a1ec05b210 | ||
|
|
dc1d507a20 | ||
|
|
72f284f987 | ||
|
|
ef11972274 |
@@ -20,10 +20,15 @@ permissions:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
uses: lukaszraczylo/shared-actions/.github/workflows/go-release.yaml@main
|
||||
uses: lukaszraczylo/shared-actions/.github/workflows/go-release-cgo.yaml@main
|
||||
with:
|
||||
go-version: "1.25"
|
||||
docker-enabled: true
|
||||
# Enable frontend build
|
||||
node-enabled: true
|
||||
node-version: "20"
|
||||
node-build-script: "cd frontend && npm install -g pnpm && pnpm install --frozen-lockfile && pnpm run build"
|
||||
node-output-path: "frontend/dist"
|
||||
node-cache-dependency-path: "frontend/pnpm-lock.yaml"
|
||||
secrets: inherit
|
||||
|
||||
benchmark:
|
||||
|
||||
+156
-246
@@ -7,19 +7,91 @@ project_name: gohoarder
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
# Generate semantic version if not provided via git tag
|
||||
# This script can be used by CI/CD to inject custom versions
|
||||
# Usage: export GORELEASER_CURRENT_TAG=$(./script/generate-version.sh)
|
||||
# - ./script/generate-version.sh
|
||||
# Download and setup Zig for cross-compilation (Linux only)
|
||||
- bash -c 'if [ "$(uname -s)" = "Linux" ]; then wget https://ziglang.org/download/0.15.2/zig-x86_64-linux-0.15.2.tar.xz -O /tmp/zig.tar.xz && ls -lh /tmp/zig.tar.xz && tar -xvf /tmp/zig.tar.xz -C /tmp && echo "/tmp/zig-x86_64-linux-0.15.2" >> $GITHUB_PATH; fi'
|
||||
|
||||
# Build configuration
|
||||
# Note: Binaries are built inside Docker containers (multi-stage builds)
|
||||
# to avoid CGO cross-compilation issues. No standalone builds here.
|
||||
# All binaries built using Zig for consistent cross-compilation
|
||||
# Zig handles CGO cross-compilation without platform-specific toolchains
|
||||
# Binaries are injected into Docker images (no Docker compilation)
|
||||
builds:
|
||||
- id: gohoarder
|
||||
skip: true
|
||||
main: ./cmd/gohoarder
|
||||
binary: gohoarder
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
tags:
|
||||
- fts5
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X github.com/lukaszraczylo/gohoarder/internal/version.Version={{.Version}}
|
||||
- -X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit={{.ShortCommit}}
|
||||
- -X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime={{.Date}}
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
overrides:
|
||||
# Use Zig only for Linux cross-compilation
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target x86_64-linux-musl
|
||||
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target x86_64-linux-musl
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target aarch64-linux-musl
|
||||
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target aarch64-linux-musl
|
||||
# darwin/arm64 builds natively on macOS runner (no Zig)
|
||||
|
||||
- id: migrate
|
||||
skip: true
|
||||
main: ./cmd/migrate
|
||||
binary: migrate
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
tags:
|
||||
- fts5
|
||||
flags:
|
||||
- -trimpath
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X main.Version={{.Version}}
|
||||
- -X main.GitCommit={{.ShortCommit}}
|
||||
- -X main.BuildTime={{.Date}}
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: amd64
|
||||
overrides:
|
||||
# Use Zig only for Linux cross-compilation
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target x86_64-linux-musl
|
||||
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target x86_64-linux-musl
|
||||
- goos: linux
|
||||
goarch: arm64
|
||||
env:
|
||||
- CGO_ENABLED=1
|
||||
- CC=/tmp/zig-x86_64-linux-0.15.2/zig cc -target aarch64-linux-musl
|
||||
- CXX=/tmp/zig-x86_64-linux-0.15.2/zig c++ -target aarch64-linux-musl
|
||||
# darwin/arm64 builds natively on macOS runner (no Zig)
|
||||
|
||||
# Archives for releases
|
||||
archives:
|
||||
@@ -76,229 +148,114 @@ release:
|
||||
prerelease: auto
|
||||
|
||||
# Docker images (v2 - modern syntax)
|
||||
# Uses PRE-BUILT binaries from native builds (no Docker compilation - much faster!)
|
||||
# GoReleaser injects the platform-specific binary into each Docker image automatically
|
||||
# This avoids slow QEMU emulation for cross-architecture builds
|
||||
dockers_v2:
|
||||
# 1. Application Engine - Main GoHoarder server
|
||||
# Uses pre-built binary from 'gohoarder' build (no Docker compilation)
|
||||
# linux/arm64 binary is cross-compiled using Zig (fast, no QEMU!)
|
||||
- id: gohoarder-server
|
||||
ids:
|
||||
- gohoarder
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-server
|
||||
goarch: amd64
|
||||
build_flags:
|
||||
- "--build-arg=VERSION={{ .Version }}"
|
||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
||||
- "--build-arg=TARGETOS=linux"
|
||||
- "--build-arg=TARGETARCH=amd64"
|
||||
tags:
|
||||
- "{{ .Version }}-amd64"
|
||||
- latest-amd64
|
||||
- "{{ .Version }}"
|
||||
- latest
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
dockerfile: Dockerfile.server
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Server
|
||||
org.opencontainers.image.description: Universal package cache proxy server
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
extra_files:
|
||||
- config.yaml.example
|
||||
|
||||
- id: gohoarder-server-arm64
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-server
|
||||
goarch: arm64
|
||||
build_flags:
|
||||
- "--build-arg=VERSION={{ .Version }}"
|
||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
||||
- "--build-arg=TARGETOS=linux"
|
||||
- "--build-arg=TARGETARCH=arm64"
|
||||
tags:
|
||||
- "{{ .Version }}-arm64"
|
||||
- latest-arm64
|
||||
dockerfile: Dockerfile.server
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Server
|
||||
org.opencontainers.image.description: Universal package cache proxy server
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
flags:
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.title=GoHoarder Server"
|
||||
- "--label=org.opencontainers.image.description=Universal package cache proxy server"
|
||||
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||
extra_files:
|
||||
- config.yaml.example
|
||||
|
||||
# 2. Website - Frontend Dashboard
|
||||
# Note: Frontend is pre-built on CI runner and injected via frontend/dist
|
||||
- id: gohoarder-frontend
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-frontend
|
||||
goarch: amd64
|
||||
tags:
|
||||
- "{{ .Version }}-amd64"
|
||||
- latest-amd64
|
||||
- "{{ .Version }}"
|
||||
- latest
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
dockerfile: Dockerfile.frontend
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Frontend
|
||||
org.opencontainers.image.description: GoHoarder web dashboard
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
flags:
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.title=GoHoarder Frontend"
|
||||
- "--label=org.opencontainers.image.description=GoHoarder web dashboard"
|
||||
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||
extra_files:
|
||||
- frontend
|
||||
|
||||
- id: gohoarder-frontend-arm64
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-frontend
|
||||
goarch: arm64
|
||||
tags:
|
||||
- "{{ .Version }}-arm64"
|
||||
- latest-arm64
|
||||
dockerfile: Dockerfile.frontend
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Frontend
|
||||
org.opencontainers.image.description: GoHoarder web dashboard
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
extra_files:
|
||||
- frontend
|
||||
- frontend/dist
|
||||
|
||||
# 3. Scanning Engine - Background scanner worker
|
||||
# Uses pre-built binary from 'gohoarder' build (no Docker compilation)
|
||||
# linux/arm64 binary is cross-compiled using Zig (fast, no QEMU!)
|
||||
- id: gohoarder-scanner
|
||||
ids:
|
||||
- gohoarder
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-scanner
|
||||
goarch: amd64
|
||||
build_flags:
|
||||
- "--build-arg=VERSION={{ .Version }}"
|
||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
||||
- "--build-arg=TARGETOS=linux"
|
||||
- "--build-arg=TARGETARCH=amd64"
|
||||
tags:
|
||||
- "{{ .Version }}-amd64"
|
||||
- latest-amd64
|
||||
- "{{ .Version }}"
|
||||
- latest
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
dockerfile: Dockerfile.scanner
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Scanner
|
||||
org.opencontainers.image.description: GoHoarder vulnerability scanning engine
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
flags:
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.title=GoHoarder Scanner"
|
||||
- "--label=org.opencontainers.image.description=GoHoarder vulnerability scanning engine"
|
||||
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||
extra_files:
|
||||
- config.yaml.example
|
||||
|
||||
- id: gohoarder-scanner-arm64
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-scanner
|
||||
goarch: arm64
|
||||
build_flags:
|
||||
- "--build-arg=VERSION={{ .Version }}"
|
||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
||||
- "--build-arg=TARGETOS=linux"
|
||||
- "--build-arg=TARGETARCH=arm64"
|
||||
tags:
|
||||
- "{{ .Version }}-arm64"
|
||||
- latest-arm64
|
||||
dockerfile: Dockerfile.scanner
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Scanner
|
||||
org.opencontainers.image.description: GoHoarder vulnerability scanning engine
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
extra_files:
|
||||
- config.yaml.example
|
||||
|
||||
# 4. Gateway - Nginx reverse proxy for unified deployment
|
||||
- id: gohoarder-gateway
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-gateway
|
||||
goarch: amd64
|
||||
tags:
|
||||
- "{{ .Version }}-amd64"
|
||||
- latest-amd64
|
||||
dockerfile: Dockerfile.gateway
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Gateway
|
||||
org.opencontainers.image.description: Nginx reverse proxy for unified GoHoarder deployment
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
|
||||
- id: gohoarder-gateway-arm64
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-gateway
|
||||
goarch: arm64
|
||||
tags:
|
||||
- "{{ .Version }}-arm64"
|
||||
- latest-arm64
|
||||
dockerfile: Dockerfile.gateway
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Gateway
|
||||
org.opencontainers.image.description: Nginx reverse proxy for unified GoHoarder deployment
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
|
||||
# 5. Migration Engine - Database migration tool
|
||||
# 4. Migration Engine - Database migration tool
|
||||
# Uses pre-built binary from 'migrate' build (no Docker compilation)
|
||||
# linux/arm64 binary is cross-compiled using Zig (fast, no QEMU!)
|
||||
- id: gohoarder-migrate
|
||||
ids:
|
||||
- migrate
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-migrate
|
||||
goarch: amd64
|
||||
build_flags:
|
||||
- "--build-arg=VERSION={{ .Version }}"
|
||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
||||
- "--build-arg=TARGETOS=linux"
|
||||
- "--build-arg=TARGETARCH=amd64"
|
||||
tags:
|
||||
- "{{ .Version }}-amd64"
|
||||
- latest-amd64
|
||||
- "{{ .Version }}"
|
||||
- latest
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
dockerfile: Dockerfile.migrate
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Migrate
|
||||
org.opencontainers.image.description: Database migration tool for GoHoarder V2 schema
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
|
||||
- id: gohoarder-migrate-arm64
|
||||
images:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-migrate
|
||||
goarch: arm64
|
||||
build_flags:
|
||||
- "--build-arg=VERSION={{ .Version }}"
|
||||
- "--build-arg=GIT_COMMIT={{ .ShortCommit }}"
|
||||
- "--build-arg=BUILD_TIME={{ .Date }}"
|
||||
- "--build-arg=TARGETOS=linux"
|
||||
- "--build-arg=TARGETARCH=arm64"
|
||||
tags:
|
||||
- "{{ .Version }}-arm64"
|
||||
- latest-arm64
|
||||
dockerfile: Dockerfile.migrate
|
||||
labels:
|
||||
org.opencontainers.image.title: GoHoarder Migrate
|
||||
org.opencontainers.image.description: Database migration tool for GoHoarder V2 schema
|
||||
org.opencontainers.image.url: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.source: https://github.com/lukaszraczylo/gohoarder
|
||||
org.opencontainers.image.version: "{{ .Version }}"
|
||||
org.opencontainers.image.created: "{{ .Date }}"
|
||||
org.opencontainers.image.revision: "{{ .FullCommit }}"
|
||||
flags:
|
||||
- "--pull"
|
||||
- "--label=org.opencontainers.image.title=GoHoarder Migrate"
|
||||
- "--label=org.opencontainers.image.description=Database migration tool for GoHoarder V2 schema"
|
||||
- "--label=org.opencontainers.image.url=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.source=https://github.com/lukaszraczylo/gohoarder"
|
||||
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||
extra_files:
|
||||
- migrations
|
||||
|
||||
# Artifact signing with cosign
|
||||
signs:
|
||||
@@ -321,50 +278,3 @@ docker_signs:
|
||||
- sign
|
||||
- "${artifact}@${digest}"
|
||||
- "--yes"
|
||||
|
||||
# Docker manifests for multi-arch support
|
||||
docker_manifests:
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-server:{{ .Version }}
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-server:{{ .Version }}-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-server:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-server:latest
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-server:latest-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-server:latest-arm64
|
||||
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-frontend:{{ .Version }}
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-frontend:{{ .Version }}-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-frontend:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-frontend:latest
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-frontend:latest-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-frontend:latest-arm64
|
||||
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-scanner:{{ .Version }}
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-scanner:{{ .Version }}-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-scanner:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-scanner:latest
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-scanner:latest-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-scanner:latest-arm64
|
||||
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-gateway:{{ .Version }}
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-gateway:{{ .Version }}-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-gateway:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-gateway:latest
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-gateway:latest-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-gateway:latest-arm64
|
||||
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-migrate:{{ .Version }}
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-migrate:{{ .Version }}-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-migrate:{{ .Version }}-arm64
|
||||
- name_template: ghcr.io/lukaszraczylo/gohoarder-migrate:latest
|
||||
image_templates:
|
||||
- ghcr.io/lukaszraczylo/gohoarder-migrate:latest-amd64
|
||||
- ghcr.io/lukaszraczylo/gohoarder-migrate:latest-arm64
|
||||
|
||||
+127
-32
@@ -1,28 +1,15 @@
|
||||
# Website - Frontend Dashboard
|
||||
# Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
# Website - Frontend Dashboard with integrated reverse proxy
|
||||
# Combines frontend serving and backend proxy (previously separate gateway)
|
||||
# EXPECTS: Pre-built frontend files in frontend/dist/ directory
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy frontend source
|
||||
COPY frontend/package.json frontend/pnpm-lock.yaml ./
|
||||
COPY frontend/ ./
|
||||
|
||||
# Install pnpm and dependencies
|
||||
RUN npm install -g pnpm && \
|
||||
pnpm install --frozen-lockfile
|
||||
|
||||
# Build the frontend
|
||||
RUN pnpm run build
|
||||
|
||||
# Production stage
|
||||
FROM nginx:alpine
|
||||
|
||||
# Install envsubst for runtime configuration
|
||||
RUN apk add --no-cache gettext
|
||||
|
||||
# Copy built frontend
|
||||
COPY --from=builder /build/dist /usr/share/nginx/html
|
||||
# Copy pre-built frontend files
|
||||
# These are built on the CI runner and injected via extra_files
|
||||
COPY frontend/dist /usr/share/nginx/html
|
||||
|
||||
# Create runtime config injection script
|
||||
RUN cat > /docker-entrypoint.d/40-inject-config.sh <<'EOF'
|
||||
@@ -48,21 +35,121 @@ EOF
|
||||
|
||||
RUN chmod +x /docker-entrypoint.d/40-inject-config.sh
|
||||
|
||||
# Copy nginx configuration
|
||||
RUN cat > /etc/nginx/conf.d/default.conf <<'EOF'
|
||||
# Create nginx templates directory and configuration template with backend proxy support
|
||||
RUN mkdir -p /etc/nginx/templates && \
|
||||
cat > /etc/nginx/templates/default.conf.template <<'EOF'
|
||||
# Upstream backend server
|
||||
upstream backend {
|
||||
server ${BACKEND_HOST}:${BACKEND_PORT};
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# Cache configuration
|
||||
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:10m max_size=100m inactive=60m use_temp_path=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
server_name ${SERVER_NAME};
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Client body size for package uploads
|
||||
client_max_body_size 500M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/access.log combined;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
|
||||
# Compression
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
|
||||
|
||||
# SPA routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
# API endpoints - proxy to backend
|
||||
location /api/ {
|
||||
# Proxy settings
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Timeouts for long-running operations
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
|
||||
# Buffer settings
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
proxy_pass http://backend/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Metrics endpoint
|
||||
location /metrics {
|
||||
proxy_pass http://backend/metrics;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
|
||||
# Package download endpoints
|
||||
location ~ ^/(npm|pypi|go)/ {
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Extended timeouts for package downloads
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
|
||||
# Large buffer for package downloads
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 128k;
|
||||
proxy_buffers 4 256k;
|
||||
proxy_busy_buffers_size 256k;
|
||||
}
|
||||
|
||||
# WebSocket support
|
||||
location /ws/ {
|
||||
proxy_pass http://backend/ws/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# WebSocket timeouts
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
}
|
||||
|
||||
# Runtime configuration endpoint
|
||||
@@ -72,19 +159,23 @@ server {
|
||||
add_header Expires "0";
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Cache static assets
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Frontend SPA - serve index.html for all other routes
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Create cache directory
|
||||
RUN mkdir -p /var/cache/nginx/static && \
|
||||
chown -R nginx:nginx /var/cache/nginx
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
@@ -95,6 +186,10 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
# Environment variables with defaults
|
||||
ENV API_BASE_URL=/api \
|
||||
APP_VERSION=unknown \
|
||||
APP_NAME=GoHoarder
|
||||
APP_NAME=GoHoarder \
|
||||
BACKEND_HOST=gohoarder-server \
|
||||
BACKEND_PORT=8080 \
|
||||
SERVER_NAME=_
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
# Use nginx template substitution and start nginx
|
||||
CMD ["/bin/sh", "-c", "envsubst '$$BACKEND_HOST $$BACKEND_PORT $$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
# Gateway - Nginx reverse proxy for unified deployment
|
||||
# Routes traffic between frontend and backend under single vhost
|
||||
FROM nginx:alpine
|
||||
|
||||
# Install envsubst for runtime configuration
|
||||
RUN apk add --no-cache gettext
|
||||
|
||||
# Copy nginx configuration template
|
||||
COPY <<'EOF' /etc/nginx/templates/default.conf.template
|
||||
# Upstream servers
|
||||
upstream backend {
|
||||
server ${BACKEND_HOST}:${BACKEND_PORT};
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
upstream frontend {
|
||||
server ${FRONTEND_HOST}:${FRONTEND_PORT};
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
# Rate limiting zones
|
||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
|
||||
limit_req_zone $binary_remote_addr zone=download_limit:10m rate=5r/s;
|
||||
|
||||
# Cache configuration
|
||||
proxy_cache_path /var/cache/nginx/static levels=1:2 keys_zone=static_cache:10m max_size=100m inactive=60m use_temp_path=off;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name ${SERVER_NAME};
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Client body size for package uploads
|
||||
client_max_body_size 500M;
|
||||
client_body_timeout 300s;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/access.log combined;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
|
||||
# API endpoints - proxy to backend
|
||||
location /api/ {
|
||||
# Rate limiting
|
||||
limit_req zone=api_limit burst=20 nodelay;
|
||||
|
||||
# Proxy settings
|
||||
proxy_pass http://backend/;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
# Headers
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
|
||||
# Connection reuse
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Timeouts for long-running operations
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
|
||||
# Buffer settings
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 8 4k;
|
||||
proxy_busy_buffers_size 8k;
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
proxy_pass http://backend/health;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Metrics endpoint (optional - may want to restrict access)
|
||||
location /metrics {
|
||||
# Uncomment to restrict to internal networks
|
||||
# allow 10.0.0.0/8;
|
||||
# allow 172.16.0.0/12;
|
||||
# allow 192.168.0.0/16;
|
||||
# deny all;
|
||||
|
||||
proxy_pass http://backend/metrics;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
}
|
||||
|
||||
# Package download endpoints with rate limiting
|
||||
location ~ ^/(npm|pypi|go)/ {
|
||||
limit_req zone=download_limit burst=10 nodelay;
|
||||
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Extended timeouts for package downloads
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_read_timeout 600s;
|
||||
|
||||
# Large buffer for package downloads
|
||||
proxy_buffering on;
|
||||
proxy_buffer_size 128k;
|
||||
proxy_buffers 4 256k;
|
||||
proxy_busy_buffers_size 256k;
|
||||
}
|
||||
|
||||
# Frontend - serve SPA
|
||||
location / {
|
||||
proxy_pass http://frontend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header Connection "";
|
||||
|
||||
# Cache static assets
|
||||
proxy_cache static_cache;
|
||||
proxy_cache_valid 200 1h;
|
||||
proxy_cache_bypass $http_cache_control;
|
||||
add_header X-Cache-Status $upstream_cache_status;
|
||||
}
|
||||
|
||||
# WebSocket support (if needed for future features)
|
||||
location /ws/ {
|
||||
proxy_pass http://backend/ws/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# WebSocket timeouts
|
||||
proxy_connect_timeout 7d;
|
||||
proxy_send_timeout 7d;
|
||||
proxy_read_timeout 7d;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS server (uncomment and configure SSL certificates)
|
||||
# server {
|
||||
# listen 443 ssl http2;
|
||||
# server_name ${SERVER_NAME};
|
||||
#
|
||||
# ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||
# ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||
#
|
||||
# # SSL configuration
|
||||
# ssl_protocols TLSv1.2 TLSv1.3;
|
||||
# ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
# ssl_prefer_server_ciphers on;
|
||||
# ssl_session_cache shared:SSL:10m;
|
||||
# ssl_session_timeout 10m;
|
||||
#
|
||||
# # Include all location blocks from above
|
||||
# # ... (copy from HTTP server block)
|
||||
# }
|
||||
EOF
|
||||
|
||||
# Create cache directory
|
||||
RUN mkdir -p /var/cache/nginx/static && \
|
||||
chown -R nginx:nginx /var/cache/nginx
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80 443
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD wget --quiet --tries=1 --spider http://localhost/health || exit 1
|
||||
|
||||
# Environment variables with defaults
|
||||
ENV BACKEND_HOST=gohoarder-server \
|
||||
BACKEND_PORT=8080 \
|
||||
FRONTEND_HOST=gohoarder-frontend \
|
||||
FRONTEND_PORT=80 \
|
||||
SERVER_NAME=_
|
||||
|
||||
# Use nginx with template substitution
|
||||
CMD ["/bin/sh", "-c", "envsubst '$$BACKEND_HOST $$BACKEND_PORT $$FRONTEND_HOST $$FRONTEND_PORT $$SERVER_NAME' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
|
||||
+10
-39
@@ -1,44 +1,10 @@
|
||||
# Migration Engine - Database Migration Tool
|
||||
# Multi-stage build to compile with CGO support
|
||||
ARG TARGETOS=linux
|
||||
ARG TARGETARCH=amd64
|
||||
# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation)
|
||||
# GoReleaser injects the platform-specific binary automatically
|
||||
|
||||
# Build stage
|
||||
FROM --platform=$TARGETOS/$TARGETARCH golang:1.23-alpine AS builder
|
||||
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||
|
||||
# Install build dependencies for CGO
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
g++ \
|
||||
musl-dev \
|
||||
sqlite-dev \
|
||||
git
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy go mod files
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build with CGO enabled
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG VERSION=dev
|
||||
ARG GIT_COMMIT=unknown
|
||||
ARG BUILD_TIME=unknown
|
||||
|
||||
RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
||||
go build -ldflags="-s -w \
|
||||
-X main.Version=${VERSION} \
|
||||
-X main.GitCommit=${GIT_COMMIT} \
|
||||
-X main.BuildTime=${BUILD_TIME}" \
|
||||
-o migrate ./cmd/migrate
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:latest
|
||||
|
||||
# Install runtime dependencies (including CGO/SQLite dependencies)
|
||||
RUN apk add --no-cache \
|
||||
@@ -54,10 +20,15 @@ RUN apk add --no-cache \
|
||||
RUN addgroup -g 1000 gohoarder && \
|
||||
adduser -D -u 1000 -G gohoarder gohoarder
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /build/migrate /usr/local/bin/migrate
|
||||
# Copy pre-built binary from GoReleaser
|
||||
# GoReleaser will automatically inject the correct binary for the target platform
|
||||
# In split/merge mode, binaries are in linux/${TARGETARCH}/ subdirectories
|
||||
COPY linux/${TARGETARCH}/migrate /usr/local/bin/migrate
|
||||
RUN chmod +x /usr/local/bin/migrate
|
||||
|
||||
# Copy migration SQL files
|
||||
COPY migrations /migrations
|
||||
|
||||
WORKDIR /app
|
||||
USER gohoarder
|
||||
|
||||
|
||||
+18
-39
@@ -1,44 +1,10 @@
|
||||
# Scanning Engine - Background Scanner Worker
|
||||
# Multi-stage build to compile with CGO support
|
||||
ARG TARGETOS=linux
|
||||
ARG TARGETARCH=amd64
|
||||
# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation)
|
||||
# GoReleaser injects the platform-specific binary automatically
|
||||
|
||||
# Build stage
|
||||
FROM --platform=$TARGETOS/$TARGETARCH golang:1.23-alpine AS builder
|
||||
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||
|
||||
# Install build dependencies for CGO
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
g++ \
|
||||
musl-dev \
|
||||
sqlite-dev \
|
||||
git
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy go mod files
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build with CGO enabled
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG VERSION=dev
|
||||
ARG GIT_COMMIT=unknown
|
||||
ARG BUILD_TIME=unknown
|
||||
|
||||
RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
||||
go build -ldflags="-s -w \
|
||||
-X github.com/lukaszraczylo/gohoarder/internal/version.Version=${VERSION} \
|
||||
-X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit=${GIT_COMMIT} \
|
||||
-X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime=${BUILD_TIME}" \
|
||||
-o gohoarder ./cmd/gohoarder
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:latest
|
||||
|
||||
# Install scanning tools and runtime dependencies (including CGO/SQLite dependencies)
|
||||
RUN apk add --no-cache \
|
||||
@@ -50,6 +16,10 @@ RUN apk add --no-cache \
|
||||
bash \
|
||||
sqlite-libs \
|
||||
musl \
|
||||
python3 \
|
||||
py3-pip \
|
||||
npm \
|
||||
go \
|
||||
&& update-ca-certificates
|
||||
|
||||
# Install Trivy for container scanning
|
||||
@@ -58,6 +28,13 @@ RUN curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/
|
||||
# Install Grype for vulnerability scanning
|
||||
RUN curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
|
||||
|
||||
# Install govulncheck for Go vulnerability scanning
|
||||
RUN go install golang.org/x/vuln/cmd/govulncheck@latest && \
|
||||
mv /root/go/bin/govulncheck /usr/local/bin/
|
||||
|
||||
# Install pip-audit for Python package vulnerability scanning
|
||||
RUN pip3 install --no-cache-dir pip-audit --break-system-packages
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1000 scanner && \
|
||||
adduser -D -u 1000 -G scanner scanner
|
||||
@@ -75,8 +52,10 @@ RUN mkdir -p /var/cache/gohoarder \
|
||||
/var/lib/gohoarder \
|
||||
/var/lib/trivy
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /build/gohoarder /usr/local/bin/gohoarder
|
||||
# Copy pre-built binary from GoReleaser
|
||||
# GoReleaser will automatically inject the correct binary for the target platform
|
||||
# In split/merge mode, binaries are in linux/${TARGETARCH}/ subdirectories
|
||||
COPY linux/${TARGETARCH}/gohoarder /usr/local/bin/gohoarder
|
||||
RUN chmod +x /usr/local/bin/gohoarder
|
||||
|
||||
# Copy example config
|
||||
|
||||
+9
-41
@@ -1,46 +1,12 @@
|
||||
# Application Engine - GoHoarder Server
|
||||
# Multi-stage build to compile with CGO support
|
||||
ARG TARGETOS=linux
|
||||
ARG TARGETARCH=amd64
|
||||
# Application Engine - Main GoHoarder Server
|
||||
# This Dockerfile expects a PRE-BUILT binary from GoReleaser (no compilation)
|
||||
# GoReleaser injects the platform-specific binary automatically
|
||||
|
||||
# Build stage
|
||||
FROM --platform=$TARGETOS/$TARGETARCH golang:1.23-alpine AS builder
|
||||
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||
|
||||
# Install build dependencies for CGO
|
||||
RUN apk add --no-cache \
|
||||
gcc \
|
||||
g++ \
|
||||
musl-dev \
|
||||
sqlite-dev \
|
||||
git
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Copy go mod files
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build with CGO enabled
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
ARG VERSION=dev
|
||||
ARG GIT_COMMIT=unknown
|
||||
ARG BUILD_TIME=unknown
|
||||
|
||||
RUN CGO_ENABLED=1 GOOS=$TARGETOS GOARCH=$TARGETARCH \
|
||||
go build -ldflags="-s -w \
|
||||
-X github.com/lukaszraczylo/gohoarder/internal/version.Version=${VERSION} \
|
||||
-X github.com/lukaszraczylo/gohoarder/internal/version.GitCommit=${GIT_COMMIT} \
|
||||
-X github.com/lukaszraczylo/gohoarder/internal/version.BuildTime=${BUILD_TIME}" \
|
||||
-o gohoarder ./cmd/gohoarder
|
||||
|
||||
# Runtime stage
|
||||
FROM alpine:latest
|
||||
|
||||
# Install runtime dependencies (including CGO/SQLite dependencies)
|
||||
# Install runtime dependencies (CGO/SQLite requires these)
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates \
|
||||
tzdata \
|
||||
@@ -62,8 +28,10 @@ RUN mkdir -p /var/cache/gohoarder \
|
||||
chmod -R 750 /var/cache/gohoarder \
|
||||
/var/lib/gohoarder
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /build/gohoarder /usr/local/bin/gohoarder
|
||||
# Copy pre-built binary from GoReleaser
|
||||
# GoReleaser will automatically inject the correct binary for the target platform
|
||||
# In split/merge mode, binaries are in linux/${TARGETARCH}/ subdirectories
|
||||
COPY linux/${TARGETARCH}/gohoarder /usr/local/bin/gohoarder
|
||||
RUN chmod +x /usr/local/bin/gohoarder
|
||||
|
||||
# Copy example config
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# GoHoarder - Kubernetes Deployment (All-in-One)
|
||||
# This manifest deploys all GoHoarder services under a single ingress
|
||||
# The frontend includes integrated nginx reverse proxy to the backend
|
||||
#
|
||||
# Usage:
|
||||
# kubectl create namespace gohoarder
|
||||
@@ -222,6 +223,13 @@ spec:
|
||||
value: "1.0.0"
|
||||
- name: APP_NAME
|
||||
value: GoHoarder
|
||||
# Backend proxy configuration (frontend includes nginx reverse proxy)
|
||||
- name: BACKEND_HOST
|
||||
value: gohoarder-server
|
||||
- name: BACKEND_PORT
|
||||
value: "8080"
|
||||
- name: SERVER_NAME
|
||||
value: hoarder.i.raczylo.com
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
@@ -325,88 +333,6 @@ spec:
|
||||
configMap:
|
||||
name: gohoarder-config
|
||||
|
||||
---
|
||||
# Deployment - Gateway (Nginx Reverse Proxy)
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: gohoarder-gateway
|
||||
namespace: gohoarder
|
||||
labels:
|
||||
app.kubernetes.io/name: gohoarder
|
||||
app.kubernetes.io/component: gateway
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: gohoarder
|
||||
app.kubernetes.io/component: gateway
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: gohoarder
|
||||
app.kubernetes.io/component: gateway
|
||||
spec:
|
||||
containers:
|
||||
- name: gateway
|
||||
image: ghcr.io/lukaszraczylo/gohoarder-gateway:latest
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: BACKEND_HOST
|
||||
value: gohoarder-server
|
||||
- name: BACKEND_PORT
|
||||
value: "8080"
|
||||
- name: FRONTEND_HOST
|
||||
value: gohoarder-frontend
|
||||
- name: FRONTEND_PORT
|
||||
value: "80"
|
||||
- name: SERVER_NAME
|
||||
value: hoarder.i.raczylo.com
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 256Mi
|
||||
|
||||
---
|
||||
# Service - Gateway
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: gohoarder-gateway
|
||||
namespace: gohoarder
|
||||
labels:
|
||||
app.kubernetes.io/name: gohoarder
|
||||
app.kubernetes.io/component: gateway
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
selector:
|
||||
app.kubernetes.io/name: gohoarder
|
||||
app.kubernetes.io/component: gateway
|
||||
|
||||
---
|
||||
# Ingress - Expose via domain
|
||||
apiVersion: networking.k8s.io/v1
|
||||
@@ -436,7 +362,7 @@ spec:
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: gohoarder-gateway
|
||||
name: gohoarder-frontend
|
||||
port:
|
||||
number: 80
|
||||
# Uncomment for HTTPS/TLS
|
||||
@@ -477,20 +403,20 @@ spec:
|
||||
averageUtilization: 80
|
||||
|
||||
---
|
||||
# HorizontalPodAutoscaler - Gateway
|
||||
# HorizontalPodAutoscaler - Frontend
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: gohoarder-gateway
|
||||
name: gohoarder-frontend
|
||||
namespace: gohoarder
|
||||
labels:
|
||||
app.kubernetes.io/name: gohoarder
|
||||
app.kubernetes.io/component: gateway
|
||||
app.kubernetes.io/component: frontend
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: gohoarder-gateway
|
||||
name: gohoarder-frontend
|
||||
minReplicas: 2
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
|
||||
+14
-38
@@ -2,7 +2,7 @@ version: '3.8'
|
||||
|
||||
# GoHoarder - Unified Deployment Example
|
||||
# This docker-compose file demonstrates deploying all GoHoarder services
|
||||
# under a single domain using the gateway reverse proxy
|
||||
# The frontend includes integrated reverse proxy to the backend
|
||||
|
||||
services:
|
||||
# Backend - Main application server
|
||||
@@ -39,7 +39,7 @@ services:
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
# Frontend - Web dashboard
|
||||
# Frontend - Web dashboard with integrated reverse proxy
|
||||
gohoarder-frontend:
|
||||
image: ghcr.io/lukaszraczylo/gohoarder-frontend:latest
|
||||
container_name: gohoarder-frontend
|
||||
@@ -49,8 +49,19 @@ services:
|
||||
- API_BASE_URL=/api
|
||||
- APP_VERSION=1.0.0
|
||||
- APP_NAME=GoHoarder
|
||||
# Backend proxy configuration (frontend includes nginx reverse proxy)
|
||||
- BACKEND_HOST=gohoarder-server
|
||||
- BACKEND_PORT=8080
|
||||
- SERVER_NAME=hoarder.i.raczylo.com
|
||||
ports:
|
||||
# Map to host port 80 (HTTP)
|
||||
- "80:80"
|
||||
# Map to host port 443 (HTTPS) - uncomment if using SSL
|
||||
# - "443:443"
|
||||
networks:
|
||||
- gohoarder-internal
|
||||
depends_on:
|
||||
- gohoarder-server
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
||||
interval: 30s
|
||||
@@ -82,41 +93,6 @@ services:
|
||||
# profiles:
|
||||
# - scanner
|
||||
|
||||
# Gateway - Nginx reverse proxy
|
||||
gohoarder-gateway:
|
||||
image: ghcr.io/lukaszraczylo/gohoarder-gateway:latest
|
||||
container_name: gohoarder-gateway
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# Backend service connection
|
||||
- BACKEND_HOST=gohoarder-server
|
||||
- BACKEND_PORT=8080
|
||||
# Frontend service connection
|
||||
- FRONTEND_HOST=gohoarder-frontend
|
||||
- FRONTEND_PORT=80
|
||||
# Server configuration
|
||||
- SERVER_NAME=hoarder.i.raczylo.com
|
||||
ports:
|
||||
# Map to host port 80 (HTTP)
|
||||
- "80:80"
|
||||
# Map to host port 443 (HTTPS) - uncomment if using SSL
|
||||
# - "443:443"
|
||||
networks:
|
||||
- gohoarder-internal
|
||||
depends_on:
|
||||
- gohoarder-server
|
||||
- gohoarder-frontend
|
||||
# Uncomment if using custom SSL certificates
|
||||
# volumes:
|
||||
# - ./ssl/cert.pem:/etc/nginx/ssl/cert.pem:ro
|
||||
# - ./ssl/key.pem:/etc/nginx/ssl/key.pem:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
|
||||
networks:
|
||||
gohoarder-internal:
|
||||
driver: bridge
|
||||
@@ -144,7 +120,7 @@ volumes:
|
||||
# - Metrics: http://localhost/metrics
|
||||
#
|
||||
# For production:
|
||||
# - Enable HTTPS in the gateway container
|
||||
# - Enable HTTPS in the frontend container (add SSL certificates to nginx)
|
||||
# - Set up proper SSL certificates
|
||||
# - Configure firewall rules
|
||||
# - Set appropriate resource limits
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"hash": "eb707743",
|
||||
"configHash": "e17a1322",
|
||||
"lockfileHash": "c2e83396",
|
||||
"browserHash": "ab796ace",
|
||||
"optimized": {},
|
||||
"chunks": {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -107,7 +107,7 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.4 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.12 // indirect
|
||||
@@ -120,7 +120,7 @@ require (
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.68.0 // indirect
|
||||
github.com/valyala/fasthttp v1.69.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect
|
||||
@@ -129,7 +129,7 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/grpc v1.78.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
|
||||
@@ -200,8 +200,8 @@ github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
@@ -248,8 +248,8 @@ github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9R
|
||||
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok=
|
||||
github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4=
|
||||
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
|
||||
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
@@ -285,8 +285,8 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -299,8 +299,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
||||
@@ -19,4 +19,4 @@ sources:
|
||||
maintainers:
|
||||
- name: Lukasz Raczylo
|
||||
email: [email protected]
|
||||
icon: https://raw.githubusercontent.com/lukaszraczylo/gohoarder/main/docs/logo.png
|
||||
# icon: https://raw.githubusercontent.com/lukaszraczylo/gohoarder/main/docs/logo.png # TODO: Add logo
|
||||
|
||||
@@ -93,24 +93,30 @@ data:
|
||||
low: {{ .Values.security.blockThresholds.low }}
|
||||
scanners:
|
||||
trivy:
|
||||
enabled: {{ .Values.security.scanners.trivy.enabled }}
|
||||
# Disabled in server config (no trivy binary), enabled via env var in scanner pod
|
||||
enabled: false
|
||||
timeout: {{ .Values.security.scanners.trivy.timeout | quote }}
|
||||
cache_db: {{ .Values.security.scanners.trivy.cacheDb | quote }}
|
||||
osv:
|
||||
# API-based scanner - works in both server and scanner pods
|
||||
enabled: {{ .Values.security.scanners.osv.enabled }}
|
||||
api_url: {{ .Values.security.scanners.osv.apiUrl | quote }}
|
||||
timeout: {{ .Values.security.scanners.osv.timeout | quote }}
|
||||
grype:
|
||||
enabled: {{ .Values.security.scanners.grype.enabled }}
|
||||
# Disabled in server config (no grype binary), enabled via env var in scanner pod
|
||||
enabled: false
|
||||
timeout: {{ .Values.security.scanners.grype.timeout | quote }}
|
||||
govulncheck:
|
||||
enabled: {{ .Values.security.scanners.govulncheck.enabled }}
|
||||
# Disabled in server config (no go/govulncheck binary), enabled via env var in scanner pod
|
||||
enabled: false
|
||||
timeout: {{ .Values.security.scanners.govulncheck.timeout | quote }}
|
||||
npm_audit:
|
||||
enabled: {{ .Values.security.scanners.npmAudit.enabled }}
|
||||
# Disabled in server config (no npm binary), enabled via env var in scanner pod
|
||||
enabled: false
|
||||
timeout: {{ .Values.security.scanners.npmAudit.timeout | quote }}
|
||||
pip_audit:
|
||||
enabled: {{ .Values.security.scanners.pipAudit.enabled }}
|
||||
# Disabled in server config (no pip binary), enabled via env var in scanner pod
|
||||
enabled: false
|
||||
timeout: {{ .Values.security.scanners.pipAudit.timeout | quote }}
|
||||
ghsa:
|
||||
enabled: {{ .Values.security.scanners.ghsa.enabled }}
|
||||
|
||||
@@ -67,11 +67,18 @@ spec:
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: API_BASE_URL
|
||||
value: {{ .Values.frontend.backendUrl | default (printf "http://%s-server:%d" (include "gohoarder.fullname" .) (.Values.server.service.port | int)) | quote }}
|
||||
value: {{ .Values.frontend.backendUrl | default "/api" | quote }}
|
||||
- name: APP_VERSION
|
||||
value: {{ .Chart.AppVersion | quote }}
|
||||
- name: APP_NAME
|
||||
value: "GoHoarder"
|
||||
# Backend proxy configuration (frontend now includes reverse proxy)
|
||||
- name: BACKEND_HOST
|
||||
value: {{ include "gohoarder.fullname" . }}-server
|
||||
- name: BACKEND_PORT
|
||||
value: {{ .Values.server.service.port | quote }}
|
||||
- name: SERVER_NAME
|
||||
value: {{ .Values.frontend.serverName | default "_" | quote }}
|
||||
{{- with .Values.frontend.env }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -109,6 +109,17 @@ spec:
|
||||
env:
|
||||
- name: CONFIG_FILE
|
||||
value: /etc/gohoarder/config.yaml
|
||||
# Enable tool-based scanners only in scanner pod (server doesn't have the tools)
|
||||
- name: GOHOARDER_SECURITY_SCANNERS_TRIVY_ENABLED
|
||||
value: "{{ .Values.security.scanners.trivy.enabled }}"
|
||||
- name: GOHOARDER_SECURITY_SCANNERS_GRYPE_ENABLED
|
||||
value: "{{ .Values.security.scanners.grype.enabled }}"
|
||||
- name: GOHOARDER_SECURITY_SCANNERS_GOVULNCHECK_ENABLED
|
||||
value: "{{ .Values.security.scanners.govulncheck.enabled }}"
|
||||
- name: GOHOARDER_SECURITY_SCANNERS_NPM_AUDIT_ENABLED
|
||||
value: "{{ .Values.security.scanners.npmAudit.enabled }}"
|
||||
- name: GOHOARDER_SECURITY_SCANNERS_PIP_AUDIT_ENABLED
|
||||
value: "{{ .Values.security.scanners.pipAudit.enabled }}"
|
||||
{{- if and (eq .Values.metadata.backend "postgresql") .Values.metadata.postgresql.existingSecret }}
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
{{- if .Values.ingress.enabled -}}
|
||||
{{- if .Values.ingress.frontend.enabled -}}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "gohoarder.fullname" . }}-frontend
|
||||
name: {{ include "gohoarder.fullname" . }}
|
||||
labels:
|
||||
{{- include "gohoarder.frontend.labels" . | nindent 4 }}
|
||||
{{- include "gohoarder.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
@@ -14,65 +13,17 @@ spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.frontend.tls.enabled }}
|
||||
{{- if .Values.ingress.tls.enabled }}
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.ingress.frontend.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||
secretName: {{ .Values.ingress.frontend.tls.secretName }}
|
||||
- {{ .Values.ingress.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||
secretName: {{ .Values.ingress.tls.secretName }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ .Values.ingress.frontend.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||
- host: {{ .Values.ingress.host | default (printf "%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /npm
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
- path: /pypi
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
- path: /go
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
- path: /ws
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
- path: /health
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
- path: /metrics
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
# Route all traffic to frontend (which now includes reverse proxy to backend)
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
@@ -81,38 +32,3 @@ spec:
|
||||
port:
|
||||
number: {{ .Values.frontend.service.port }}
|
||||
{{- end }}
|
||||
---
|
||||
{{- if .Values.ingress.api.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "gohoarder.fullname" . }}-api
|
||||
labels:
|
||||
{{- include "gohoarder.server.labels" . | nindent 4 }}
|
||||
{{- with .Values.ingress.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.ingress.className }}
|
||||
ingressClassName: {{ .Values.ingress.className }}
|
||||
{{- end }}
|
||||
{{- if .Values.ingress.api.tls.enabled }}
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.ingress.api.host | default (printf "api.%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||
secretName: {{ .Values.ingress.api.tls.secretName }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- host: {{ .Values.ingress.api.host | default (printf "api.%s.%s" "gohoarder" .Values.global.domain) | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ include "gohoarder.fullname" . }}-server
|
||||
port:
|
||||
number: {{ .Values.server.service.port }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -507,21 +507,12 @@ ingress:
|
||||
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
|
||||
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
|
||||
|
||||
# Ingress for frontend
|
||||
frontend:
|
||||
enabled: true
|
||||
host: "gohoarder.local"
|
||||
tls:
|
||||
enabled: false
|
||||
secretName: "gohoarder-frontend-tls"
|
||||
|
||||
# Ingress for API (if you want separate ingress)
|
||||
api:
|
||||
# Single ingress routes all traffic to frontend
|
||||
# Frontend now includes reverse proxy to backend (merged gateway functionality)
|
||||
host: "gohoarder.local"
|
||||
tls:
|
||||
enabled: false
|
||||
host: "api.gohoarder.local"
|
||||
tls:
|
||||
enabled: false
|
||||
secretName: "gohoarder-api-tls"
|
||||
secretName: "gohoarder-tls"
|
||||
|
||||
# Autoscaling configuration
|
||||
autoscaling:
|
||||
|
||||
+3
-1
@@ -290,7 +290,9 @@ func (a *App) initializeComponents() error {
|
||||
a.healthChecker.AddCheck("scanner", func(ctx context.Context) (health.Status, string) {
|
||||
if a.config.Security.Enabled {
|
||||
if err := a.scanManager.Health(ctx); err != nil {
|
||||
return health.StatusUnhealthy, err.Error()
|
||||
// Scanner failures (e.g., API rate limits) shouldn't mark server as unhealthy
|
||||
// Server can still serve cached packages, just can't scan new ones
|
||||
return health.StatusDegraded, err.Error()
|
||||
}
|
||||
}
|
||||
return health.StatusHealthy, ""
|
||||
|
||||
Vendored
+47
-1
@@ -145,7 +145,37 @@ func (m *Manager) getOrFetch(ctx context.Context, registry, name, version string
|
||||
if err == nil {
|
||||
// Cache hit!
|
||||
metrics.RecordCacheHit(registry)
|
||||
_ = m.metadata.UpdateDownloadCount(ctx, registry, name, version) // #nosec G104 -- Async update, error logged
|
||||
|
||||
// Update download count (log errors for debugging)
|
||||
if err := m.metadata.UpdateDownloadCount(ctx, registry, name, version); err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Str("registry", registry).
|
||||
Str("package", name).
|
||||
Str("version", version).
|
||||
Msg("Failed to update download count - package may not exist in database")
|
||||
|
||||
// Try to save package to database if it doesn't exist
|
||||
// This handles the case where storage has files but database was migrated/reset
|
||||
if saveErr := m.metadata.SavePackage(ctx, pkg); saveErr != nil {
|
||||
log.Error().
|
||||
Err(saveErr).
|
||||
Str("registry", registry).
|
||||
Str("package", name).
|
||||
Str("version", version).
|
||||
Msg("Failed to save package to database")
|
||||
} else {
|
||||
// Retry download count update after saving package
|
||||
if retryErr := m.metadata.UpdateDownloadCount(ctx, registry, name, version); retryErr != nil {
|
||||
log.Error().
|
||||
Err(retryErr).
|
||||
Str("registry", registry).
|
||||
Str("package", name).
|
||||
Str("version", version).
|
||||
Msg("Failed to update download count even after saving package")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Track download in analytics if enabled
|
||||
if m.analytics != nil {
|
||||
@@ -290,6 +320,22 @@ servePkg:
|
||||
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to retrieve just-stored package")
|
||||
}
|
||||
|
||||
// Track download count for first-time download (cache miss)
|
||||
// This ensures download count increments regardless of cache hit/miss
|
||||
if err := m.metadata.UpdateDownloadCount(ctx, registry, name, version); err != nil {
|
||||
log.Warn().
|
||||
Err(err).
|
||||
Str("registry", registry).
|
||||
Str("package", name).
|
||||
Str("version", version).
|
||||
Msg("Failed to update download count for newly cached package")
|
||||
}
|
||||
|
||||
// Track download in analytics if enabled
|
||||
if m.analytics != nil {
|
||||
m.trackDownload(registry, name, version, storedPkg.Size)
|
||||
}
|
||||
|
||||
return &CacheEntry{
|
||||
Package: storedPkg,
|
||||
Data: storedData,
|
||||
|
||||
Vendored
+3
@@ -343,6 +343,7 @@ func TestGet(t *testing.T) {
|
||||
s.On("Put", mock.Anything, "npm/lodash/4.17.21", mock.Anything, mock.Anything).Return(nil)
|
||||
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
||||
s.On("Get", mock.Anything, "npm/lodash/4.17.21").Return(io.NopCloser(strings.NewReader("upstream data")), nil)
|
||||
m.On("UpdateDownloadCount", mock.Anything, "npm", "lodash", "4.17.21").Return(nil)
|
||||
},
|
||||
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
||||
return io.NopCloser(strings.NewReader("upstream data")), "https://registry.npmjs.org/lodash", nil
|
||||
@@ -374,6 +375,7 @@ func TestGet(t *testing.T) {
|
||||
s.On("Put", mock.Anything, "npm/expired-pkg/1.0.0", mock.Anything, mock.Anything).Return(nil)
|
||||
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
||||
s.On("Get", mock.Anything, "npm/expired-pkg/1.0.0").Return(io.NopCloser(strings.NewReader("refreshed data")), nil)
|
||||
m.On("UpdateDownloadCount", mock.Anything, "npm", "expired-pkg", "1.0.0").Return(nil)
|
||||
},
|
||||
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
||||
return io.NopCloser(strings.NewReader("refreshed data")), "https://registry.npmjs.org/expired-pkg", nil
|
||||
@@ -435,6 +437,7 @@ func TestGet(t *testing.T) {
|
||||
m.On("SavePackage", mock.Anything, mock.Anything).Return(nil)
|
||||
// Second Get succeeds (after re-storing)
|
||||
s.On("Get", mock.Anything, "npm/inconsistent/1.0.0").Return(io.NopCloser(strings.NewReader("recovered data")), nil).Once()
|
||||
m.On("UpdateDownloadCount", mock.Anything, "npm", "inconsistent", "1.0.0").Return(nil)
|
||||
},
|
||||
fetchFunc: func(ctx context.Context) (io.ReadCloser, string, error) {
|
||||
return io.NopCloser(strings.NewReader("recovered data")), "https://registry.npmjs.org/inconsistent", nil
|
||||
|
||||
@@ -583,29 +583,38 @@ func (s *GORMStoreV2) GetTimeSeriesStats(ctx context.Context, period string, reg
|
||||
// Determine which table to query based on period
|
||||
var tableName string
|
||||
var since time.Time
|
||||
var bucketDuration time.Duration
|
||||
|
||||
switch period {
|
||||
case "1h":
|
||||
tableName = "download_stats_hourly"
|
||||
since = time.Now().Add(-1 * time.Hour)
|
||||
since = time.Now().Add(-1 * time.Hour).Truncate(time.Hour)
|
||||
bucketDuration = time.Hour
|
||||
case "1day":
|
||||
tableName = "download_stats_hourly"
|
||||
since = time.Now().Add(-24 * time.Hour)
|
||||
since = time.Now().Add(-24 * time.Hour).Truncate(time.Hour)
|
||||
bucketDuration = time.Hour
|
||||
case "7day":
|
||||
tableName = "download_stats_daily"
|
||||
since = time.Now().Add(-7 * 24 * time.Hour)
|
||||
since = time.Now().Add(-7 * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
bucketDuration = 24 * time.Hour
|
||||
case "30day":
|
||||
tableName = "download_stats_daily"
|
||||
since = time.Now().Add(-30 * 24 * time.Hour)
|
||||
since = time.Now().Add(-30 * 24 * time.Hour).Truncate(24 * time.Hour)
|
||||
bucketDuration = 24 * time.Hour
|
||||
default:
|
||||
tableName = "download_stats_hourly"
|
||||
since = time.Now().Add(-24 * time.Hour)
|
||||
since = time.Now().Add(-24 * time.Hour).Truncate(time.Hour)
|
||||
bucketDuration = time.Hour
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
// Try to get registry-level aggregate stats first
|
||||
query := s.db.WithContext(ctx).
|
||||
Table(tableName).
|
||||
Select("time_bucket as timestamp, download_count as value").
|
||||
Where("time_bucket >= ?", since)
|
||||
Where("time_bucket >= ? AND time_bucket <= ?", since, now)
|
||||
|
||||
// Filter by registry if specified
|
||||
if registry != "" {
|
||||
@@ -630,10 +639,45 @@ func (s *GORMStoreV2) GetTimeSeriesStats(ctx context.Context, period string, reg
|
||||
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to get time series stats")
|
||||
}
|
||||
|
||||
// If no aggregate data found, try summing package-level stats
|
||||
if len(results) == 0 {
|
||||
sumQuery := s.db.WithContext(ctx).
|
||||
Table(tableName).
|
||||
Select("time_bucket as timestamp, SUM(download_count) as value").
|
||||
Where("time_bucket >= ? AND time_bucket <= ?", since, now)
|
||||
|
||||
if registry != "" {
|
||||
registryID, err := s.getRegistryID(registry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sumQuery = sumQuery.Where("registry_id = ? AND package_id IS NOT NULL", registryID)
|
||||
} else {
|
||||
sumQuery = sumQuery.Where("package_id IS NOT NULL")
|
||||
}
|
||||
|
||||
sumQuery = sumQuery.Group("time_bucket").Order("time_bucket ASC")
|
||||
|
||||
if err := sumQuery.Scan(&results).Error; err != nil {
|
||||
return nil, errors.Wrap(err, errors.ErrCodeStorageFailure, "failed to get time series stats from package data")
|
||||
}
|
||||
}
|
||||
|
||||
// Create a map of existing data points
|
||||
dataMap := make(map[time.Time]int64)
|
||||
for _, r := range results {
|
||||
dataMap[r.Timestamp] = r.Value
|
||||
}
|
||||
|
||||
// Generate continuous time series with 0 values for missing periods
|
||||
for t := since; t.Before(now) || t.Equal(now); t = t.Add(bucketDuration) {
|
||||
value := int64(0)
|
||||
if v, exists := dataMap[t]; exists {
|
||||
value = v
|
||||
}
|
||||
stats.DataPoints = append(stats.DataPoints, &metadata.TimeSeriesDataPoint{
|
||||
Timestamp: r.Timestamp,
|
||||
Value: r.Value,
|
||||
Timestamp: t,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -107,11 +107,17 @@ func (s *Scanner) Health(ctx context.Context) error {
|
||||
}
|
||||
defer resp.Body.Close() // #nosec G104 -- Cleanup, error not critical
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("github api returned status: %d", resp.StatusCode)
|
||||
// Accept any 2xx or 403 (rate limit) as healthy
|
||||
// Rate limits are expected without a GitHub token and shouldn't fail health checks
|
||||
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode == http.StatusForbidden {
|
||||
log.Debug().Msg("GitHub API rate limited (expected without token)")
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
return fmt.Errorf("github api returned status: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
// mapRegistryToEcosystem maps our registry names to GitHub ecosystem names
|
||||
|
||||
+52
-2
@@ -121,13 +121,17 @@ func (s *Scanner) Scan(ctx context.Context, registry, packageName, version strin
|
||||
// Convert registry to OSV ecosystem
|
||||
ecosystem := s.registryToEcosystem(registry)
|
||||
|
||||
// Clean package name and version for Go modules
|
||||
// Go proxy cache keys include /@v/version.suffix which OSV doesn't understand
|
||||
cleanName, cleanVersion := s.cleanGoModuleName(packageName, version, ecosystem)
|
||||
|
||||
// Build request
|
||||
req := OSVRequest{
|
||||
Package: PackageInfo{
|
||||
Name: packageName,
|
||||
Name: cleanName,
|
||||
Ecosystem: ecosystem,
|
||||
},
|
||||
Version: version,
|
||||
Version: cleanVersion,
|
||||
}
|
||||
|
||||
// Marshal request
|
||||
@@ -199,6 +203,52 @@ func (s *Scanner) registryToEcosystem(registry string) string {
|
||||
}
|
||||
}
|
||||
|
||||
// cleanGoModuleName cleans Go module cache keys to extract the actual module path and version
|
||||
// Go proxy cache keys include /@v/version.suffix patterns that need to be cleaned
|
||||
// Examples:
|
||||
// - "gorm.io/driver/sqlite/@v/v1.6.0.zip" -> "gorm.io/driver/sqlite", "v1.6.0"
|
||||
// - "github.com/pkg/errors/@v/v0.9.1.mod" -> "github.com/pkg/errors", "v0.9.1"
|
||||
// - "regular-package" -> "regular-package", "version" (unchanged for non-Go)
|
||||
func (s *Scanner) cleanGoModuleName(packageName, version, ecosystem string) (string, string) {
|
||||
// Only clean for Go modules
|
||||
if ecosystem != "Go" {
|
||||
return packageName, version
|
||||
}
|
||||
|
||||
// Check if package name contains /@v/ pattern (Go module proxy format)
|
||||
if strings.Contains(packageName, "/@v/") {
|
||||
// Split on /@v/ to get the module path
|
||||
parts := strings.Split(packageName, "/@v/")
|
||||
if len(parts) == 2 {
|
||||
// parts[0] is the clean module path (e.g., "gorm.io/driver/sqlite")
|
||||
// parts[1] might be "v1.6.0.zip" or "v1.6.0.mod" or "v1.6.0.info"
|
||||
cleanName := parts[0]
|
||||
|
||||
// Extract version from the second part if version wasn't already clean
|
||||
// Remove file suffixes like .zip, .mod, .info
|
||||
versionPart := parts[1]
|
||||
versionPart = strings.TrimSuffix(versionPart, ".zip")
|
||||
versionPart = strings.TrimSuffix(versionPart, ".mod")
|
||||
versionPart = strings.TrimSuffix(versionPart, ".info")
|
||||
|
||||
// Use the extracted version if it looks valid, otherwise use the provided version
|
||||
if versionPart != "" && strings.HasPrefix(versionPart, "v") {
|
||||
return cleanName, versionPart
|
||||
}
|
||||
|
||||
return cleanName, version
|
||||
}
|
||||
}
|
||||
|
||||
// Also clean version of any file suffixes
|
||||
cleanVersion := version
|
||||
cleanVersion = strings.TrimSuffix(cleanVersion, ".zip")
|
||||
cleanVersion = strings.TrimSuffix(cleanVersion, ".mod")
|
||||
cleanVersion = strings.TrimSuffix(cleanVersion, ".info")
|
||||
|
||||
return packageName, cleanVersion
|
||||
}
|
||||
|
||||
// convertOSVResult converts OSV response to metadata.ScanResult
|
||||
func (s *Scanner) convertOSVResult(osvResp *OSVResponse, registry, packageName, version string) *metadata.ScanResult {
|
||||
vulnerabilities := make([]metadata.Vulnerability, 0, len(osvResp.Vulns))
|
||||
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
# Workflow prepare script for CI/CD
|
||||
# Installs CGO dependencies for SQLite support
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== GoHoarder Workflow Prepare ==="
|
||||
echo "Host OS: $(uname -s)"
|
||||
echo "Target GOOS: ${TARGET_GOOS:-auto}"
|
||||
echo "Target GOARCH: ${TARGET_GOARCH:-auto}"
|
||||
|
||||
# Detect host OS
|
||||
HOST_OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
# Install SQLite development headers based on platform
|
||||
case "$HOST_OS" in
|
||||
linux*)
|
||||
echo "Installing SQLite development headers for Linux..."
|
||||
if command -v apt-get &> /dev/null; then
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq libsqlite3-dev
|
||||
elif command -v yum &> /dev/null; then
|
||||
# RHEL/CentOS
|
||||
sudo yum install -y sqlite-devel
|
||||
elif command -v apk &> /dev/null; then
|
||||
# Alpine
|
||||
sudo apk add --no-cache sqlite-dev
|
||||
fi
|
||||
echo "✓ SQLite headers installed"
|
||||
;;
|
||||
|
||||
darwin*)
|
||||
echo "Installing SQLite for macOS..."
|
||||
# macOS usually has SQLite via Xcode Command Line Tools
|
||||
# but ensure it's available via Homebrew if needed
|
||||
if ! pkg-config --exists sqlite3; then
|
||||
brew install sqlite3
|
||||
fi
|
||||
echo "✓ SQLite available"
|
||||
;;
|
||||
|
||||
mingw*|msys*|cygwin*)
|
||||
echo "Installing SQLite for Windows..."
|
||||
# Download SQLite amalgamation for Windows
|
||||
SQLITE_VERSION="3470200"
|
||||
SQLITE_YEAR="2024"
|
||||
SQLITE_DIR="/c/sqlite"
|
||||
SQLITE_URL="https://www.sqlite.org/${SQLITE_YEAR}/sqlite-amalgamation-${SQLITE_VERSION}.zip"
|
||||
|
||||
mkdir -p "$SQLITE_DIR"
|
||||
curl -sSL "$SQLITE_URL" -o /tmp/sqlite.zip
|
||||
unzip -q /tmp/sqlite.zip -d /tmp/
|
||||
cp /tmp/sqlite-amalgamation-${SQLITE_VERSION}/* "$SQLITE_DIR/"
|
||||
rm -rf /tmp/sqlite.zip /tmp/sqlite-amalgamation-${SQLITE_VERSION}
|
||||
|
||||
echo "CGO_CFLAGS=-I${SQLITE_DIR}" >> "$GITHUB_ENV"
|
||||
echo "CGO_LDFLAGS=-L${SQLITE_DIR}" >> "$GITHUB_ENV"
|
||||
echo "✓ SQLite setup complete"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown OS: $HOST_OS - skipping SQLite setup"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Verify SQLite is available
|
||||
echo ""
|
||||
echo "=== Verifying SQLite availability ==="
|
||||
if pkg-config --exists sqlite3; then
|
||||
echo "✓ SQLite pkg-config found"
|
||||
pkg-config --modversion sqlite3
|
||||
else
|
||||
echo "⚠ SQLite pkg-config not found (may still work via system headers)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Workflow prepare complete ==="
|
||||
Reference in New Issue
Block a user