Skip to content

Commit acfa658

Browse files
authored
Initial commit
0 parents  commit acfa658

17 files changed

Lines changed: 895 additions & 0 deletions

File tree

.dockerignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.claude/
2+
.git/
3+
.github/
4+
.serena/
5+
6+
.dockerignore
7+
.env*
8+
.gitignore
9+
.mcp.json
10+
CLAUDE.md
11+
Dockerfile
12+
mise.toml
13+
Taskfile.yaml

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
DB_IAM_USER=
2+
DB_NAME=
3+
INSTANCE_CONNECTION_NAME=
4+
GOOGLE_APPLICATION_CREDENTIALS=

.github/workflows/cd.yaml

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
name: CD
2+
on:
3+
push:
4+
branches:
5+
- main
6+
workflow_dispatch:
7+
inputs:
8+
environment_name:
9+
description: Environment
10+
required: true
11+
type: choice
12+
options:
13+
- dev
14+
- stg
15+
- qa
16+
- prod
17+
concurrency:
18+
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.environment_name }}
19+
cancel-in-progress: true
20+
jobs:
21+
set-env:
22+
runs-on: ubuntu-latest
23+
outputs:
24+
environment: ${{ steps.set-env.outputs.environment }}
25+
service_name: ${{ steps.set-env.outputs.service_name }}
26+
image_tag: ${{ steps.set-env.outputs.image_tag }}
27+
steps:
28+
- uses: actions/checkout@v6
29+
- name: Set Environmet
30+
id: set-env
31+
run: |
32+
# Environment
33+
IS_WORKFLOW_DISPATCH="${{ github.event_name == 'workflow_dispatch' && github.event.inputs.environment_name != '' }}"
34+
IS_PUSH_TO_MAIN="${{ github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.base_ref == 'main') }}"
35+
if [ $IS_WORKFLOW_DISPATCH = "true" ]; then
36+
export TARGET_ENVIRONMENT="${{ github.event.inputs.environment_name }}"
37+
elif [ $IS_PUSH_TO_MAIN = "true" ]; then
38+
export TARGET_ENVIRONMENT="stg"
39+
else
40+
exit 1
41+
fi
42+
43+
echo "Environment: $TARGET_ENVIRONMENT"
44+
45+
# Service name suffix
46+
if [ "$TARGET_ENVIRONMENT" = "prod" ]; then
47+
export SERVICE_NAME_SUFFIX=""
48+
else
49+
export SERVICE_NAME_SUFFIX="-$TARGET_ENVIRONMENT"
50+
fi
51+
52+
export REPO_NAME=$(echo ${{ github.repository }} | awk -F '/' '{print $2}')
53+
export SERVICE_NAME=$REPO_NAME$SERVICE_NAME_SUFFIX
54+
export IMAGE_TAG=${{ vars._AR_HOSTNAME }}/${{ vars.PROJECT_ID }}/github-actions/$REPO_NAME/$SERVICE_NAME:${{ github.sha }}
55+
56+
echo "Repository name: $REPO_NAME"
57+
echo "Service name: $SERVICE_NAME"
58+
echo "Image tag: $IMAGE_TAG"
59+
60+
echo "environment=$TARGET_ENVIRONMENT" >> "$GITHUB_OUTPUT"
61+
echo "service_name=$SERVICE_NAME" >> "$GITHUB_OUTPUT"
62+
echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
63+
build-and-push:
64+
runs-on: ubuntu-latest
65+
permissions:
66+
contents: read
67+
id-token: write
68+
env:
69+
SERVICE_NAME: ${{ needs.set-env.outputs.service_name }}
70+
IMAGE_TAG: ${{ needs.set-env.outputs.image_tag }}
71+
needs:
72+
- set-env
73+
steps:
74+
- uses: actions/checkout@v6
75+
- name: Build
76+
run: |
77+
docker build \
78+
-t ${{ env.IMAGE_TAG }} \
79+
. \
80+
-f Dockerfile \
81+
--no-cache
82+
- name: Authenticate
83+
id: auth
84+
uses: google-github-actions/auth@v2
85+
with:
86+
workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
87+
service_account: ${{ vars.SERVICE_ACCOUNT }}
88+
- name: Setup Google Cloud
89+
uses: google-github-actions/setup-gcloud@v2
90+
with:
91+
project_id: ${{ vars.PROJECT_ID }}
92+
- name: Configure Docker
93+
run: |
94+
gcloud auth \
95+
configure-docker \
96+
${{ vars._AR_HOSTNAME }}
97+
- name: Push
98+
run: |
99+
docker push \
100+
${{ env.IMAGE_TAG }}
101+
deploy:
102+
runs-on: ubuntu-latest
103+
permissions:
104+
id-token: write
105+
environment: ${{ needs.set-env.outputs.environment }}
106+
env:
107+
SERVICE_NAME: ${{ needs.set-env.outputs.service_name }}
108+
IMAGE_TAG: ${{ needs.set-env.outputs.image_tag }}
109+
needs:
110+
- set-env
111+
- build-and-push
112+
steps:
113+
- name: Authenticate
114+
id: auth
115+
uses: google-github-actions/auth@v2
116+
with:
117+
workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }}
118+
service_account: ${{ vars.SERVICE_ACCOUNT }}
119+
- name: Deploy
120+
id: deploy
121+
uses: google-github-actions/deploy-cloudrun@v2
122+
with:
123+
service: ${{ env.SERVICE_NAME }}
124+
image: ${{ env.IMAGE_TAG }}
125+
region: ${{ vars._DEPLOY_REGION }}
126+
env_vars: |
127+
DB_IAM_USER=${{ vars.DB_IAM_USER }}
128+
DB_NAME=${{ vars.DB_NAME }}
129+
INSTANCE_CONNECTION_NAME=${{ vars.INSTANCE_CONNECTION_NAME }}
130+
env_vars_update_strategy: overwrite
131+
secrets_update_strategy: overwrite

