Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,24 @@ on:
- 'internal/**'
- '.github/workflows/backend.yml'

permissions: {}

jobs:
build_and_test_backend:
name: Build Backend
runs-on: ubuntu-latest
permissions:
contents: read
defaults:
run:
working-directory: .
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # actions/setup-go@v5
with:
go-version: '1.25'
go-version-file: go.mod
- name: Install dependencies
run: go mod download
- name: Build backend
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@ on:
- 'frontend/**' # Triggers only when changes are made inside the frontend folder
- '.github/workflows/frontend.yml' # Also trigger if the workflow file itself changes

permissions: {}

jobs:
build_and_test_frontend:
name: Build Frontend
runs-on: ubuntu-latest
permissions:
contents: read
defaults:
run:
working-directory: ./frontend
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
Expand Down
89 changes: 76 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ on:
types: [published]
workflow_dispatch:

permissions: {}

jobs:
# Step 1: Detect which folders have changed
detect-changes:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
frontend: ${{ steps.filter.outputs.frontend || steps.check-first.outputs.is_first }}
backend: ${{ steps.filter.outputs.backend || steps.check-first.outputs.is_first }}
current_tag: ${{ steps.get-tags.outputs.current_tag }}
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4
with:
fetch-depth: 0

Expand Down Expand Up @@ -46,7 +50,7 @@ jobs:
fi

- name: Check for file changes
uses: dorny/paths-filter@v3
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # dorny/paths-filter@v3
id: filter
# We only run this if it's NOT the first release to avoid errors
if: steps.check-first.outputs.is_first == 'false'
Expand All @@ -66,18 +70,20 @@ jobs:
needs: detect-changes
if: needs.detect-changes.outputs.frontend == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4

- name: Log in to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Build and push frontend
uses: docker/build-push-action@v5
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # docker/build-push-action@v5
with:
context: ./frontend
push: true
Expand All @@ -95,18 +101,20 @@ jobs:
needs: detect-changes
if: needs.detect-changes.outputs.backend == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4

- name: Log in to Docker Hub
uses: docker/login-action@v2
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Build and push backend
uses: docker/build-push-action@v5
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # docker/build-push-action@v5
with:
context: .
file: cmd/server/Dockerfile
Expand All @@ -118,26 +126,68 @@ jobs:
- name: Log out from Docker Hub
run: docker logout

# Step 4: Cross-compile goini and publish to GitHub Release + Homebrew tap (paused)
# Step 4: Trivy vulnerability scan on built images
scan-images:
needs: [detect-changes, build-frontend, build-backend]
if: always() && (needs.build-frontend.result == 'success' || needs.build-backend.result == 'success')
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Scan frontend image
if: needs.build-frontend.result == 'success'
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # aquasecurity/trivy-action@0.35.0
with:
image-ref: ${{ secrets.DOCKERHUB_USERNAME }}/goinitializer-web:${{ needs.detect-changes.outputs.current_tag }}
format: sarif
output: trivy-frontend.sarif
severity: CRITICAL
exit-code: '1'

- name: Upload frontend SARIF to GitHub Code Scanning
if: always() && needs.build-frontend.result == 'success'
uses: github/codeql-action/upload-sarif@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-frontend.sarif
category: trivy-frontend

- name: Scan backend image
if: needs.build-backend.result == 'success'
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # aquasecurity/trivy-action@0.35.0
with:
image-ref: ${{ secrets.DOCKERHUB_USERNAME }}/goinitializer:${{ needs.detect-changes.outputs.current_tag }}
format: sarif
output: trivy-backend.sarif
severity: CRITICAL
exit-code: '1'

- name: Upload backend SARIF to GitHub Code Scanning
if: always() && needs.build-backend.result == 'success'
uses: github/codeql-action/upload-sarif@5c8a8a642e79153f5d047b10ec1cba1d1cc65699 # github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-backend.sarif
category: trivy-backend

# Step 5: Cross-compile goini and publish to GitHub Release + Homebrew tap (paused)
release-cli:
if: false # paused — re-enable when CLI release is ready
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout Code
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Run goreleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
version: latest
Expand All @@ -146,9 +196,22 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }}

- name: Generate SBOM (SPDX)
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # anchore/sbom-action@v0
with:
format: spdx-json
artifact-name: sbom-spdx.json

- name: Generate SBOM (CycloneDX)
uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # anchore/sbom-action@v0
with:
format: cyclonedx-json
artifact-name: sbom-cyclonedx.json

create-summary:
needs: [detect-changes, build-frontend, build-backend, release-cli]
needs: [detect-changes, build-frontend, build-backend, scan-images, release-cli]
runs-on: ubuntu-latest
permissions: {}
if: always() # Ensure this runs even if builds are skipped
steps:
- name: Generate Summary
Expand Down
62 changes: 62 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Security Scans

on:
push:
branches: [main]
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- '.github/workflows/security.yml'
- '.gosec.yaml'
pull_request:
branches: [main]
paths:
- '**.go'
- 'go.mod'
- 'go.sum'
- '.github/workflows/security.yml'
- '.gosec.yaml'

permissions: {}

jobs:
govulncheck:
name: govulncheck
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Run govulncheck
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

gosec:
name: gosec SAST
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout Code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Run gosec
uses: securego/gosec@43fee884f668c23601e0bec7a8c095fba226f889 # securego/gosec@v2.22.1
with:
args: -conf .gosec.json -severity medium ./...
6 changes: 6 additions & 0 deletions .gosec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"global": {
"nosec": "false",
"audit": "true"
}
}
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,21 @@ docker-logs: ## Tail logs from all docker-compose services
.PHONY: docker-restart
docker-restart: docker-down docker-up ## Rebuild images and restart all services

# ── Docker image digest pinning ───────────────────────────────────────────────

.PHONY: update-digests
update-digests: ## Re-resolve and print current sha256 digests for all pinned base images
@echo "=== golang:1.25.8-alpine ==="
@docker buildx imagetools inspect golang:1.25.8-alpine --format '{{json .Manifest}}' | python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])"
@echo "=== alpine:latest ==="
@docker buildx imagetools inspect alpine:latest --format '{{json .Manifest}}' | python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])"
@echo "=== node:22-alpine ==="
@docker buildx imagetools inspect node:22-alpine --format '{{json .Manifest}}' | python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])"
@echo "=== nginx:stable-alpine ==="
@docker buildx imagetools inspect nginx:stable-alpine --format '{{json .Manifest}}' | python3 -c "import sys,json; print(json.load(sys.stdin)['digest'])"
@echo ""
@echo "Update FROM lines in cmd/server/Dockerfile and frontend/Dockerfile with the above digests."

# ── goini CLI helpers ─────────────────────────────────────────────────────────

.PHONY: install-cli
Expand Down
Loading
Loading