-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Go init #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| .git | ||
| .gitignore | ||
| README.md | ||
| node_modules | ||
| frontend/node_modules | ||
| frontend/dist | ||
| *.md |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| version: 2 | ||
| updates: | ||
| - package-ecosystem: "gomod" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "monthly" | ||
| time: "06:00" | ||
| commit-message: | ||
| prefix: "chore" | ||
| labels: | ||
| - "dependencies" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| dependencies: | ||
| patterns: | ||
| - "*" | ||
|
|
||
| - package-ecosystem: "github-actions" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "monthly" | ||
| time: "06:00" | ||
| commit-message: | ||
| prefix: "chore" | ||
| labels: | ||
| - "dependencies" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| dependencies: | ||
| patterns: | ||
| - "*" | ||
|
|
||
| - package-ecosystem: "docker" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "monthly" | ||
| time: "06:00" | ||
| commit-message: | ||
| prefix: "chore" | ||
| labels: | ||
| - "dependencies" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| dependencies: | ||
| patterns: | ||
| - "*" | ||
|
|
||
| - package-ecosystem: "npm" | ||
| directory: "/frontend" | ||
| schedule: | ||
| interval: "monthly" | ||
| time: "06:00" | ||
| commit-message: | ||
| prefix: "chore" | ||
| labels: | ||
| - "dependencies" | ||
| open-pull-requests-limit: 5 | ||
| groups: | ||
| dependencies: | ||
| patterns: | ||
| - "*" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| name: Quality | ||
|
|
||
| on: | ||
| push: | ||
| branches: ["main"] | ||
| pull_request: | ||
| branches: ["main"] | ||
|
|
||
| jobs: | ||
| backend: | ||
| name: Backend Quality Checks | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Set up Go | ||
| uses: actions/setup-go@v6 | ||
| with: | ||
| go-version: "1.25" | ||
| cache: true | ||
|
|
||
| - name: Verify dependencies | ||
| run: go mod verify | ||
|
|
||
| - name: Build | ||
| run: go build -v ./... | ||
|
|
||
| - name: Check format | ||
| run: go mod tidy && gofmt -s -w . && git diff --exit-code | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe easier to use https://github.com/golangci/golangci-lint? It does do even more checks. Example configuration: https://github.com/AikidoSec/firewall-go/blob/main/.golangci.yml |
||
|
|
||
| - name: Check vet | ||
| run: go vet ./... | ||
|
|
||
| - name: Run Tests | ||
| run: go test -v -race -coverprofile=coverage.txt ./... | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps nice if it is a separate job? Can of course be changed later. |
||
|
|
||
| frontend: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add to frontend PR later so it does not fail here? |
||
| name: Frontend Quality Checks | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Use Node.js 24.x | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: 24.x | ||
| cache: 'npm' | ||
| cache-dependency-path: ./frontend/package-lock.json | ||
|
|
||
| - name: Setup Aikido Safe Chain | ||
| working-directory: ./frontend | ||
| run: | | ||
| npm i -g @aikidosec/safe-chain | ||
| safe-chain setup-ci | ||
| - name: Install dependencies | ||
| working-directory: ./frontend | ||
| run: npm ci --safe-chain-skip-minimum-package-age | ||
|
|
||
| - name: Run Linter | ||
| working-directory: ./frontend | ||
| run: node --run lint | ||
|
|
||
| - name: Check Formatting | ||
| working-directory: ./frontend | ||
| run: node --run check:format | ||
|
|
||
| - name: Type Checking | ||
| working-directory: ./frontend | ||
| run: node --run check:types | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| name: Release Please | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE_NAME: ${{ github.repository }} | ||
|
|
||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
|
|
||
| jobs: | ||
| release-please: | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| new-release-created: ${{ steps.release-please-action.outputs.releases_created }} | ||
| tag-name: ${{ steps.release-please-action.outputs.tag_name }} | ||
| steps: | ||
| - uses: googleapis/release-please-action@v4 | ||
| id: release-please-action | ||
| with: | ||
| release-type: go | ||
|
|
||
| build-and-push: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| attestations: write | ||
| id-token: write | ||
| needs: release-please | ||
| if: needs.release-please.outputs.new-release-created == 'true' | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Set up QEMU | ||
| uses: docker/setup-qemu-action@v3 | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Log in to the Container registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Extract metadata (tags, labels) for Docker | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| tags: | | ||
| type=semver,pattern={{version}},value=${{ needs.release-please.outputs.tag-name }} | ||
| type=semver,pattern={{major}}.{{minor}}.{{patch}},value=${{ needs.release-please.outputs.tag-name }} | ||
| type=semver,pattern={{major}}.{{minor}},value=${{ needs.release-please.outputs.tag-name }} | ||
| type=semver,pattern={{major}},value=${{ needs.release-please.outputs.tag-name }} | ||
| type=raw,value=latest,enable={{is_default_branch}} | ||
|
|
||
| - name: Build and push Docker image | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: . | ||
| push: true | ||
| platforms: linux/amd64,linux/arm64 | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
| provenance: true | ||
| sbom: true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # data directory for SQLite | ||
| data/ | ||
| *.db | ||
|
|
||
| # Go | ||
| *.exe | ||
| *.exe~ | ||
| *.dll | ||
| *.so | ||
| *.dylib | ||
| *.test | ||
| *.out | ||
| go.work |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,73 @@ | ||||||
| # Build stage for frontend | ||||||
| FROM node:24-trixie-slim AS frontend-builder | ||||||
|
|
||||||
| WORKDIR /app/frontend | ||||||
|
|
||||||
| # Copy frontend package files | ||||||
| COPY frontend/package*.json ./ | ||||||
|
|
||||||
| # Install dependencies | ||||||
| RUN npm install | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| # Copy frontend source | ||||||
| COPY frontend/ ./ | ||||||
|
|
||||||
| # Build frontend | ||||||
| RUN npm run build | ||||||
|
|
||||||
| # Build stage for Go backend | ||||||
| FROM golang:1.25-alpine AS backend-builder | ||||||
|
|
||||||
| WORKDIR /app | ||||||
|
|
||||||
| # Install build dependencies for SQLite | ||||||
| RUN apk add --no-cache gcc musl-dev sqlite-dev | ||||||
|
|
||||||
| # Copy go mod files | ||||||
| COPY go.mod go.sum* ./ | ||||||
|
|
||||||
| # Download dependencies | ||||||
| RUN go mod download | ||||||
|
|
||||||
| # Copy source code | ||||||
| COPY cmd/ ./cmd/ | ||||||
| COPY internal/ ./internal/ | ||||||
| COPY main.go ./ | ||||||
|
|
||||||
| # Define build arguments | ||||||
| ARG VERSION=dev | ||||||
| ARG BUILD_TIME=unknown | ||||||
| ARG GIT_COMMIT=unknown | ||||||
|
|
||||||
| # Build the Go application with version information | ||||||
| RUN CGO_ENABLED=1 GOOS=linux go build \ | ||||||
| -ldflags="-X 'orca-cd/internal/config.Version=${VERSION}' \ | ||||||
| -X 'orca-cd/internal/config.BuildTime=${BUILD_TIME}' \ | ||||||
| -X 'orca-cd/internal/config.GitCommit=${GIT_COMMIT}'" \ | ||||||
| -o server . | ||||||
|
|
||||||
| # Final stage | ||||||
| FROM alpine:3.22 | ||||||
|
|
||||||
| RUN apk --no-cache add ca-certificates sqlite-libs | ||||||
|
|
||||||
| WORKDIR /root/ | ||||||
|
|
||||||
| # Copy the Go binary from backend builder | ||||||
| COPY --from=backend-builder /app/server . | ||||||
|
|
||||||
| # Copy the built frontend from frontend builder | ||||||
| COPY --from=frontend-builder /app/frontend/dist ./frontend/dist | ||||||
|
|
||||||
| # Create data directory for SQLite database | ||||||
| RUN mkdir -p ./data | ||||||
|
|
||||||
| VOLUME ["/data"] | ||||||
|
|
||||||
| # Expose port 8080 | ||||||
| EXPOSE 8080 | ||||||
|
|
||||||
| HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 CMD ["server", "health"] | ||||||
|
|
||||||
| # Run the server (root command starts the API) | ||||||
| CMD ["./server"] | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to add CLI options, e.g. for password reset, it might be nice to add the server to a sub command to display help if the root command is invoked? |
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| package cmd | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to put all Go code in a folder "backend"? |
||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
| "net/http" | ||
| "os" | ||
| "time" | ||
|
|
||
| "github.com/rs/zerolog/log" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
|
|
||
| var healthCmd = &cobra.Command{ | ||
| Use: "health", | ||
| Short: "Check the health of the API server", | ||
| Long: `Performs a health check by querying the /api/health endpoint.`, | ||
| Run: func(cmd *cobra.Command, args []string) { | ||
| port, _ := cmd.Flags().GetString("port") | ||
| url := fmt.Sprintf("http://localhost:%s/api/health", port) | ||
|
|
||
| log.Info().Str("url", url).Msg("Checking server health") | ||
|
|
||
| client := &http.Client{ | ||
| Timeout: 5 * time.Second, | ||
| } | ||
|
|
||
| resp, err := client.Get(url) | ||
| if err != nil { | ||
| log.Error().Err(err).Msg("Health check failed") | ||
| os.Exit(1) | ||
| } | ||
| defer resp.Body.Close() | ||
|
|
||
| if resp.StatusCode != http.StatusOK { | ||
| log.Error().Int("status_code", resp.StatusCode).Msg("Health check failed") | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| var result map[string]interface{} | ||
| if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { | ||
| log.Error().Err(err).Msg("Failed to parse response") | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| log.Info().Msg("Server is healthy!") | ||
| log.Info().Interface("status", result["status"]).Msg("Health status") | ||
| if serverTime, ok := result["time"].(string); ok { | ||
| parsedTime, err := time.Parse(time.RFC3339Nano, serverTime) | ||
| if err == nil { | ||
| log.Info().Str("server_time", parsedTime.Format(time.RFC1123)).Msg("Server time") | ||
| } else { | ||
| log.Info().Interface("server_time", result["time"]).Msg("Server time") | ||
| } | ||
| } | ||
| }, | ||
| } | ||
|
|
||
| func init() { | ||
| healthCmd.Flags().StringP("port", "p", "8080", "Port where the server is running") | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.