diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..8880d2a364 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: ci + +on: + push: + branches: ["**"] + 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-file: go.mod + cache: true + + - name: Run 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 ./... + + - name: Run unit tests with coverage + 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 "$(go env GOPATH)/bin" >> "$GITHUB_PATH" + + - name: Run gosec (intentionally fails for first push) + run: gosec ./... diff --git a/.gitignore b/.gitignore index 2092f54e78..b62d782e5a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ out .env learn-cicd-starter notely +gosec-report.json diff --git a/README.md b/README.md index c2bec0368b..52b2a9dd5e 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,7 @@ 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! + +Joseline Kahunde's version of Boot.dev's Notely app. + + diff --git a/json.go b/json.go index 1e6e7985e1..77aa881133 100644 --- a/json.go +++ b/json.go @@ -13,9 +13,11 @@ func respondWithError(w http.ResponseWriter, code int, msg string, logErr error) if code > 499 { log.Printf("Responding with 5XX error: %s", msg) } + type errorResponse struct { Error string `json:"error"` } + respondWithJSON(w, code, errorResponse{ Error: msg, }) @@ -23,12 +25,16 @@ func respondWithError(w http.ResponseWriter, code int, msg string, logErr error) func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) { w.Header().Set("Content-Type", "application/json") + dat, err := json.Marshal(payload) if err != nil { - log.Printf("Error marshalling JSON: %s", err) - w.WriteHeader(500) + log.Printf("Error marshalling JSON: %v", err) + w.WriteHeader(http.StatusInternalServerError) return } + w.WriteHeader(code) - w.Write(dat) + if _, err := w.Write(dat); err != nil { + log.Printf("warning: response write failed: %v", err) + } } diff --git a/main.go b/main.go index 19d7366c5f..721626e34d 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" @@ -88,11 +89,19 @@ func main() { v1Router.Get("/healthz", handlerReadiness) router.Mount("/v1", v1Router) + // after: router.Mount("/v1", v1Router) + srv := &http.Server{ - Addr: ":" + port, - Handler: router, + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 10 * time.Second, // fixes G112 + ReadTimeout: 15 * time.Second, + WriteTimeout: 15 * time.Second, + IdleTimeout: 60 * time.Second, } log.Printf("Serving on port: %s\n", port) - log.Fatal(srv.ListenAndServe()) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("server failed: %v", err) + } }