Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d7c23d6
Update README.md
KaliBytes Oct 28, 2025
bc49f07
Merge pull request #1 from KaliBytes/KaliBytes-patch-1
KaliBytes Oct 28, 2025
35c01ed
README.md
KaliBytes Oct 28, 2025
b6309c9
Update README.md
KaliBytes Oct 29, 2025
046240c
Add Style job to check Go formatting in CI
Oct 29, 2025
709b8bd
Merge pull request #4 from KaliBytes/style-check
KaliBytes Oct 29, 2025
9a7f902
Fix duplicate name in CI workflow
Oct 29, 2025
6babcf8
Add CI workflow for linting with go fmt and staticcheck
Oct 29, 2025
a6d58d5
style: gofmt
Oct 29, 2025
80ebb6c
fix(ci): correct YAML and run staticcheck
Oct 29, 2025
90f2613
Update main.go
KaliBytes Oct 30, 2025
4678f21
Add gosec to CI workflow
KaliBytes Oct 30, 2025
08acd1e
Add CD workflow
KaliBytes Oct 31, 2025
9fe1a30
Fix CI workflow to run on pull requests
KaliBytes Oct 31, 2025
926db01
CD: build in module mode (no vendor)
KaliBytes Oct 31, 2025
28b1097
Create build-and-push.yml
KaliBytes Nov 3, 2025
befcb93
Update build-and-push.yml
KaliBytes Nov 3, 2025
203e542
Update build-and-push.yml
KaliBytes Nov 3, 2025
cf17c51
Update build-and-push.yml
KaliBytes Nov 3, 2025
fc9267e
Update build-and-push.yml
KaliBytes Nov 3, 2025
c0d7bd8
Build Go binary inside image (multi-stage)
KaliBytes Nov 4, 2025
fe23a01
Update build-and-push.yml
KaliBytes Nov 4, 2025
df68011
Update build-and-push.yml
KaliBytes Nov 4, 2025
7cb11e1
Update cd.yml
KaliBytes Nov 4, 2025
397f60a
Update cd.yml
KaliBytes Nov 4, 2025
454fa64
Update cd.yml
KaliBytes Nov 4, 2025
da0389a
Update cd.yml
KaliBytes Nov 4, 2025
32eda45
Update main.go
KaliBytes Nov 4, 2025
985abc8
Update build-and-push.yml
KaliBytes Nov 5, 2025
72b3fa8
Update cd.yml
KaliBytes Nov 5, 2025
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
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.git
.github
README.md
.gitignore
**/.DS_Store
bin
dist

42 changes: 42 additions & 0 deletions .github/workflows/build-and-push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Build & Push Notely image

on:
push:
branches: [ main ]

env:
PROJECT_ID: disco-glass-477110-u7
REGION: us-central1
REPOSITORY: notely-ar-repo
IMAGE: notely
GAR_HOST: us-central1-docker.pkg.dev
IMAGE_URI: us-central1-docker.pkg.dev/disco-glass-477110-u7/notely-ar-repo/notely

jobs:
build-and-push:
runs-on: ubuntu-latest

permissions:
contents: read
id-token: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}

- name: Configure Docker to use Artifact Registry
run: gcloud auth configure-docker $GAR_HOST --quiet

- name: Build image
run: |
docker build -t $IMAGE_URI:latest -t $IMAGE_URI:${{ github.sha }} .

- name: Push image (latest and sha)
run: |
docker push $IMAGE_URI:latest
docker push $IMAGE_URI:${{ github.sha }}
44 changes: 44 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: CD

on:
push:
branches: [ main ]

env:
PROJECT_ID: disco-glass-477110-u7
REGION: us-central1
REPOSITORY: notely-ar-repo
IMAGE: notely
GAR_HOST: us-central1-docker.pkg.dev
IMAGE_URI: us-central1-docker.pkg.dev/disco-glass-477110-u7/notely-ar-repo/notely

jobs:
Deploy:
runs-on: ubuntu-latest

permissions:
contents: read
id-token: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_CREDENTIALS }}

- name: Set gcloud project/region
run: |
gcloud config set project $PROJECT_ID
gcloud config set run/region $REGION

- name: Deploy to Cloud Run
run: |
gcloud run deploy notely \
--image $IMAGE_URI:latest \
--region $REGION \
--platform managed \
--allow-unauthenticated \
--quiet
24 changes: 24 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: CI

on:
pull_request:
branches: [ main ]

