Skip to content

Commit 8731adb

Browse files
committed
Initial release
0 parents  commit 8731adb

24 files changed

Lines changed: 4432 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches: [main, dev]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Set up Go
15+
uses: actions/setup-go@v5
16+
with:
17+
go-version-file: go.mod
18+
19+
- name: Verify dependencies
20+
run: go mod verify
21+
22+
- name: Build
23+
run: go build ./cmd/growcast/...
24+
25+
- name: Vet
26+
run: go vet ./...

.github/workflows/release.yml

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches: [main, dev]
6+
7+
jobs:
8+
release:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: write
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
18+
- name: Set up Go
19+
uses: actions/setup-go@v5
20+
with:
21+
go-version-file: go.mod
22+
23+
- name: Read version
24+
id: version
25+
run: |
26+
if [ ! -f VERSION ]; then
27+
echo "ERROR: VERSION file not found"
28+
exit 1
29+
fi
30+
VERSION=$(cat VERSION | tr -d '[:space:]')
31+
if [ -z "$VERSION" ]; then
32+
echo "ERROR: VERSION file is empty"
33+
exit 1
34+
fi
35+
if [ "${{ github.ref_name }}" != "main" ]; then
36+
VERSION="dev"
37+
fi
38+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
39+
echo "short_sha=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
40+
41+
- name: Build binary
42+
run: |
43+
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
44+
-ldflags="-w -s -X main.version=${{ steps.version.outputs.version }}" \
45+
-o growcast-linux-amd64 \
46+
./cmd/growcast/...
47+
48+
- name: Log in to Docker Hub
49+
uses: docker/login-action@v3
50+
with:
51+
username: ${{ secrets.DOCKER_USERNAME }}
52+
password: ${{ secrets.DOCKER_PASSWORD }}
53+
54+
- name: Set up Docker Buildx
55+
uses: docker/setup-buildx-action@v3
56+
57+
# --- main branch: versioned + latest tags ---
58+
59+
- name: Build and push CPU image (main)
60+
if: github.ref_name == 'main'
61+
uses: docker/build-push-action@v6
62+
with:
63+
context: .
64+
file: Dockerfile
65+
push: true
66+
tags: |
67+
dwot/growcast:${{ steps.version.outputs.version }}
68+
dwot/growcast:latest
69+
cache-from: type=gha
70+
cache-to: type=gha,mode=max
71+
72+
- name: Build and push GPU image (main)
73+
if: github.ref_name == 'main'
74+
uses: docker/build-push-action@v6
75+
with:
76+
context: .
77+
file: Dockerfile.gpu
78+
push: true
79+
tags: |
80+
dwot/growcast:${{ steps.version.outputs.version }}-gpu
81+
dwot/growcast:gpu-latest
82+
cache-from: type=gha
83+
cache-to: type=gha,mode=max
84+
85+
- name: Generate release notes
86+
if: github.ref_name == 'main'
87+
id: release_notes
88+
run: |
89+
VERSION=${{ steps.version.outputs.version }}
90+
PREV_TAG=$(git tag --sort=-version:refname | grep -v "^v${VERSION}$" | head -n1)
91+
if [ -z "$PREV_TAG" ]; then
92+
RANGE="HEAD"
93+
else
94+
RANGE="${PREV_TAG}..HEAD"
95+
fi
96+
NOTES=$(git log $RANGE --pretty=format:"- %s (%h)" --no-merges)
97+
if [ -z "$NOTES" ]; then
98+
NOTES="- Initial release"
99+
fi
100+
{
101+
echo "notes<<EOF"
102+
echo "## What's Changed"
103+
echo ""
104+
echo "$NOTES"
105+
echo "EOF"
106+
} >> "$GITHUB_OUTPUT"
107+
108+
- name: Create git tag
109+
if: github.ref_name == 'main'
110+
run: |
111+
VERSION=${{ steps.version.outputs.version }}
112+
if git rev-parse "v${VERSION}" >/dev/null 2>&1; then
113+
echo "Tag v${VERSION} already exists, skipping"
114+
else
115+
git config user.name "github-actions[bot]"
116+
git config user.email "github-actions[bot]@users.noreply.github.com"
117+
git tag "v${VERSION}"
118+
git push origin "v${VERSION}"
119+
fi
120+
121+
- name: Create GitHub Release
122+
if: github.ref_name == 'main'
123+
uses: softprops/action-gh-release@v2
124+
with:
125+
tag_name: v${{ steps.version.outputs.version }}
126+
name: v${{ steps.version.outputs.version }}
127+
body: ${{ steps.release_notes.outputs.notes }}
128+
files: growcast-linux-amd64
129+
130+
# --- dev branch: dev + sha tags, no release ---
131+
132+
- name: Build and push CPU image (dev)
133+
if: github.ref_name != 'main'
134+
uses: docker/build-push-action@v6
135+
with:
136+
context: .
137+
file: Dockerfile
138+
push: true
139+
tags: |
140+
dwot/growcast:dev
141+
dwot/growcast:dev-${{ steps.version.outputs.short_sha }}
142+
cache-from: type=gha
143+
cache-to: type=gha,mode=max
144+
145+
- name: Build and push GPU image (dev)
146+
if: github.ref_name != 'main'
147+
uses: docker/build-push-action@v6
148+
with:
149+
context: .
150+
file: Dockerfile.gpu
151+
push: true
152+
tags: |
153+
dwot/growcast:dev-gpu
154+
dwot/growcast:dev-${{ steps.version.outputs.short_sha }}-gpu
155+
cache-from: type=gha
156+
cache-to: type=gha,mode=max