.github/workflows/ci.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: CI
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
branches:
8+
- main
9+
types:
10+
- opened
11+
- ready_for_review
12+
- reopened
13+
- synchronize
14+
workflow_dispatch:
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
jobs:
19+
test:
20+
if: github.event.pull_request.draft == false
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v6
24+
- uses: jdx/mise-action@v3
25+
- name: Test
26+
run: task test

.gitignore

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Created by https://www.toptal.com/developers/gitignore/api/macos,go
2+
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,go
3+
4+
### Go ###
5+
# If you prefer the allow list template instead of the deny list, see community template:
6+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
7+
#
8+
# Binaries for programs and plugins
9+
*.exe
10+
*.exe~
11+
*.dll
12+
*.so
13+
*.dylib
14+
15+
# Test binary, built with `go test -c`
16+
*.test
17+
18+
# Output of the go coverage tool, specifically when used with LiteIDE
19+
*.out
20+
21+
# Dependency directories (remove the comment below to include it)
22+
# vendor/
23+
24+
# Go workspace file
25+
go.work
26+
27+
### macOS ###
28+
# General
29+
.DS_Store
30+
.AppleDouble
31+
.LSOverride
32+
33+
# Icon must end with two \r
34+
Icon
35+
36+
37+
# Thumbnails
38+
._*
39+
40+
# Files that might appear in the root of a volume
41+
.DocumentRevisions-V100
42+
.fseventsd
43+
.Spotlight-V100
44+
.TemporaryItems
45+
.Trashes
46+
.VolumeIcon.icns
47+
.com.apple.timemachine.donotpresent
48+
49+
# Directories potentially created on remote AFP share
50+
.AppleDB
51+
.AppleDesktop
52+
Network Trash Folder
53+
Temporary Items
54+
.apdisk
55+
56+
### macOS Patch ###
57+
# iCloud generated files
58+
*.icloud
59+
60+
# End of https://www.toptal.com/developers/gitignore/api/macos,go
61+
62+
.env.keys

.mcp.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"mcpServers": {
3+
"serena": {
4+
"command": "uvx",
5+
"args": [
6+
"--from",
7+
"git+https://github.com/oraios/serena.git",
8+
"serena-mcp-server"
9+
]
10+
}
11+
}
12+
}

.serena/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/cache

CLAUDE.md

Whitespace-only changes.

Dockerfile

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Build stage
2+
FROM golang:1.25.6-alpine AS builder
3+
4+
# Install build dependencies
5+
RUN apk add --no-cache git
6+
7+
# Set working directory
8+
WORKDIR /app
9+
10+
# Copy go mod files
11+
COPY go.mod go.sum ./
12+
13+
# Download dependencies
14+
RUN go mod download
15+
16+
# Copy source code
17+
COPY . .
18+
19+
# Build the application
20+
RUN CGO_ENABLED=0 GOOS=linux go build -tags timetzdata -o ./bin/main ./cmd/server/main.go
21+
22+
# Runtime stage
23+
FROM alpine:3
24+
25+
ENV PORT=8080
26+
27+
# Install runtime dependencies
28+
RUN apk add --no-cache ca-certificates
29+
30+
# Create non-root user
31+
RUN addgroup -g 1000 appuser && \
32+
adduser -D -u 1000 -G appuser appuser
33+
34+
# Set working directory
35+
WORKDIR /app
36+
37+
# Copy binary from builder
38+
COPY --from=builder /app/bin/main /app/main
39+
40+
# Copy OpenAPI spec
41+
COPY --from=builder /app/openapi /app/openapi
42+
43+
# Change ownership
44+
RUN chown -R appuser:appuser /app
45+
46+
# Switch to non-root user
47+
USER appuser
48+
49+
# Expose port
50+
EXPOSE $PORT
51+
52+
# Run the application
53+
CMD ["/app/main"]

Taskfile.yaml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
version: "3"
2+
3+
vars:
4+
GO: go
5+
6+
tasks:
7+
install:
8+
desc: Install dependencies
9+
cmds:
10+
- "{{.GO}} mod tidy"
11+
12+
generate:
13+
desc: Generate API code from OpenAPI spec
14+
cmds:
15+
- rm -rf ./generated
16+
- mkdir -p ./generated
17+
- "{{.GO}} tool oapi-codegen -config ./openapi/config.yaml ./openapi/openapi.yaml"
18+
19+
clean:
20+
desc: Clean build artifacts
21+
cmds:
22+
- rm -rf ./bin
23+
24+
build:
25+
desc: Build the application
26+
deps:
27+
- clean
28+
cmds:
29+
- mkdir -p ./bin
30+
- "{{.GO}} build -tags timetzdata -o ./bin/main ./cmd/server/main.go"
31+
32+
run:
33+
desc: Run the application
34+
cmds:
35+
- "{{.GO}} run ./cmd/server/main.go"
36+
37+
build-and-run:
38+
desc: Build and run the application
39+
deps:
40+
- build
41+
cmds:
42+
- ./bin/main
43+
44+
test:
45+
desc: Run tests
46+
cmds:
47+
- "{{.GO}} test -v ./..."
48+
49+
default:
50+
desc: Show available tasks
51+
cmds:
52+
- task --list

0 commit comments

Comments
 (0)