jobs:
style:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Run go fmt check
run: |
go fmt ./...
go vet ./...

24 changes: 20 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
FROM --platform=linux/amd64 debian:stable-slim
# syntax=docker/dockerfile:1

RUN apt-get update && apt-get install -y ca-certificates
############# Build stage #############
FROM golang:1.22-alpine AS builder
WORKDIR /app

ADD notely /usr/bin/notely
# Cache dependencies
COPY go.mod go.sum ./
RUN go mod download

# Copy source files
COPY . .

# Build a static Linux binary
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/notely .

############# Runtime stage #############
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/notely /usr/local/bin/notely

USER nonroot:nonroot
ENTRYPOINT ["/usr/local/bin/notely"]

CMD ["notely"]
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
![Tests](https://github.com/KaliBytes/learn-cicd-starter/actions/workflows/ci.yml/badge.svg)

# learn-cicd-starter (Notely)

This repo contains the starter code for the "Notely" application for the "Learn CICD" course on [Boot.dev](https://boot.dev).
Expand All @@ -21,3 +23,4 @@ go build -o notely && ./notely
*This starts the server in non-database mode.* It will serve a simple webpage at `http://localhost:8080`.

You do *not* need to set up a database or any interactivity on the webpage yet. Instructions for that will come later in the course!

42 changes: 22 additions & 20 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"log"
"net/http"
"os"
"time"

"github.com/go-chi/chi"
"github.com/go-chi/chi/v5"
"github.com/go-chi/cors"
"github.com/joho/godotenv"

Expand All @@ -25,8 +26,7 @@ type apiConfig struct {
var staticFiles embed.FS

func main() {
err := godotenv.Load(".env")
if err != nil {
if err := godotenv.Load(".env"); err != nil {
log.Printf("warning: assuming default configuration. .env unreadable: %v", err)
}

Expand All @@ -37,8 +37,7 @@ func main() {

apiCfg := apiConfig{}

// https://github.com/libsql/libsql-client-go/#open-a-connection-to-sqld
// libsql://[your-database].turso.io?authToken=[your-auth-token]
// Optional DB hookup (runs without CRUD if DATABASE_URL is missing)
dbURL := os.Getenv("DATABASE_URL")
if dbURL == "" {
log.Println("DATABASE_URL environment variable is not set")
Expand All @@ -48,13 +47,11 @@ func main() {
if err != nil {
log.Fatal(err)
}
dbQueries := database.New(db)
apiCfg.DB = dbQueries
apiCfg.DB = database.New(db)
log.Println("Connected to database!")
}

router := chi.NewRouter()

router.Use(cors.Handler(cors.Options{
AllowedOrigins: []string{"https://*", "http://*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
Expand All @@ -64,6 +61,7 @@ func main() {
MaxAge: 300,
}))

// Root serves embedded static file
router.Get("/", func(w http.ResponseWriter, r *http.Request) {
f, err := staticFiles.Open("static/index.html")
if err != nil {
Expand All @@ -76,23 +74,27 @@ func main() {
}
})

v1Router := chi.NewRouter()

// v1 API routes
v1 := chi.NewRouter()
if apiCfg.DB != nil {
v1Router.Post("/users", apiCfg.handlerUsersCreate)
v1Router.Get("/users", apiCfg.middlewareAuth(apiCfg.handlerUsersGet))
v1Router.Get("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesGet))
v1Router.Post("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesCreate))
v1.Post("/users", apiCfg.handlerUsersCreate)
v1.Get("/users", apiCfg.middlewareAuth(apiCfg.handlerUsersGet))
v1.Get("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesGet))
v1.Post("/notes", apiCfg.middlewareAuth(apiCfg.handlerNotesCreate))
}
v1.Get("/healthz", handlerReadiness)
router.Mount("/v1", v1)

v1Router.Get("/healthz", handlerReadiness)

router.Mount("/v1", v1Router)
// HTTP server configured for Cloud Run
srv := &http.Server{
Addr: ":" + port,
Handler: router,
Addr: ":" + port,
Handler: router,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}

log.Printf("Serving on port: %s\n", port)
log.Printf("Serving on port %s ...", port)
log.Fatal(srv.ListenAndServe())
}
42 changes: 21 additions & 21 deletions vendor/github.com/go-chi/chi/chi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions vendor/github.com/go-chi/chi/context.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions vendor/github.com/go-chi/cors/cors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions vendor/github.com/google/uuid/dce.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions vendor/github.com/google/uuid/hash.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading