From 66812659563e9bed1f6b83b50e1b76f22fc28ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 11:50:40 +0300 Subject: [PATCH 01/19] add --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c2bec0368b..53971dcbc5 100644 --- a/README.md +++ b/README.md @@ -21,3 +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! + + +hakancoruhh's version of Boot.dev's Notely app. \ No newline at end of file From 7f5216526ea0a73ca71175e9704d7036770867dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 11:57:35 +0300 Subject: [PATCH 02/19] ci: update --- .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 0000000000..a54d8248d5 --- /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@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25.1" + + - name: Force Failure + run: (exit 1) \ No newline at end of file From 68cfb284f90b475dfbe96acf606c3a7d49b61196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 12:00:12 +0300 Subject: [PATCH 03/19] ci: update --- .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 a54d8248d5..da0999db14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,4 +19,4 @@ jobs: go-version: "1.25.1" - name: Force Failure - run: (exit 1) \ No newline at end of file + run: go version \ No newline at end of file From 483d444db9b4e6d16d897fbeea1aa0c1822fc217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 12:06:03 +0300 Subject: [PATCH 04/19] test: update --- internal/auth/auth_test.go | 130 +++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 internal/auth/auth_test.go diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go new file mode 100644 index 0000000000..cbe8126211 --- /dev/null +++ b/internal/auth/auth_test.go @@ -0,0 +1,130 @@ +package auth_test + +import ( + "errors" + "net/http" + "testing" + + "github.com/bootdotdev/learn-cicd-starter/internal/auth" +) + +func TestGetAPIKey(t *testing.T) { + tests := []struct { + name string + headers map[string]string + expectedKey string + expectedError error + }{ + { + name: "valid API key", + headers: map[string]string{"Authorization": "ApiKey valid-api-key-123"}, + expectedKey: "valid-api-key-123", + expectedError: nil, + }, + { + name: "valid API key with special characters", + headers: map[string]string{"Authorization": "ApiKey abc123-def456_ghi789"}, + expectedKey: "abc123-def456_ghi789", + expectedError: nil, + }, + { + name: "missing authorization header", + headers: map[string]string{}, + expectedKey: "", + expectedError: auth.ErrNoAuthHeaderIncluded, + }, + { + name: "empty authorization header", + headers: map[string]string{"Authorization": ""}, + expectedKey: "", + expectedError: auth.ErrNoAuthHeaderIncluded, + }, + { + name: "malformed authorization header - no space", + headers: map[string]string{"Authorization": "ApiKey"}, + expectedKey: "", + expectedError: errors.New("malformed authorization header"), + }, + { + name: "malformed authorization header - wrong prefix", + headers: map[string]string{"Authorization": "Bearer token123"}, + expectedKey: "", + expectedError: errors.New("malformed authorization header"), + }, + { + name: "malformed authorization header - lowercase apikey", + headers: map[string]string{"Authorization": "apikey token123"}, + expectedKey: "", + expectedError: errors.New("malformed authorization header"), + }, + { + name: "malformed authorization header - only space", + headers: map[string]string{"Authorization": " "}, + expectedKey: "", + expectedError: errors.New("malformed authorization header"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Create HTTP headers from the test case + headers := make(http.Header) + for key, value := range tt.headers { + headers.Set(key, value) + } + + // Call the function + key, err := auth.GetAPIKey(headers) + + // Check the returned key + if key != tt.expectedKey { + t.Errorf("GetAPIKey() key = %v, want %v", key, tt.expectedKey) + } + + // Check the returned error + if tt.expectedError == nil { + if err != nil { + t.Errorf("GetAPIKey() error = %v, want nil", err) + } + } else { + if err == nil { + t.Errorf("GetAPIKey() error = nil, want %v", tt.expectedError) + } else if err.Error() != tt.expectedError.Error() { + t.Errorf("GetAPIKey() error = %v, want %v", err, tt.expectedError) + } + } + }) + } +} + +func TestGetAPIKey_EdgeCases(t *testing.T) { + t.Run("case insensitive header name", func(t *testing.T) { + headers := make(http.Header) + headers.Set("authorization", "ApiKey test-key") // lowercase header name + + key, err := auth.GetAPIKey(headers) + + if err != nil { + t.Errorf("GetAPIKey() error = %v, want nil", err) + } + if key != "test-key" { + t.Errorf("GetAPIKey() key = %v, want %v", key, "test-key") + } + }) + + t.Run("multiple authorization headers", func(t *testing.T) { + headers := make(http.Header) + headers.Add("Authorization", "Bearer token1") + headers.Add("Authorization", "ApiKey test-key") + + key, err := auth.GetAPIKey(headers) + + // Should get the first one (Bearer), which should be malformed + if err == nil { + t.Errorf("GetAPIKey() error = nil, want malformed authorization header error") + } + if key != "" { + t.Errorf("GetAPIKey() key = %v, want empty string", key) + } + }) +} From 2677a477d3b562487842918574a2780cd06d03fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 12:07:20 +0300 Subject: [PATCH 05/19] update --- .github/workflows/ci.yml | 2 +- internal/auth/auth.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index da0999db14..7e70d74b70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,4 +19,4 @@ jobs: go-version: "1.25.1" - name: Force Failure - run: go version \ No newline at end of file + run: go test ./... \ No newline at end of file diff --git a/internal/auth/auth.go b/internal/auth/auth.go index f969aacf63..39fd65774b 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -10,6 +10,7 @@ var ErrNoAuthHeaderIncluded = errors.New("no authorization header included") // GetAPIKey - func GetAPIKey(headers http.Header) (string, error) { + return "", errors.New("test error") authHeader := headers.Get("Authorization") if authHeader == "" { return "", ErrNoAuthHeaderIncluded From 5bff5d4b005fbdfb51c40e802c434194a0b704ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 12:08:22 +0300 Subject: [PATCH 06/19] test: update --- .github/workflows/ci.yml | 2 +- internal/auth/auth.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e70d74b70..025f164898 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,5 +18,5 @@ jobs: with: go-version: "1.25.1" - - name: Force Failure + - name: Run Tests run: go test ./... \ No newline at end of file diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 39fd65774b..f969aacf63 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -10,7 +10,6 @@ var ErrNoAuthHeaderIncluded = errors.New("no authorization header included") // GetAPIKey - func GetAPIKey(headers http.Header) (string, error) { - return "", errors.New("test error") authHeader := headers.Get("Authorization") if authHeader == "" { return "", ErrNoAuthHeaderIncluded From cc96158d1d6c1893bbf04f723b4596704f1f3b45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 18:35:33 +0300 Subject: [PATCH 07/19] ci: update --- .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 025f164898..7284b6c5c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,4 +19,4 @@ jobs: go-version: "1.25.1" - name: Run Tests - run: go test ./... \ No newline at end of file + run: go test -cover ./... \ No newline at end of file From 0bb1bb2b5584d74120b87f2c2d7ef5a455ef2fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 18:38:38 +0300 Subject: [PATCH 08/19] ci : update --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 53971dcbc5..13ab9980a3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ # learn-cicd-starter (Notely) +[![CI](https://github.com/hakancoruhh/learn-cicd-starter/actions/workflows/ci.yml/badge.svg)](https://github.com/hakancoruhh/learn-cicd-starter/actions/workflows/ci.yml) + +![alt text goes here](IMAGE_URL) + + + + + This repo contains the starter code for the "Notely" application for the "Learn CICD" course on [Boot.dev](https://boot.dev). ## Local Development From 9469573e2e9c83b45aed92a6575f24f27dde4f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 18:40:25 +0300 Subject: [PATCH 09/19] add tests badge --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 13ab9980a3..49968d6f5c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # learn-cicd-starter (Notely) -[![CI](https://github.com/hakancoruhh/learn-cicd-starter/actions/workflows/ci.yml/badge.svg)](https://github.com/hakancoruhh/learn-cicd-starter/actions/workflows/ci.yml) -![alt text goes here](IMAGE_URL) + +![code coverage badge](https://github.com/hakancoruhh/learn-cicd-starter/actions/workflows/ci.yml/badge.svg) From dcf4b1d1c9b1d849f33f2e56c3d92372d019fdf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:36:39 +0300 Subject: [PATCH 10/19] ci: add style check to CI workflow --- .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 7284b6c5c5..37833c9c2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,4 +19,7 @@ jobs: go-version: "1.25.1" - name: Run Tests - run: go test -cover ./... \ No newline at end of file + run: go test -cover ./... + + - name: Style Check + run: test -z $(go fmt ./...) \ No newline at end of file From 6255a986b1b822d7f3e35e37a0d47f04e9b2079a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:39:15 +0300 Subject: [PATCH 11/19] ci: add newline at end of style check command --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37833c9c2b..6786500131 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,4 +22,5 @@ jobs: run: go test -cover ./... - name: Style Check - run: test -z $(go fmt ./...) \ No newline at end of file + run: test -z $(go fmt ./...) + \ No newline at end of file From 8c8edc33ac87d379a97ab32308d46bff60ae4b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:40:55 +0300 Subject: [PATCH 12/19] ci: rename style check job to 'Style' --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6786500131..2f5b9788e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,5 @@ jobs: - name: Run Tests run: go test -cover ./... - - name: Style Check + - name: Style run: test -z $(go fmt ./...) - \ No newline at end of file From ac029d78b0585dd6388068fea63e716d590ba41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:42:27 +0300 Subject: [PATCH 13/19] ci: enhance style check job with setup steps and Go version specification --- .github/workflows/ci.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f5b9788e3..95581ef327 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,5 +21,18 @@ jobs: - name: Run Tests run: go test -cover ./... - - name: Style + style: + name: Style + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25.1" + + - name: Check formatting run: test -z $(go fmt ./...) From c56f3c98729c1c5f373534172806f5444b6058ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:45:16 +0300 Subject: [PATCH 14/19] ci: add staticcheck to CI workflow for improved code analysis --- .github/workflows/ci.yml | 8 ++++++++ main.go | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95581ef327..a086fff818 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,3 +36,11 @@ jobs: - name: Check formatting run: test -z $(go fmt ./...) + + - name: Install staticcheck + run: go install honnef.co/go/tools/cmd/staticcheck@latest + + - name: Run staticcheck + run: staticcheck ./... + + diff --git a/main.go b/main.go index 19d7366c5f..a621713c2b 100644 --- a/main.go +++ b/main.go @@ -96,3 +96,8 @@ func main() { log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) } + +func unused() { + // this function does nothing + // and is called nowhere +} From 48b1d024682c7a5e61c46c5b163f3266199901ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:46:16 +0300 Subject: [PATCH 15/19] refactor: remove unused function from main.go --- main.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/main.go b/main.go index a621713c2b..19d7366c5f 100644 --- a/main.go +++ b/main.go @@ -96,8 +96,3 @@ func main() { log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) } - -func unused() { - // this function does nothing - // and is called nowhere -} From 6ca4722b330a553774892dbe461becd93f6c1a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:50:36 +0300 Subject: [PATCH 16/19] ci: add gosec for security analysis in CI workflow --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a086fff818..14e1732f45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,12 @@ jobs: - name: Run Tests run: go test -cover ./... + - name: Install gosec + run: go install github.com/securego/gosec/v2/cmd/gosec@latest + + - name: Run gosec + run: gosec ./... + style: name: Style runs-on: ubuntu-latest From f2e7d4eb19078b55d4ed42f09c81c19bcabd106e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 19:53:12 +0300 Subject: [PATCH 17/19] fix: handle error when writing JSON response and set ReadHeaderTimeout for HTTP server --- json.go | 4 +++- main.go | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/json.go b/json.go index 1e6e7985e1..0f8075d808 100644 --- a/json.go +++ b/json.go @@ -30,5 +30,7 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { return } w.WriteHeader(code) - w.Write(dat) + if _, err := w.Write(dat); err != nil { + log.Printf("Error writing response: %s", err) + } } diff --git a/main.go b/main.go index 19d7366c5f..b3a4ade65d 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" @@ -89,8 +90,9 @@ func main() { router.Mount("/v1", v1Router) srv := &http.Server{ - Addr: ":" + port, - Handler: router, + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 30 * time.Second, } log.Printf("Serving on port: %s\n", port) From 1a03773571c2e561592cf1cdc1626c9c603d898b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 13 Sep 2025 20:00:54 +0300 Subject: [PATCH 18/19] ci: add continuous deployment workflow for Go application --- .github/workflows/cd.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 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 0000000000..88b1a8091b --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,23 @@ +name: cd + +on: + push: + branches: [main] + + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "1.25.1" + + - name: run script + run: scripts/buildprod.sh From 69dbfeff1e659a4c98f84be7c0e282d5d86eadf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hakan=20=C3=87oruh?= Date: Sat, 20 Sep 2025 09:31:31 +0300 Subject: [PATCH 19/19] ci: add additional newlines for better readability in deployment workflow --- .github/workflows/cd.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 88b1a8091b..72fd0cb99f 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -21,3 +21,5 @@ jobs: - name: run script run: scripts/buildprod.sh + +