.gitignore

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.so.*
7+
*.dylib
8+
9+
# Test binaries, built with `go test -c`
10+
*.test
11+
12+
# Output of the go coverage tool
13+
*.out
14+
15+
# Go workspace file
16+
go.work
17+
18+
# Dependency directories
19+
vendor/
20+
21+
# IDE
22+
.idea/
23+
.vscode/
24+
*.swp
25+
*.swo
26+
*~
27+
28+
# OS
29+
.DS_Store
30+
Thumbs.db
31+
32+
# Application
33+
config.yaml
34+
stats.txt
35+
logs/
36+
37+
# Docker
38+
.docker/
39+
40+
# Local environment
41+
.env
42+
.env.local
43+
/CLAUDE.md
44+
/fail_log.txt
45+
/teststats.txt
46+
/debug.txt
47+
/growcast-output.txt

.gitlab-ci.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
stages:
2+
- build
3+
4+
.kaniko_auth: &kaniko_auth
5+
before_script:
6+
- mkdir -p /kaniko/.docker
7+
- |
8+
echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n "$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" | base64 | tr -d '\n')\"}}}" \
9+
> /kaniko/.docker/config.json
10+
11+
build-cpu:
12+
<<: *kaniko_auth
13+
stage: build
14+
image:
15+
name: gcr.io/kaniko-project/executor:v1.23.0-debug
16+
entrypoint: [""]
17+
rules:
18+
- if: $CI_COMMIT_BRANCH == "main"
19+
- if: $CI_COMMIT_TAG
20+
script:
21+
- /kaniko/executor
22+
--context "$CI_PROJECT_DIR"
23+
--dockerfile "$CI_PROJECT_DIR/Dockerfile"
24+
--custom-platform linux/amd64
25+
--cache=true
26+
--cache-repo "$CI_REGISTRY_IMAGE/cpu-cache"
27+
--destination "$CI_REGISTRY_IMAGE:latest"
28+
--destination "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
29+
30+
build-gpu:
31+
<<: *kaniko_auth
32+
stage: build
33+
image:
34+
name: gcr.io/kaniko-project/executor:v1.23.0-debug
35+
entrypoint: [""]
36+
rules:
37+
- if: $CI_COMMIT_BRANCH == "main"
38+
- if: $CI_COMMIT_TAG
39+
script:
40+
- /kaniko/executor
41+
--context "$CI_PROJECT_DIR"
42+
--dockerfile "$CI_PROJECT_DIR/Dockerfile.gpu"
43+
--custom-platform linux/amd64
44+
--cache=true
45+
--cache-repo "$CI_REGISTRY_IMAGE/gpu-cache"
46+
--destination "$CI_REGISTRY_IMAGE:gpu-latest"
47+
--destination "$CI_REGISTRY_IMAGE:gpu-$CI_COMMIT_SHORT_SHA"

Dockerfile

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Multi-stage build for minimal image size
2+
FROM golang:1.24-alpine AS builder
3+
4+
# Install build dependencies
5+
RUN apk add --no-cache git
6+
7+
WORKDIR /app
8+
9+
# Copy go mod files
10+
COPY go.mod go.sum ./
11+
12+
# Download dependencies
13+
RUN go mod download
14+
15+
# Copy source code
16+
COPY . .
17+
18+
# Build the application
19+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \
20+
-ldflags="-w -s" \
21+
-o /app/growcast ./cmd/growcast/main.go
22+
23+
# Final stage
24+
FROM alpine:3.21
25+
26+
# Install runtime dependencies (FFmpeg is essential)
27+
RUN apk add --no-cache ffmpeg ca-certificates
28+
29+
# Create non-root user
30+
RUN addgroup -g 1000 growcast && \
31+
adduser -D -u 1000 -G growcast growcast
32+
33+
WORKDIR /app
34+
35+
# Copy binary and bundled fonts from builder
36+
COPY --from=builder /app/growcast .
37+
COPY fonts/ ./fonts/
38+
39+
# Create config and data directories
40+
RUN mkdir -p /etc/growcast /var/growcast && \
41+
chown -R growcast:growcast /etc/growcast /var/growcast
42+
43+
# Switch to non-root user
44+
USER growcast
45+
46+
# Health check
47+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
48+
CMD wget --no-verbose --tries=1 -O /dev/null http://localhost:8080/health || exit 1
49+
50+
# Expose API port
51+
EXPOSE 8080
52+
53+
# Set environment variables
54+
ENV GROWCAST_LOG_LEVEL=info
55+
56+
# Volume for configuration
57+
VOLUME ["/etc/growcast", "/var/growcast"]
58+
59+
# Run the application
60+
CMD ["./growcast", "-config", "/etc/growcast/config.yaml"]

0 commit comments

Comments
 (0)