From c979088e0b2406a346bdf05e357846d01b6fefb3 Mon Sep 17 00:00:00 2001 From: fnkit Date: Tue, 17 Feb 2026 16:45:12 +0000 Subject: [PATCH] initial commit - go function --- .forgejo/workflows/deploy.yml | 83 +++++++++++++++++++++++++++++++++++ .gitignore | 15 +++++++ Dockerfile | 13 ++++++ cmd/main.go | 29 ++++++++++++ docker-compose.yml | 36 +++++++++++++++ function.go | 17 +++++++ go.mod | 5 +++ 7 files changed, 198 insertions(+) create mode 100644 .forgejo/workflows/deploy.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 cmd/main.go create mode 100644 docker-compose.yml create mode 100644 function.go create mode 100644 go.mod diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..e0a31d3 --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,83 @@ +# FnKit Deploy โ€” Forgejo Actions +# Builds and deploys this function container on every push to main +# Requires: Forgejo runner with Docker socket access (fnkit deploy runner) +# +# Pipeline: git push โ†’ build image โ†’ deploy container โ†’ health check + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Build image + run: | + FUNCTION_NAME="fn-go" + IMAGE_NAME="fnkit-fn-${FUNCTION_NAME}:latest" + IMAGE_PREV="fnkit-fn-${FUNCTION_NAME}:prev" + + echo "๐Ÿ”จ Building ${FUNCTION_NAME}..." + docker build -t $IMAGE_NAME . + + echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV + echo "IMAGE_PREV=$IMAGE_PREV" >> $GITHUB_ENV + echo "FUNCTION_NAME=$FUNCTION_NAME" >> $GITHUB_ENV + + - name: Deploy container + run: | + echo "๐ŸŒ Ensuring fnkit-network exists..." + docker network create fnkit-network 2>/dev/null || true + + # Tag current image as :prev for rollback + docker tag $IMAGE_NAME $IMAGE_PREV 2>/dev/null || true + + echo "โ™ป๏ธ Replacing running container..." + docker stop $FUNCTION_NAME 2>/dev/null || true + docker rm $FUNCTION_NAME 2>/dev/null || true + + echo "๐Ÿš€ Starting ${FUNCTION_NAME}..." + docker run -d \ + --name $FUNCTION_NAME \ + --network fnkit-network \ + --label fnkit.fn=true \ + --label fnkit.deployed="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --restart unless-stopped \ + $IMAGE_NAME + + - name: Health check + run: | + echo "๐Ÿฅ Checking container health..." + sleep 3 + + if docker ps --filter "name=$FUNCTION_NAME" --filter "status=running" -q | grep -q .; then + echo "โœ… ${FUNCTION_NAME} is running" + echo "๐ŸŒ Available at gateway: /${FUNCTION_NAME}" + else + echo "โŒ Container failed to start โ€” rolling back..." + docker logs $FUNCTION_NAME 2>&1 || true + + # Rollback to previous image if available + if docker image inspect $IMAGE_PREV >/dev/null 2>&1; then + echo "๐Ÿ”„ Rolling back to previous image..." + docker rm $FUNCTION_NAME 2>/dev/null || true + docker run -d \ + --name $FUNCTION_NAME \ + --network fnkit-network \ + --label fnkit.fn=true \ + --label fnkit.deployed="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + --label fnkit.rollback=true \ + --restart unless-stopped \ + $IMAGE_PREV + echo "โš ๏ธ Rolled back to previous version" + fi + exit 1 + fi + + - name: Cleanup old images + run: | + echo "๐Ÿงน Cleaning up dangling images..." + docker image prune -f --filter "label=fnkit.fn=true" 2>/dev/null || true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..484b440 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Docker +Dockerfile.local +# Go +vendor/ +*.exe diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1439627 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:1.21 AS builder +LABEL fnkit.fn="true" +WORKDIR /app +COPY go.* ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o server ./cmd/main.go + +FROM gcr.io/distroless/static-debian11 +COPY --from=builder /app/server /server +ENV FUNCTION_TARGET=HelloWorld +EXPOSE 8080 +CMD ["/server"] diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..ab19203 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "log" + "os" + + // Blank-import the function package so the init() runs + _ "fn-go" + "github.com/GoogleCloudPlatform/functions-framework-go/funcframework" +) + +func main() { + // Use PORT environment variable, or default to 8080. + port := "8080" + if envPort := os.Getenv("PORT"); envPort != "" { + port = envPort + } + + // By default, listen on all interfaces. If testing locally, run with + // LOCAL_ONLY=true to avoid triggering firewall warnings and + // exposing the server outside of your own machine. + hostname := "" + if localOnly := os.Getenv("LOCAL_ONLY"); localOnly == "true" { + hostname = "127.0.0.1" + } + if err := funcframework.StartHostPort(hostname, port); err != nil { + log.Fatalf("funcframework.StartHostPort: %v\n", err) + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6768e42 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,36 @@ +# Docker Compose for FnKit function with gateway integration +# Requires: docker network create fnkit-network +# Requires: fnkit-gateway running (fnkit gateway init && fnkit gateway build && fnkit gateway start) + +version: '3.8' + +services: + fn-go: + build: . + container_name: fn-go + networks: + - fnkit-network + depends_on: + fnkit-gateway: + condition: service_started + restart: unless-stopped + + # Uncomment to include gateway in this compose file + # fnkit-gateway: + # image: fnkit-gateway:latest + # container_name: fnkit-gateway + # ports: + # - "8080:8080" + # environment: + # - FNKIT_AUTH_TOKEN=${FNKIT_AUTH_TOKEN:-} + # networks: + # - fnkit-network + +networks: + fnkit-network: + name: fnkit-network + external: true + +# Usage: +# docker-compose up -d +# curl -H "Authorization: Bearer " http://localhost:8080/fn-go diff --git a/function.go b/function.go new file mode 100644 index 0000000..17e2f6f --- /dev/null +++ b/function.go @@ -0,0 +1,17 @@ +package function + +import ( + "fmt" + "net/http" + + "github.com/GoogleCloudPlatform/functions-framework-go/functions" +) + +func init() { + functions.HTTP("HelloWorld", helloWorld) +} + +// helloWorld writes "Hello, World!" to the HTTP response. +func helloWorld(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello, World!") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6ea1333 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module fn-go + +go 1.21 + +require github.com/GoogleCloudPlatform/functions-framework-go v1.8.0