From bb7bcbae666ab313cb80a57eac3e3d34830a2dc0 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 14:20:42 +0300 Subject: [PATCH 01/18] readme update --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c2bec0368b7..4e09be16773 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,5 @@ 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! + +Habtamu's version of Boot.dev's Notely app \ No newline at end of file From 70529048320fdaefa84d0dc079da3699f1ccf77f Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 14:36:52 +0300 Subject: [PATCH 02/18] ci file is added --- .github/workflows/ci.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..664032071d1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,22 @@ +name: ci + +on: + pull_request: + branches: [main] + +jobs: + tests: + name: Tests + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: "1.26.0" + + - name: Force Failure + run: (exit 1) \ No newline at end of file From 98caf281fcdc98eb3cf7b58d07ad52672f3d985b Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 14:43:17 +0300 Subject: [PATCH 03/18] ci file is added --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 664032071d1..2942aa6a3aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,4 +19,4 @@ jobs: go-version: "1.26.0" - name: Force Failure - run: (exit 1) \ No newline at end of file + run: (exit 0) \ No newline at end of file From 636b4484a3af03cca44f0c1685c80b0573750a06 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:11:29 +0300 Subject: [PATCH 04/18] ci file is added --- .github/workflows/ci.yml | 8 ++--- internal/auth/get_api_key_test.go | 52 +++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 internal/auth/get_api_key_test.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2942aa6a3aa..e1c649f71de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,10 @@ jobs: - name: Check out code uses: actions/checkout@v6 - - name: Set up Go - uses: actions/setup-go@v6 - with: - go-version: "1.26.0" + + + - name: Run tests + run: go test ./... - name: Force Failure run: (exit 0) \ No newline at end of file diff --git a/internal/auth/get_api_key_test.go b/internal/auth/get_api_key_test.go new file mode 100644 index 00000000000..4996a9d6d3f --- /dev/null +++ b/internal/auth/get_api_key_test.go @@ -0,0 +1,52 @@ +// unit test for getAPIKey function +package auth + +import ( + "testing" +) + +func TestGetAPIKey(t *testing.T) { + tests := []struct { + name string + headers map[string][]string + want string + wantErr bool + }{ + { + name: "valid API key", + headers: map[string][]string{ + "Authorization": {"ApiKey my-api-key"}, + }, + want: "my-api-key", + wantErr: false, + }, + { + name: "missing Authorization header", + headers: map[string][]string{}, + want: "", + wantErr: true, + }, + { + name: "malformed Authorization header", + headers: map[string][]string{ + "Authorization": {"Bearer my-api-key"}, + }, + want: "", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetAPIKey(tt.headers) + if (err != nil) != tt.wantErr { + t.Errorf("GetAPIKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetAPIKey() = %v, want %v", got, tt.want) + } + }) + } +} + From 73c4d7285bb3738a96891da9e13f559fefab5af4 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:15:25 +0300 Subject: [PATCH 05/18] ci file is added --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1c649f71de..647e0c2a670 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - name: Run tests - run: go test ./... + run: go test ./... -cover - name: Force Failure run: (exit 0) \ No newline at end of file From 017d0e754f3a6b25e5f3f5ed345442d448fa949c Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:19:56 +0300 Subject: [PATCH 06/18] ci file is added --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4e09be16773..7d1928ec39f 100644 --- a/README.md +++ b/README.md @@ -21,5 +21,6 @@ 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! +https://github.com///actions/workflows//badge.svg Habtamu's version of Boot.dev's Notely app \ No newline at end of file From 33f2b38d992f748f5ada2f433b900a9fb4bad9a6 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:23:25 +0300 Subject: [PATCH 07/18] ci file is added --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d1928ec39f..870fde3d82f 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,5 @@ 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! -https://github.com///actions/workflows//badge.svg - +![alt text goes here](https://github.com/habtse/learn-cicd-starter/actions/workflows/ci/badge.svg) Habtamu's version of Boot.dev's Notely app \ No newline at end of file From d3451ecce658d6096f6a5edff0776baf0bd5052b Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:34:19 +0300 Subject: [PATCH 08/18] ci file is added --- .github/workflows/ci.yml | 12 ++++- internal/auth/get_api_key_test.go | 89 +++++++++++++++---------------- 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 647e0c2a670..27804559c8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,4 +19,14 @@ jobs: run: go test ./... -cover - name: Force Failure - run: (exit 0) \ No newline at end of file + run: (exit 0) + style: + name: Style + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v6 + + - name: Run style check + run: go fmt ./... \ No newline at end of file diff --git a/internal/auth/get_api_key_test.go b/internal/auth/get_api_key_test.go index 4996a9d6d3f..c61f15559b7 100644 --- a/internal/auth/get_api_key_test.go +++ b/internal/auth/get_api_key_test.go @@ -2,51 +2,50 @@ package auth import ( - "testing" -) + "testing" +) func TestGetAPIKey(t *testing.T) { - tests := []struct { - name string - headers map[string][]string - want string - wantErr bool - }{ - { - name: "valid API key", - headers: map[string][]string{ - "Authorization": {"ApiKey my-api-key"}, - }, - want: "my-api-key", - wantErr: false, - }, - { - name: "missing Authorization header", - headers: map[string][]string{}, - want: "", - wantErr: true, - }, - { - name: "malformed Authorization header", - headers: map[string][]string{ - "Authorization": {"Bearer my-api-key"}, - }, - want: "", - wantErr: true, - }, - } + tests := []struct { + name string + headers map[string][]string + want string + wantErr bool + }{ + { + name: "valid API key", + headers: map[string][]string{ + "Authorization": {"ApiKey my-api-key"}, + }, + want: "my-api-key", + wantErr: false, + }, + { + name: "missing Authorization header", + headers: map[string][]string{}, + want: "", + wantErr: true, + }, + { + name: "malformed Authorization header", + headers: map[string][]string{ + "Authorization": {"Bearer my-api-key"}, + }, + want: "", + wantErr: true, + }, + } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := GetAPIKey(tt.headers) - if (err != nil) != tt.wantErr { - t.Errorf("GetAPIKey() error = %v, wantErr %v", err, tt.wantErr) - return - } - if got != tt.want { - t.Errorf("GetAPIKey() = %v, want %v", got, tt.want) - } - }) - } -} - + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetAPIKey(tt.headers) + if (err != nil) != tt.wantErr { + t.Errorf("GetAPIKey() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetAPIKey() = %v, want %v", got, tt.want) + } + }) + } +} From edc217d352f89407fbf97e70b4c939b059d64c44 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:38:51 +0300 Subject: [PATCH 09/18] ci file is added --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27804559c8b..dcadef1a671 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,4 +29,4 @@ jobs: uses: actions/checkout@v6 - name: Run style check - run: go fmt ./... \ No newline at end of file + run: test -z "$(go fmt ./...)" \ No newline at end of file From 206137efb7d015f6448752753e42150654ce1cb5 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:46:38 +0300 Subject: [PATCH 10/18] ci file is added --- .github/workflows/ci.yml | 6 +++++- main.go | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcadef1a671..db6c3d2b91e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,4 +29,8 @@ jobs: uses: actions/checkout@v6 - name: Run style check - run: test -z "$(go fmt ./...)" \ No newline at end of file + run: test -z "$(go fmt ./...)" + - name: Install staticcheck + run: go install honnef.co/go/tools/cmd/staticcheck@latest + - name: Run staticcheck + run: staticcheck ./... \ No newline at end of file diff --git a/main.go b/main.go index 19d7366c5f7..f4639b3eea8 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,10 @@ type apiConfig struct { //go:embed static/* var staticFiles embed.FS - +func unused() { + // this function does nothing + // and is called nowhere +} func main() { err := godotenv.Load(".env") if err != nil { @@ -63,7 +66,6 @@ func main() { AllowCredentials: false, MaxAge: 300, })) - router.Get("/", func(w http.ResponseWriter, r *http.Request) { f, err := staticFiles.Open("static/index.html") if err != nil { From 8444a3842b3b792da4342223ebdd159b28ebc8b3 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:48:19 +0300 Subject: [PATCH 11/18] ci file is added --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db6c3d2b91e..e4d257a79c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,10 @@ jobs: uses: actions/checkout@v6 - name: Run style check - run: test -z "$(go fmt ./...)" + run: test -z $(go fmt ./...) + - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@latest + - name: Run staticcheck run: staticcheck ./... \ No newline at end of file From ec943bc2b95996029f0f381c9f7052fd8a85b5be Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:50:58 +0300 Subject: [PATCH 12/18] ci file is added --- main.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index f4639b3eea8..71166b6c081 100644 --- a/main.go +++ b/main.go @@ -23,9 +23,10 @@ type apiConfig struct { //go:embed static/* var staticFiles embed.FS + func unused() { - // this function does nothing - // and is called nowhere + // this function does nothing + // and is called nowhere } func main() { err := godotenv.Load(".env") From f9a52b21d8460a9349862adcf808dac233dfb54e Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:53:01 +0300 Subject: [PATCH 13/18] ci file is added --- main.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/main.go b/main.go index 71166b6c081..24311284c6c 100644 --- a/main.go +++ b/main.go @@ -24,10 +24,6 @@ type apiConfig struct { //go:embed static/* var staticFiles embed.FS -func unused() { - // this function does nothing - // and is called nowhere -} func main() { err := godotenv.Load(".env") if err != nil { From 65494d8c50ac679df4c8e67f05476783532e0da2 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 15:59:02 +0300 Subject: [PATCH 14/18] ci file is added --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4d257a79c4..41b82f57c16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,9 @@ jobs: - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@latest - + + - name: Add Go bin to PATH + run: echo "$HOME/go/bin" >> $GITHUB_PATH + - name: Run staticcheck run: staticcheck ./... \ No newline at end of file From b9aa734f42ace75ca5a8575afd0d9a648daa964a Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 16:04:41 +0300 Subject: [PATCH 15/18] security added --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41b82f57c16..577c966d8f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,16 @@ jobs: - name: Run tests run: go test ./... -cover + - name: Install gosec + run: go install github.com/securego/gosec/v2/cmd/gosec@latest + + - name: Add Go bin to PATH + run: echo "$HOME/go/bin" >> $GITHUB_PATH + + - name: Run gosec + run: gosec ./... + + - name: Force Failure run: (exit 0) style: From 217fd3d88ecb3cb9ff9c9fd1ae941fa49a1bf57e Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 16:13:37 +0300 Subject: [PATCH 16/18] security issue is fixed --- json.go | 5 ++++- main.go | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/json.go b/json.go index 1e6e7985e18..c72d9a4f36f 100644 --- a/json.go +++ b/json.go @@ -30,5 +30,8 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { return } w.WriteHeader(code) - w.Write(dat) + _, err = w.Write(dat) + if err != nil { + log.Printf("Error writing response: %s", err) + } } diff --git a/main.go b/main.go index 24311284c6c..77e7239bb04 100644 --- a/main.go +++ b/main.go @@ -88,10 +88,10 @@ func main() { router.Mount("/v1", v1Router) srv := &http.Server{ - Addr: ":" + port, - Handler: router, + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 10 * time.Second, } - log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) } From ae3a1d50543578b81ad0694eb25e720253842dd6 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 16:18:38 +0300 Subject: [PATCH 17/18] security issue is fixed --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 77e7239bb04..777b454976d 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "log" "net/http" "os" + "time" "github.com/go-chi/chi" "github.com/go-chi/cors" From 186834d004c3810ceecdfe331d4dbba33a7a89d9 Mon Sep 17 00:00:00 2001 From: habtse Date: Mon, 6 Jul 2026 16:46:30 +0300 Subject: [PATCH 18/18] cd file is added --- .github/workflows/cd.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/cd.yml diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 00000000000..5c168ac8ceb --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,21 @@ +name: Deploy + +on: + push: + branches: [main] + +jobs: + Deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "stable" + + - name: Build application + run: ./scripts/buildprod.sh \ No newline at end of file