diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000000..ee5d8deed4 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,36 @@ +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: Build 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@v2 + + - name: Use gcloud CLI + run: gcloud info + + - name: Build and push Docker image + run: gcloud builds submit --tag us-central1-docker.pkg.dev/notely-472417/notely-ar-repo/notely:latest . \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..8b4625406c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +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: Check tests cover + 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 + + 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 ./...)" + + - name: Install staticchek + run: go install honnef.co/go/tools/cmd/staticcheck@latest + + - name: Check Lint + run: staticcheck ./... + diff --git a/README.md b/README.md index c2bec0368b..8c896241cb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # 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). - +![test badge image](https://github.com/chumaachike/learn-cicd-starter/actions/workflows/ci.yml/badge.svg) ## Local Development Make sure you're on Go version 1.22+. @@ -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! + + + Chuma's version of Bootdev's Notely app \ No newline at end of file diff --git a/coverage.out b/coverage.out new file mode 100644 index 0000000000..79c2a489b7 --- /dev/null +++ b/coverage.out @@ -0,0 +1,78 @@ +mode: set +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:12.99,14.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:14.16,17.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:19.2,20.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:20.16,23.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:25.2,25.46 1 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:28.102,35.16 5 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:35.16,38.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:40.2,48.16 3 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:48.16,51.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:53.2,54.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:54.16,57.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:59.2,60.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:60.16,63.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_notes.go:65.2,65.50 1 0 +github.com/bootdotdev/learn-cicd-starter/handler_ready.go:5.63,7.2 1 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:15.82,22.16 5 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:22.16,25.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:27.2,28.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:28.16,31.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:33.2,40.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:40.16,43.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:45.2,46.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:46.16,49.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:51.2,52.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:52.16,55.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:56.2,56.50 1 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:59.49,62.16 3 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:62.16,64.3 1 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:65.2,67.24 3 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:70.99,73.16 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:73.16,76.3 2 0 +github.com/bootdotdev/learn-cicd-starter/handler_user.go:78.2,78.45 1 0 +github.com/bootdotdev/learn-cicd-starter/json.go:9.82,10.19 1 0 +github.com/bootdotdev/learn-cicd-starter/json.go:10.19,12.3 1 0 +github.com/bootdotdev/learn-cicd-starter/json.go:13.2,13.16 1 0 +github.com/bootdotdev/learn-cicd-starter/json.go:13.16,15.3 1 0 +github.com/bootdotdev/learn-cicd-starter/json.go:16.2,21.4 2 0 +github.com/bootdotdev/learn-cicd-starter/json.go:24.76,27.16 3 0 +github.com/bootdotdev/learn-cicd-starter/json.go:27.16,31.3 3 0 +github.com/bootdotdev/learn-cicd-starter/json.go:32.2,33.14 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:27.13,29.16 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:29.16,31.3 1 0 +github.com/bootdotdev/learn-cicd-starter/main.go:33.2,34.16 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:34.16,36.3 1 0 +github.com/bootdotdev/learn-cicd-starter/main.go:38.2,43.17 3 0 +github.com/bootdotdev/learn-cicd-starter/main.go:43.17,46.3 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:46.8,48.17 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:48.17,50.4 1 0 +github.com/bootdotdev/learn-cicd-starter/main.go:51.3,53.40 3 0 +github.com/bootdotdev/learn-cicd-starter/main.go:56.2,67.63 3 0 +github.com/bootdotdev/learn-cicd-starter/main.go:67.63,69.17 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:69.17,72.4 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:73.3,74.42 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:74.42,76.4 1 0 +github.com/bootdotdev/learn-cicd-starter/main.go:79.2,81.22 2 0 +github.com/bootdotdev/learn-cicd-starter/main.go:81.22,86.3 4 0 +github.com/bootdotdev/learn-cicd-starter/main.go:88.2,97.33 5 0 +github.com/bootdotdev/learn-cicd-starter/middleware_auth.go:12.78,13.54 1 0 +github.com/bootdotdev/learn-cicd-starter/middleware_auth.go:13.54,15.17 2 0 +github.com/bootdotdev/learn-cicd-starter/middleware_auth.go:15.17,18.4 2 0 +github.com/bootdotdev/learn-cicd-starter/middleware_auth.go:20.3,21.17 2 0 +github.com/bootdotdev/learn-cicd-starter/middleware_auth.go:21.17,24.4 2 0 +github.com/bootdotdev/learn-cicd-starter/middleware_auth.go:26.3,26.22 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:17.59,19.16 2 0 +github.com/bootdotdev/learn-cicd-starter/models.go:19.16,21.3 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:23.2,24.16 2 0 +github.com/bootdotdev/learn-cicd-starter/models.go:24.16,26.3 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:27.2,33.8 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:44.59,46.16 2 0 +github.com/bootdotdev/learn-cicd-starter/models.go:46.16,48.3 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:50.2,51.16 2 0 +github.com/bootdotdev/learn-cicd-starter/models.go:51.16,53.3 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:54.2,60.8 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:63.66,65.29 2 0 +github.com/bootdotdev/learn-cicd-starter/models.go:65.29,68.17 3 0 +github.com/bootdotdev/learn-cicd-starter/models.go:68.17,70.4 1 0 +github.com/bootdotdev/learn-cicd-starter/models.go:73.2,73.20 1 0 diff --git a/internal/auth/auth.go b/internal/auth/auth.go index f969aacf63..acfc30431c 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -7,6 +7,7 @@ import ( ) var ErrNoAuthHeaderIncluded = errors.New("no authorization header included") +var ErrMalformedAuthHeader = errors.New("malformed authorization header") // GetAPIKey - func GetAPIKey(headers http.Header) (string, error) { @@ -16,7 +17,7 @@ func GetAPIKey(headers http.Header) (string, error) { } splitAuth := strings.Split(authHeader, " ") if len(splitAuth) < 2 || splitAuth[0] != "ApiKey" { - return "", errors.New("malformed authorization header") + return "", ErrMalformedAuthHeader } return splitAuth[1], nil diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go new file mode 100644 index 0000000000..eec33f582e --- /dev/null +++ b/internal/auth/auth_test.go @@ -0,0 +1,30 @@ +package auth + +import ( + "errors" + "net/http" + "testing" +) + +func TestGetAPIKey(t *testing.T) { + tests := map[string]struct { + input http.Header + wantKey string + wantErr error + }{ + "valid header": {input: http.Header{"Authorization": []string{"ApiKey ChumaAchike"}}, wantKey: "ChumaAchike", wantErr: nil}, + "no header": {input: nil, wantErr: ErrNoAuthHeaderIncluded, wantKey: ""}, + "malformed header": {input: http.Header{"Authorization": []string{"NoAPIkey hapum"}}, wantErr: ErrMalformedAuthHeader, wantKey: ""}, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + key, err := GetAPIKey(tc.input) + if key != tc.wantKey { + t.Errorf("expected key %q, got %q", tc.wantKey, key) + } + if !errors.Is(err, tc.wantErr) { + t.Errorf("expected error %v, got %v in %s", tc.wantErr, err, name) + } + }) + } +} diff --git a/json.go b/json.go index 1e6e7985e1..5a68f6586b 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 { + respondWithError(w, http.StatusInternalServerError, "unable to write data to json", err) + } } diff --git a/main.go b/main.go index 19d7366c5f..d7b17ba7c5 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,12 @@ func main() { router.Mount("/v1", v1Router) srv := &http.Server{ - Addr: ":" + port, - Handler: router, + Addr: ":" + port, + Handler: router, + ReadHeaderTimeout: 5 * time.Second, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 120 * time.Second, } log.Printf("Serving on port: %s\n", port)