From 9b72c407c172db245aaa42823849958a19825d67 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Mon, 22 Sep 2025 09:19:14 -0400 Subject: [PATCH 01/29] pushing README.md changes --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c2bec0368b..e1d4866085 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! + + +Tim Treem's version of Boot.Dev's Notely app. From 5cfa3cce664b29219b2f7c355dca6f90a2d47c7f Mon Sep 17 00:00:00 2001 From: tjtreem Date: Mon, 22 Sep 2025 09:44:23 -0400 Subject: [PATCH 02/29] first workflow(.yml) --- .github/workflows/ci.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 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..7a3820f778 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +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) + + From 063aea30e19b64f44ff45dd133c9ab471995546d Mon Sep 17 00:00:00 2001 From: tjtreem Date: Mon, 22 Sep 2025 10:02:29 -0400 Subject: [PATCH 03/29] first workflow(.yml) revised --- .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 7a3820f778..d5594066ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ name: ci on: - pull request: + pull_request: branches: [main] jobs: From ae50899fc71c5b4d3db611e8559b39ccdad80f02 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Mon, 22 Sep 2025 10:08:47 -0400 Subject: [PATCH 04/29] updated and fixed the workflow job error --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5594066ab..a98f531bee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: with: go-version: "1.25.1" - - name: Force Failure - run: (exit 1) + - name: Run Go + run: go version From 28f6e8b76f804fc5ed784c640dcdd64665a67d46 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Tue, 23 Sep 2025 09:52:09 -0400 Subject: [PATCH 05/29] intentional test fail --- .github/workflows/ci.yml | 4 +-- internal/auth/get_api_key_test.go | 59 +++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 internal/auth/get_api_key_test.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a98f531bee..8a66b39bac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: with: go-version: "1.25.1" - - name: Run Go - run: go version + - name: Run tests + run: go test ./... diff --git a/internal/auth/get_api_key_test.go b/internal/auth/get_api_key_test.go new file mode 100644 index 0000000000..bc11b7f23a --- /dev/null +++ b/internal/auth/get_api_key_test.go @@ -0,0 +1,59 @@ +package auth + +import ( + "testing" + "errors" + "net/http" +) + +type testCase struct { + name string + setHeader bool + headerVal string + wantKey string + wantErr error +} + + + +func TestGetAPIKey(t *testing.T) { + cases := []testCase{ + {name: "no header", setHeader: false, wantErr: ErrNoAuthHeaderIncluded}, + {name: "empty header", setHeader: true, headerVal: "", wantErr: ErrNoAuthHeaderIncluded}, + {name: "valid", setHeader: false, headerVal: "ApiKey my-secret", wantKey: "my-secret", wantErr: nil}, + {name: "wrong prefix", setHeader: true, headerVal: "Bearer token", wantErr: errors.New("any")}, + {name: "missing key", setHeader: true, headerVal: "ApiKey", wantErr: errors.New("any")}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + headers := http.Header{} + if tc.setHeader { + headers.Set("Authorization", tc.headerVal) + } + + got, err := GetAPIKey(headers) + + if tc.wantErr != nil { + // For the specific missing-header error: + if errors.Is(tc.wantErr, ErrNoAuthHeaderIncluded) && !errors.Is(err, ErrNoAuthHeaderIncluded) { + t.Fatalf("expected ErrNoAuthHeaderIncluded, got %v", err) + } + // For generic malformed cases just ensure err != nil + if errors.Is(tc.wantErr, ErrNoAuthHeaderIncluded) && err == nil { + t.Fatalf("expected error, got nil") + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got != tc.wantKey { + t.Fatalf("want key %q, got %q", tc.wantKey, got) + } + }) + } +} + + From e7c371a7c1a7b832523ad6ff8b299a990210c371 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Tue, 23 Sep 2025 09:55:46 -0400 Subject: [PATCH 06/29] fix: restore valid test --- internal/auth/get_api_key_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/auth/get_api_key_test.go b/internal/auth/get_api_key_test.go index bc11b7f23a..e4be4c85c7 100644 --- a/internal/auth/get_api_key_test.go +++ b/internal/auth/get_api_key_test.go @@ -20,7 +20,7 @@ func TestGetAPIKey(t *testing.T) { cases := []testCase{ {name: "no header", setHeader: false, wantErr: ErrNoAuthHeaderIncluded}, {name: "empty header", setHeader: true, headerVal: "", wantErr: ErrNoAuthHeaderIncluded}, - {name: "valid", setHeader: false, headerVal: "ApiKey my-secret", wantKey: "my-secret", wantErr: nil}, + {name: "valid", setHeader: true, headerVal: "ApiKey my-secret", wantKey: "my-secret", wantErr: nil}, {name: "wrong prefix", setHeader: true, headerVal: "Bearer token", wantErr: errors.New("any")}, {name: "missing key", setHeader: true, headerVal: "ApiKey", wantErr: errors.New("any")}, } From 3cba3df15cd63217ca8d26903b3a9a37ed84aa46 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Tue, 23 Sep 2025 10:07:01 -0400 Subject: [PATCH 07/29] adding coverage --- .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 8a66b39bac..405e34a2fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,6 @@ jobs: go-version: "1.25.1" - name: Run tests - run: go test ./... + run: go test -cover ./... From a8416b3b5556b824c4cc92509ec78f408ce4c562 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Tue, 23 Sep 2025 10:18:39 -0400 Subject: [PATCH 08/29] added testing badge to README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index e1d4866085..6a819b3713 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # learn-cicd-starter (Notely) +![Testing Badge](https://github.com/tjtreem/learn-cicd-starter/actions/workflows/ci.yml/badge.svg) + This repo contains the starter code for the "Notely" application for the "Learn CICD" course on [Boot.dev](https://boot.dev). ## Local Development @@ -24,3 +26,6 @@ You do *not* need to set up a database or any interactivity on the webpage yet. Tim Treem's version of Boot.Dev's Notely app. + + + From a556ed57976a447248ffa28d289320a3aabbf58e Mon Sep 17 00:00:00 2001 From: tjtreem Date: Wed, 24 Sep 2025 16:30:40 -0400 Subject: [PATCH 09/29] added formatting check to ci workflow --- .github/workflows/ci.yml | 18 +++++++ internal/auth/get_api_key_test.go | 90 +++++++++++++++---------------- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 405e34a2fb..c33ed1618d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,4 +21,22 @@ jobs: - name: Run tests run: go test -cover ./... + 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: Run formatting check + run: test -z $(go fmt ./...) + + + diff --git a/internal/auth/get_api_key_test.go b/internal/auth/get_api_key_test.go index e4be4c85c7..2bd36e6227 100644 --- a/internal/auth/get_api_key_test.go +++ b/internal/auth/get_api_key_test.go @@ -1,59 +1,55 @@ package auth import ( - "testing" "errors" "net/http" + "testing" ) type testCase struct { - name string - setHeader bool - headerVal string - wantKey string - wantErr error + name string + setHeader bool + headerVal string + wantKey string + wantErr error } - - func TestGetAPIKey(t *testing.T) { - cases := []testCase{ - {name: "no header", setHeader: false, wantErr: ErrNoAuthHeaderIncluded}, - {name: "empty header", setHeader: true, headerVal: "", wantErr: ErrNoAuthHeaderIncluded}, - {name: "valid", setHeader: true, headerVal: "ApiKey my-secret", wantKey: "my-secret", wantErr: nil}, - {name: "wrong prefix", setHeader: true, headerVal: "Bearer token", wantErr: errors.New("any")}, - {name: "missing key", setHeader: true, headerVal: "ApiKey", wantErr: errors.New("any")}, - } - - for _, tc := range cases { - t.Run(tc.name, func(t *testing.T) { - headers := http.Header{} - if tc.setHeader { - headers.Set("Authorization", tc.headerVal) - } - - got, err := GetAPIKey(headers) - - if tc.wantErr != nil { - // For the specific missing-header error: - if errors.Is(tc.wantErr, ErrNoAuthHeaderIncluded) && !errors.Is(err, ErrNoAuthHeaderIncluded) { - t.Fatalf("expected ErrNoAuthHeaderIncluded, got %v", err) - } - // For generic malformed cases just ensure err != nil - if errors.Is(tc.wantErr, ErrNoAuthHeaderIncluded) && err == nil { - t.Fatalf("expected error, got nil") - } - return - } - - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if got != tc.wantKey { - t.Fatalf("want key %q, got %q", tc.wantKey, got) - } - }) - } + cases := []testCase{ + {name: "no header", setHeader: false, wantErr: ErrNoAuthHeaderIncluded}, + {name: "empty header", setHeader: true, headerVal: "", wantErr: ErrNoAuthHeaderIncluded}, + {name: "valid", setHeader: true, headerVal: "ApiKey my-secret", wantKey: "my-secret", wantErr: nil}, + {name: "wrong prefix", setHeader: true, headerVal: "Bearer token", wantErr: errors.New("any")}, + {name: "missing key", setHeader: true, headerVal: "ApiKey", wantErr: errors.New("any")}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + headers := http.Header{} + if tc.setHeader { + headers.Set("Authorization", tc.headerVal) + } + + got, err := GetAPIKey(headers) + + if tc.wantErr != nil { + // For the specific missing-header error: + if errors.Is(tc.wantErr, ErrNoAuthHeaderIncluded) && !errors.Is(err, ErrNoAuthHeaderIncluded) { + t.Fatalf("expected ErrNoAuthHeaderIncluded, got %v", err) + } + // For generic malformed cases just ensure err != nil + if errors.Is(tc.wantErr, ErrNoAuthHeaderIncluded) && err == nil { + t.Fatalf("expected error, got nil") + } + return + } + + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got != tc.wantKey { + t.Fatalf("want key %q, got %q", tc.wantKey, got) + } + }) + } } - - From 45202cfde75c586d9624667c93fd6b127d13ea55 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Wed, 24 Sep 2025 16:45:06 -0400 Subject: [PATCH 10/29] added staticcheck to ci workflow --- .github/workflows/ci.yml | 4 ++++ main.go | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c33ed1618d..692995666b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,10 @@ jobs: - name: Run formatting check 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..501683c8c4 100644 --- a/main.go +++ b/main.go @@ -96,3 +96,10 @@ func main() { log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) } + + + +func unused() { + + +} From e0ea29d66a8470d8a7353cc1a4a2cc58ae27fc85 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Wed, 24 Sep 2025 16:46:59 -0400 Subject: [PATCH 11/29] added staticcheck to ci workflow with corrected main.go file --- main.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/main.go b/main.go index 501683c8c4..88920cdc39 100644 --- a/main.go +++ b/main.go @@ -98,8 +98,3 @@ func main() { } - -func unused() { - - -} From 0cbc46d630585c2f4c68816553ea98dde2d51ae3 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Wed, 24 Sep 2025 17:17:27 -0400 Subject: [PATCH 12/29] changes --- main.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main.go b/main.go index 88920cdc39..cd3d2ddf20 100644 --- a/main.go +++ b/main.go @@ -95,6 +95,5 @@ func main() { log.Printf("Serving on port: %s\n", port) log.Fatal(srv.ListenAndServe()) -} - +} From eee1797aba01d62b4bf86f845fc7a49627135c3a Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 11:38:10 -0400 Subject: [PATCH 13/29] added gosec with code failure --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 692995666b..9148154dcd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,12 @@ jobs: - name: Run tests run: go test -cover ./... + + - name: Install gosec + run: go install github.com/securego/gosec/v2/cmd/gosec@latest + + - name: Check gosec + run: gosec ./... style: name: Style From cc92dde8041f5de8b035d6fcab36e204845b670a Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 12:04:29 -0400 Subject: [PATCH 14/29] gosec with updated and corrected code --- json.go | 7 ++++++- main.go | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/json.go b/json.go index 1e6e7985e1..f6e1e9e847 100644 --- a/json.go +++ b/json.go @@ -30,5 +30,10 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { return } w.WriteHeader(code) - w.Write(dat) + + n, err := w.Write(dat) + if err != nil { + log.Prinf("respondWithJSON: write error after %d bytes: %v", n, err) + return + } } diff --git a/main.go b/main.go index cd3d2ddf20..9f6cb00851 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,10 @@ func main() { router.Mount("/v1", v1Router) srv := &http.Server{ - Addr: ":" + port, - Handler: router, + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 1 * time.Second, + } log.Printf("Serving on port: %s\n", port) From cb8100323ad4d3bf806208241a0901dbf22003c6 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 12:07:29 -0400 Subject: [PATCH 15/29] gosec with updated and corrected code --- json.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json.go b/json.go index f6e1e9e847..b68b59ca0a 100644 --- a/json.go +++ b/json.go @@ -33,7 +33,7 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { n, err := w.Write(dat) if err != nil { - log.Prinf("respondWithJSON: write error after %d bytes: %v", n, err) + log.Printf("respondWithJSON: write error after %d bytes: %v", n, err) return } } From 5efceec852cd1b981f0ab7169207cce6766a882a Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 12:29:10 -0400 Subject: [PATCH 16/29] gosec with updated and corrected code --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9148154dcd..781d09a07d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.25.1" + go-version: "stable" - name: Run tests run: go test -cover ./... @@ -38,10 +38,10 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.25.1" + go-version: "stable" - name: Run formatting check - run: test -z $(go fmt ./...) + run: test -z "$(gofmt -l .)" - name: Install staticcheck run: go install honnef.co/go/tools/cmd/staticcheck@latest From 2b210517c5cdcc99b7eb4c91e758bebdea639d68 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 12:32:02 -0400 Subject: [PATCH 17/29] gosec with updated and corrected code --- json.go | 4 +- main.go | 7 ++-- vendor/github.com/go-chi/chi/chi.go | 42 +++++++++---------- vendor/github.com/go-chi/chi/context.go | 14 +++---- vendor/github.com/go-chi/cors/cors.go | 12 +++--- vendor/github.com/google/uuid/dce.go | 4 +- vendor/github.com/google/uuid/hash.go | 4 +- vendor/github.com/google/uuid/node_js.go | 1 + vendor/github.com/google/uuid/node_net.go | 1 + vendor/github.com/google/uuid/null.go | 17 ++++---- vendor/github.com/google/uuid/version4.go | 14 +++---- vendor/nhooyr.io/websocket/accept.go | 1 + vendor/nhooyr.io/websocket/close_notjs.go | 1 + vendor/nhooyr.io/websocket/compress_notjs.go | 1 + vendor/nhooyr.io/websocket/conn_notjs.go | 1 + vendor/nhooyr.io/websocket/dial.go | 1 + vendor/nhooyr.io/websocket/doc.go | 11 ++--- .../websocket/internal/wsjs/wsjs_js.go | 1 + vendor/nhooyr.io/websocket/read.go | 1 + vendor/nhooyr.io/websocket/write.go | 1 + 20 files changed, 74 insertions(+), 65 deletions(-) diff --git a/json.go b/json.go index b68b59ca0a..d2de41b725 100644 --- a/json.go +++ b/json.go @@ -30,10 +30,10 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { return } w.WriteHeader(code) - + n, err := w.Write(dat) if err != nil { log.Printf("respondWithJSON: write error after %d bytes: %v", n, err) - return + return } } diff --git a/main.go b/main.go index 9f6cb00851..286412ffb6 100644 --- a/main.go +++ b/main.go @@ -90,10 +90,9 @@ func main() { router.Mount("/v1", v1Router) srv := &http.Server{ - Addr: ":" + port, - Handler: router, - ReadHeaderTimeout: 1 * time.Second, - + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 1 * time.Second, } log.Printf("Serving on port: %s\n", port) diff --git a/vendor/github.com/go-chi/chi/chi.go b/vendor/github.com/go-chi/chi/chi.go index b7063dc297..db1780b1c6 100644 --- a/vendor/github.com/go-chi/chi/chi.go +++ b/vendor/github.com/go-chi/chi/chi.go @@ -1,29 +1,29 @@ -// // Package chi is a small, idiomatic and composable router for building HTTP services. // // chi requires Go 1.10 or newer. // // Example: -// package main // -// import ( -// "net/http" +// package main +// +// import ( +// "net/http" // -// "github.com/go-chi/chi" -// "github.com/go-chi/chi/middleware" -// ) +// "github.com/go-chi/chi" +// "github.com/go-chi/chi/middleware" +// ) // -// func main() { -// r := chi.NewRouter() -// r.Use(middleware.Logger) -// r.Use(middleware.Recoverer) +// func main() { +// r := chi.NewRouter() +// r.Use(middleware.Logger) +// r.Use(middleware.Recoverer) // -// r.Get("/", func(w http.ResponseWriter, r *http.Request) { -// w.Write([]byte("root.")) -// }) +// r.Get("/", func(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("root.")) +// }) // -// http.ListenAndServe(":3333", r) -// } +// http.ListenAndServe(":3333", r) +// } // // See github.com/go-chi/chi/_examples/ for more in-depth examples. // @@ -47,12 +47,12 @@ // placeholder which will match / characters. // // Examples: -// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" -// "/user/{name}/info" matches "/user/jsmith/info" -// "/page/*" matches "/page/intro/latest" -// "/page/*/index" also matches "/page/intro/latest" -// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" // +// "/user/{name}" matches "/user/jsmith" but not "/user/jsmith/info" or "/user/jsmith/" +// "/user/{name}/info" matches "/user/jsmith/info" +// "/page/*" matches "/page/intro/latest" +// "/page/*/index" also matches "/page/intro/latest" +// "/date/{yyyy:\\d\\d\\d\\d}/{mm:\\d\\d}/{dd:\\d\\d}" matches "/date/2017/04/01" package chi import "net/http" diff --git a/vendor/github.com/go-chi/chi/context.go b/vendor/github.com/go-chi/chi/context.go index 8c97f214a9..bd30d52b4f 100644 --- a/vendor/github.com/go-chi/chi/context.go +++ b/vendor/github.com/go-chi/chi/context.go @@ -112,13 +112,13 @@ func (x *Context) URLParam(key string) string { // // For example, // -// func Instrument(next http.Handler) http.Handler { -// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -// next.ServeHTTP(w, r) -// routePattern := chi.RouteContext(r.Context()).RoutePattern() -// measure(w, r, routePattern) -// }) -// } +// func Instrument(next http.Handler) http.Handler { +// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { +// next.ServeHTTP(w, r) +// routePattern := chi.RouteContext(r.Context()).RoutePattern() +// measure(w, r, routePattern) +// }) +// } func (x *Context) RoutePattern() string { routePattern := strings.Join(x.RoutePatterns, "") return replaceWildcards(routePattern) diff --git a/vendor/github.com/go-chi/cors/cors.go b/vendor/github.com/go-chi/cors/cors.go index 8df81636e3..75196ab91c 100644 --- a/vendor/github.com/go-chi/cors/cors.go +++ b/vendor/github.com/go-chi/cors/cors.go @@ -3,15 +3,15 @@ // // You can configure it by passing an option struct to cors.New: // -// c := cors.New(cors.Options{ -// AllowedOrigins: []string{"foo.com"}, -// AllowedMethods: []string{"GET", "POST", "DELETE"}, -// AllowCredentials: true, -// }) +// c := cors.New(cors.Options{ +// AllowedOrigins: []string{"foo.com"}, +// AllowedMethods: []string{"GET", "POST", "DELETE"}, +// AllowCredentials: true, +// }) // // Then insert the handler in the chain: // -// handler = c.Handler(handler) +// handler = c.Handler(handler) // // See Options documentation for more options. // diff --git a/vendor/github.com/google/uuid/dce.go b/vendor/github.com/google/uuid/dce.go index fa820b9d30..9302a1c1bb 100644 --- a/vendor/github.com/google/uuid/dce.go +++ b/vendor/github.com/google/uuid/dce.go @@ -42,7 +42,7 @@ func NewDCESecurity(domain Domain, id uint32) (UUID, error) { // NewDCEPerson returns a DCE Security (Version 2) UUID in the person // domain with the id returned by os.Getuid. // -// NewDCESecurity(Person, uint32(os.Getuid())) +// NewDCESecurity(Person, uint32(os.Getuid())) func NewDCEPerson() (UUID, error) { return NewDCESecurity(Person, uint32(os.Getuid())) } @@ -50,7 +50,7 @@ func NewDCEPerson() (UUID, error) { // NewDCEGroup returns a DCE Security (Version 2) UUID in the group // domain with the id returned by os.Getgid. // -// NewDCESecurity(Group, uint32(os.Getgid())) +// NewDCESecurity(Group, uint32(os.Getgid())) func NewDCEGroup() (UUID, error) { return NewDCESecurity(Group, uint32(os.Getgid())) } diff --git a/vendor/github.com/google/uuid/hash.go b/vendor/github.com/google/uuid/hash.go index b404f4bec2..24ccde6464 100644 --- a/vendor/github.com/google/uuid/hash.go +++ b/vendor/github.com/google/uuid/hash.go @@ -39,7 +39,7 @@ func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { // NewMD5 returns a new MD5 (Version 3) UUID based on the // supplied name space and data. It is the same as calling: // -// NewHash(md5.New(), space, data, 3) +// NewHash(md5.New(), space, data, 3) func NewMD5(space UUID, data []byte) UUID { return NewHash(md5.New(), space, data, 3) } @@ -47,7 +47,7 @@ func NewMD5(space UUID, data []byte) UUID { // NewSHA1 returns a new SHA1 (Version 5) UUID based on the // supplied name space and data. It is the same as calling: // -// NewHash(sha1.New(), space, data, 5) +// NewHash(sha1.New(), space, data, 5) func NewSHA1(space UUID, data []byte) UUID { return NewHash(sha1.New(), space, data, 5) } diff --git a/vendor/github.com/google/uuid/node_js.go b/vendor/github.com/google/uuid/node_js.go index 24b78edc90..96090351a9 100644 --- a/vendor/github.com/google/uuid/node_js.go +++ b/vendor/github.com/google/uuid/node_js.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build js // +build js package uuid diff --git a/vendor/github.com/google/uuid/node_net.go b/vendor/github.com/google/uuid/node_net.go index 0cbbcddbd6..e91358f7d9 100644 --- a/vendor/github.com/google/uuid/node_net.go +++ b/vendor/github.com/google/uuid/node_net.go @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +//go:build !js // +build !js package uuid diff --git a/vendor/github.com/google/uuid/null.go b/vendor/github.com/google/uuid/null.go index d7fcbf2865..06ecf9de2a 100644 --- a/vendor/github.com/google/uuid/null.go +++ b/vendor/github.com/google/uuid/null.go @@ -17,15 +17,14 @@ var jsonNull = []byte("null") // NullUUID implements the SQL driver.Scanner interface so // it can be used as a scan destination: // -// var u uuid.NullUUID -// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) -// ... -// if u.Valid { -// // use u.UUID -// } else { -// // NULL value -// } -// +// var u uuid.NullUUID +// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&u) +// ... +// if u.Valid { +// // use u.UUID +// } else { +// // NULL value +// } type NullUUID struct { UUID UUID Valid bool // Valid is true if UUID is not NULL diff --git a/vendor/github.com/google/uuid/version4.go b/vendor/github.com/google/uuid/version4.go index 7697802e4d..62ac273815 100644 --- a/vendor/github.com/google/uuid/version4.go +++ b/vendor/github.com/google/uuid/version4.go @@ -9,7 +9,7 @@ import "io" // New creates a new random UUID or panics. New is equivalent to // the expression // -// uuid.Must(uuid.NewRandom()) +// uuid.Must(uuid.NewRandom()) func New() UUID { return Must(NewRandom()) } @@ -17,7 +17,7 @@ func New() UUID { // NewString creates a new random UUID and returns it as a string or panics. // NewString is equivalent to the expression // -// uuid.New().String() +// uuid.New().String() func NewString() string { return Must(NewRandom()).String() } @@ -31,11 +31,11 @@ func NewString() string { // // A note about uniqueness derived from the UUID Wikipedia entry: // -// Randomly generated UUIDs have 122 random bits. One's annual risk of being -// hit by a meteorite is estimated to be one chance in 17 billion, that -// means the probability is about 0.00000000006 (6 × 10−11), -// equivalent to the odds of creating a few tens of trillions of UUIDs in a -// year and having one duplicate. +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. func NewRandom() (UUID, error) { if !poolEnabled { return NewRandomFromReader(rander) diff --git a/vendor/nhooyr.io/websocket/accept.go b/vendor/nhooyr.io/websocket/accept.go index 18536bdb2c..b54471312e 100644 --- a/vendor/nhooyr.io/websocket/accept.go +++ b/vendor/nhooyr.io/websocket/accept.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js package websocket diff --git a/vendor/nhooyr.io/websocket/close_notjs.go b/vendor/nhooyr.io/websocket/close_notjs.go index 4251311d2e..8cafaa9dcf 100644 --- a/vendor/nhooyr.io/websocket/close_notjs.go +++ b/vendor/nhooyr.io/websocket/close_notjs.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js package websocket diff --git a/vendor/nhooyr.io/websocket/compress_notjs.go b/vendor/nhooyr.io/websocket/compress_notjs.go index 809a272c3d..e094513b35 100644 --- a/vendor/nhooyr.io/websocket/compress_notjs.go +++ b/vendor/nhooyr.io/websocket/compress_notjs.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js package websocket diff --git a/vendor/nhooyr.io/websocket/conn_notjs.go b/vendor/nhooyr.io/websocket/conn_notjs.go index 0c85ab7711..3c1361c5b2 100644 --- a/vendor/nhooyr.io/websocket/conn_notjs.go +++ b/vendor/nhooyr.io/websocket/conn_notjs.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js package websocket diff --git a/vendor/nhooyr.io/websocket/dial.go b/vendor/nhooyr.io/websocket/dial.go index 7a7787ff71..7b91713e9c 100644 --- a/vendor/nhooyr.io/websocket/dial.go +++ b/vendor/nhooyr.io/websocket/dial.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js package websocket diff --git a/vendor/nhooyr.io/websocket/doc.go b/vendor/nhooyr.io/websocket/doc.go index efa920e3b6..a2b873c722 100644 --- a/vendor/nhooyr.io/websocket/doc.go +++ b/vendor/nhooyr.io/websocket/doc.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js // Package websocket implements the RFC 6455 WebSocket protocol. @@ -16,7 +17,7 @@ // // More documentation at https://nhooyr.io/websocket. // -// Wasm +// # Wasm // // The client side supports compiling to Wasm. // It wraps the WebSocket browser API. @@ -25,8 +26,8 @@ // // Some important caveats to be aware of: // -// - Accept always errors out -// - Conn.Ping is no-op -// - HTTPClient, HTTPHeader and CompressionMode in DialOptions are no-op -// - *http.Response from Dial is &http.Response{} with a 101 status code on success +// - Accept always errors out +// - Conn.Ping is no-op +// - HTTPClient, HTTPHeader and CompressionMode in DialOptions are no-op +// - *http.Response from Dial is &http.Response{} with a 101 status code on success package websocket // import "nhooyr.io/websocket" diff --git a/vendor/nhooyr.io/websocket/internal/wsjs/wsjs_js.go b/vendor/nhooyr.io/websocket/internal/wsjs/wsjs_js.go index 26ffb45625..88e8f43f69 100644 --- a/vendor/nhooyr.io/websocket/internal/wsjs/wsjs_js.go +++ b/vendor/nhooyr.io/websocket/internal/wsjs/wsjs_js.go @@ -1,3 +1,4 @@ +//go:build js // +build js // Package wsjs implements typed access to the browser javascript WebSocket API. diff --git a/vendor/nhooyr.io/websocket/read.go b/vendor/nhooyr.io/websocket/read.go index ae05cf93ed..0f10afef62 100644 --- a/vendor/nhooyr.io/websocket/read.go +++ b/vendor/nhooyr.io/websocket/read.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js package websocket diff --git a/vendor/nhooyr.io/websocket/write.go b/vendor/nhooyr.io/websocket/write.go index 2210cf817a..9528e5acd7 100644 --- a/vendor/nhooyr.io/websocket/write.go +++ b/vendor/nhooyr.io/websocket/write.go @@ -1,3 +1,4 @@ +//go:build !js // +build !js package websocket From c843ca7d7b9fb0f3210295f40973aa4dd1df4cb9 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 12:59:34 -0400 Subject: [PATCH 18/29] added cd workflow --- .github/workflows/cd.yml | 24 ++++++++++++++++++++++++ Dockerfile | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) 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..b12afd28c7 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,24 @@ +name: cd + +on: + push_request: + branches: [main] + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + + steps: + - name: Checck out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: "stable" + + - name: Build notely app + run: ./scripts/buildprod.sh + + diff --git a/Dockerfile b/Dockerfile index 2be3d18b81..bb611a09b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 debian:stable-slim +FROM debian:stable-slim RUN apt-get update && apt-get install -y ca-certificates From a908d1e2c618466da95bd89e6782ce1570f8543e Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 13:02:36 -0400 Subject: [PATCH 19/29] added cd workflow --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b12afd28c7..0f369a2380 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,7 +1,7 @@ name: cd on: - push_request: + push: branches: [main] jobs: From 2b9ff04be3cd2d70f847b041de27792fc89fa80e Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 13:08:38 -0400 Subject: [PATCH 20/29] added cd workflow --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 0f369a2380..72de7cbf81 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -19,6 +19,6 @@ jobs: go-version: "stable" - name: Build notely app - run: ./scripts/buildprod.sh + run: scripts/buildprod.sh From d639665b7eca07d93bc77c05babd23224dde3d69 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 13:14:08 -0400 Subject: [PATCH 21/29] added cd workflow --- .github/workflows/cd.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 72de7cbf81..91b5bda3df 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checck out code + - name: Check out code uses: actions/checkout@v4 - name: Set up Go @@ -18,7 +18,10 @@ jobs: with: go-version: "stable" + - name: Make build script executable + run: chmod +x scripts/buildprod.sh + - name: Build notely app - run: scripts/buildprod.sh + run: ./scripts/buildprod.sh From 9e51d4d6dc8e9a7d9323db679588c2eefdd9e026 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Thu, 25 Sep 2025 13:16:27 -0400 Subject: [PATCH 22/29] added cd workflow --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 91b5bda3df..2dc933007d 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -5,7 +5,7 @@ on: branches: [main] jobs: - deploy: + Deploy: name: Deploy runs-on: ubuntu-latest From 53517f4e2aba4ee6ea60efd821c7222d066f2bfc Mon Sep 17 00:00:00 2001 From: tjtreem Date: Fri, 26 Sep 2025 11:22:29 -0400 Subject: [PATCH 23/29] modified cd.yml to include Google cloud build --- .github/workflows/cd.yml | 22 +++++++++++++++++++++- .github/workflows/ci.yml | 2 ++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2dc933007d..b0e4ef1f4f 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -5,7 +5,7 @@ on: branches: [main] jobs: - Deploy: + deploy: name: Deploy runs-on: ubuntu-latest @@ -24,4 +24,24 @@ jobs: - name: Build notely app run: ./scripts/buildprod.sh + - id: 'auth' + uses: 'google-github-actions/auth@v2' + with: + credentials_json: '${{ secrets.GCP_CREDENTIALS }}' + + - name: 'Set up Cloud SDK' + uses: 'google-github-actions/setup-gcloud@v3' + + - name: 'Use gcloud CLI' + run: 'gcloud info' + + - name: Set IMAGE_TAG + run: | + SHORT_SHA="$(echo ${{github.sha}} | head -c 8)" + echo "IMAGE_TAG=$SHORT_SHA" >> "$GITHUB_ENV" + + - name: Build Docker image and push to Artifact Registry + run: | + gcloud builds submit --tag us-central1-docker.pkg.dev/notely-473217/notely-ar-repo/notely:$IMAGE_TAG . + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 781d09a07d..2eb0578a96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,8 @@ name: ci on: pull_request: branches: [main] + push: + branches: [addtests] jobs: tests: From a9c06f176d06e6e353f3051acf780398a52a03f7 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Sat, 27 Sep 2025 12:47:09 -0400 Subject: [PATCH 24/29] modified cd workflow for Cloud build and deployment --- static/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/index.html b/static/index.html index 72be101028..5d4ad73c09 100644 --- a/static/index.html +++ b/static/index.html @@ -7,7 +7,7 @@ -

Notely

+

Welcome to Notely

From c74580da6a2a75cd1f0f2eebc9bd284752d42e79 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Sat, 27 Sep 2025 13:35:19 -0400 Subject: [PATCH 25/29] added migrations to cd workflow --- scripts/migrateup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/migrateup.sh b/scripts/migrateup.sh index fe69c6fc7a..5de640af7a 100755 --- a/scripts/migrateup.sh +++ b/scripts/migrateup.sh @@ -5,4 +5,4 @@ if [ -f .env ]; then fi cd sql/schema -goose turso $DATABASE_URL up +goose turso "$DATABASE_URL" up From d8d50d64cdd48a6767a8f08c378b846ec71d5542 Mon Sep 17 00:00:00 2001 From: tjtreem Date: Sat, 27 Sep 2025 13:42:55 -0400 Subject: [PATCH 26/29] new push --- .github/workflows/cd.yml | 49 ++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b0e4ef1f4f..2ac5c3fecc 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -9,20 +9,16 @@ jobs: name: Deploy runs-on: ubuntu-latest + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + steps: - name: Check out code uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: "stable" - - - name: Make build script executable - run: chmod +x scripts/buildprod.sh - - - name: Build notely app - run: ./scripts/buildprod.sh + - name: Install goose + run: | + curl -fsSL https://raw.githubusercontent.com/pressly/goose/master/install.sh | sh - id: 'auth' uses: 'google-github-actions/auth@v2' @@ -32,16 +28,41 @@ jobs: - name: 'Set up Cloud SDK' uses: 'google-github-actions/setup-gcloud@v3' - - name: 'Use gcloud CLI' - run: 'gcloud info' + - name: Configure gcloud defaults + run: | + gcloud config set project notely-473217 + gcloud config set run/region us-central1 - name: Set IMAGE_TAG run: | SHORT_SHA="$(echo ${{github.sha}} | head -c 8)" echo "IMAGE_TAG=$SHORT_SHA" >> "$GITHUB_ENV" - - name: Build Docker image and push to Artifact Registry + - name: Build and push image (Cloud Build) run: | - gcloud builds submit --tag us-central1-docker.pkg.dev/notely-473217/notely-ar-repo/notely:$IMAGE_TAG . + gcloud builds submit \ + --tag us-central1-docker.pkg.dev/notely-473217/notely-ar-repo/notely:${IMAGE_TAG} . + + - name: Run DB migrations + run: | + export PATH="$PWD/bin:$PATH" + chmod +x scripts/migrateup.sh + ./scripts/migrateup.sh + + - name: Deploy to Cloud Run + run: | + gcloud run deploy notely \ + --image us-central1-docker.pkg.dev/notely-473217/notely-ar-repo/notely:${IMAGE_TAG} \ + --allow-unauthenticated \ + --max-instances=4 + + # Optional: ensure unauthenticated access if first deploy + - name: Ensure public invoker (optional) + continue-on-error: true + run: | + gcloud run services add-iam-policy-binding notely \ + --member="allUsers" \ + --role="roles/run.invoker" + From df3b73556bc8240f8624a5c9b664f787a70ad29d Mon Sep 17 00:00:00 2001 From: tjtreem Date: Sat, 27 Sep 2025 13:54:16 -0400 Subject: [PATCH 27/29] fixes to cloud build --- Dockerfile | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index bb611a09b4..3b2dfa3c7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,15 @@ -FROM debian:stable-slim - -RUN apt-get update && apt-get install -y ca-certificates +# Build stage +FROM golang:1.22 AS build +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o notely . -ADD notely /usr/bin/notely -CMD ["notely"] +# Run stage +FROM debian:stable-slim +RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* +COPY --from=build /app/notely /usr/bin/notely +EXPOSE 8080 +CMD ["/usr/bin/notely"] From e9d497bedde9f3321ea74d8416de8be53266f30f Mon Sep 17 00:00:00 2001 From: tjtreem Date: Sat, 27 Sep 2025 14:07:49 -0400 Subject: [PATCH 28/29] debug changes --- .github/workflows/cd.yml | 4 ++++ scripts/migrateup.sh | 9 +++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2ac5c3fecc..36253ca3b8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -43,6 +43,10 @@ jobs: gcloud builds submit \ --tag us-central1-docker.pkg.dev/notely-473217/notely-ar-repo/notely:${IMAGE_TAG} . + - name: Debug DB URL presence + run: | + [ -n "$DATABASE_URL" ] && echo "DATABASE_URL is set" || echo "DATABASE_URL is MISSING" + - name: Run DB migrations run: | export PATH="$PWD/bin:$PATH" diff --git a/scripts/migrateup.sh b/scripts/migrateup.sh index 5de640af7a..0f073a2c2f 100755 --- a/scripts/migrateup.sh +++ b/scripts/migrateup.sh @@ -1,8 +1,5 @@ #!/bin/bash - -if [ -f .env ]; then - source .env -fi - -cd sql/schema +set -euo pipefail +cd "$(dirname "$0")/../sql/schema" +echo "Running: goose turso up" goose turso "$DATABASE_URL" up From 109b6f3de02903609582257e2aab76c76cad3f6d Mon Sep 17 00:00:00 2001 From: tjtreem Date: Sun, 28 Sep 2025 14:08:20 -0400 Subject: [PATCH 29/29] debugging --- handler_user.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/handler_user.go b/handler_user.go index d53d4316fb..8a2d8f2fc8 100644 --- a/handler_user.go +++ b/handler_user.go @@ -7,6 +7,7 @@ import ( "encoding/json" "net/http" "time" + "log" "github.com/bootdotdev/learn-cicd-starter/internal/database" "github.com/google/uuid" @@ -37,7 +38,9 @@ func (cfg *apiConfig) handlerUsersCreate(w http.ResponseWriter, r *http.Request) Name: params.Name, ApiKey: apiKey, }) + if err != nil { + log.Printf("create user failed: %v", err) respondWithError(w, http.StatusInternalServerError, "Couldn't create user", err) return }