diff --git a/.env b/.env
new file mode 100644
index 0000000000..b0cca29a37
--- /dev/null
+++ b/.env
@@ -0,0 +1,4 @@
+
+PORT="8080"
+
+
diff --git a/.github/workflows/apprunner.yaml b/.github/workflows/apprunner.yaml
new file mode 100644
index 0000000000..ca752f68f4
--- /dev/null
+++ b/.github/workflows/apprunner.yaml
@@ -0,0 +1,10 @@
+version: 1.0
+runtime: go
+runtime-version: 1.22
+build:
+ commands:
+ - go build -mod=vendor -o server .
+run:
+ command: ./server
+ network:
+ port: 8080
\ No newline at end of file
diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 0000000000..aed137c9e5
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,59 @@
+# This workflow name is for display in GitHub's UI.
+name: Dynamic CD for ECS
+
+# This specifies when the workflow should run.
+# We'll trigger on pushes to the main branch and on pull requests.
+# This helps with dynamic tagging and allows for checks on PRs.
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+# A simple set of global environment variables for easy configuration.
+env:
+ AWS_REGION: us-east-1 # The AWS region where your resources are located.
+ ECR_REPOSITORY: notely-repo # The name of your ECR repository.
+ ECS_CLUSTER: notely-cluster # The name of your ECS cluster.
+ ECS_SERVICE: notely-service # The name of your ECS service.
+ ECS_TASK_DEFINITION: notely-deployment.json # The path to your task definition file.
+ CONTAINER_NAME: notely-container # The name of the container in your task definition.
+
+jobs:
+ deploy:
+ name: Deploy to Amazon ECS
+ # This ensures the job only runs on push events, not pull requests.
+ if: github.event_name == 'push'
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write # This is required for requesting the JWT for OIDC
+ contents: read
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ # Use a GitHub secret for your IAM role ARN to keep it secure.
+ role-to-assume: ${{ secrets.AWS_IAM_ROLE_ARN }}
+ aws-region: ${{ env.AWS_REGION }}
+
+ - name: Login to Amazon ECR
+ id: login-ecr
+ uses: aws-actions/amazon-ecr-login@v2
+
+ - name: Build and push Docker image
+ id: build-image
+ env:
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
+ # The image tag is now a combination of the repository and the git commit SHA.
+ # This makes every image tag unique and traceable.
+ IMAGE_TAG: ${{ github.sha }}
+ run: |
+ # The `run` command is a multi-line script.
+ # It builds the image and tags it with the dynamic ECR registry URL and the git SHA.
+ # It then pushes the image to ECR.
+ docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
+ docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
+
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000..85b5b3402f
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,58 @@
+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.23.0"
+
+ - name: Run All Tests
+ run: go test -cover ./...
+
+ - name: Install gosec
+ run: go install github.com/securego/gosec/v2/cmd/gosec@latest
+
+ - name: Gosec check
+ 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.23.0"
+
+ - name: Check formatting
+ run: |
+ go_fmt_result=$(find . -path ./vendor -prune -o -name '*.go' -print0 | xargs -0 gofmt -l)
+ if [[ -n "$go_fmt_result" ]]; then
+ echo "The following files are not formatted correctly:"
+ echo "$go_fmt_result"
+ exit 1
+ fi
+
+ - name: Install staticcheck
+ run: go install honnef.co/go/tools/cmd/staticcheck@latest
+
+ - name: Run Staticcheck
+ run: staticcheck ./...
+
+
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 2092f54e78..0000000000
--- a/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-out
-.env
-learn-cicd-starter
-notely
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000000..26d33521af
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000000..105ce2da2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/learn-cicd-starter .iml b/.idea/learn-cicd-starter .iml
new file mode 100644
index 0000000000..2c80e12694
--- /dev/null
+++ b/.idea/learn-cicd-starter .iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000000..6bcf0f6d9f
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000000..3c278e964b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000000..94a25f7f4c
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 2be3d18b81..41ec99ecc5 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,30 @@
-FROM --platform=linux/amd64 debian:stable-slim
+# Use a specific Go version (1.22) as the build environment.
+FROM golang:1.22-alpine AS builder
-RUN apt-get update && apt-get install -y ca-certificates
+# Set the working directory
+WORKDIR /app
-ADD notely /usr/bin/notely
+# Copy go.mod and go.sum and download dependencies
+COPY go.mod go.sum ./
+RUN go mod download
-CMD ["notely"]
+# Copy the source code
+COPY . .
+
+# Build the application binary
+RUN CGO_ENABLED=0 go build -o server .
+
+# Final stage: create a small, efficient image to run the binary
+FROM alpine:latest
+
+# Set the working directory
+WORKDIR /app
+
+# Copy the compiled binary from the builder stage
+COPY --from=builder /app/server ./server
+
+# Expose the port your application listens on
+EXPOSE 8080
+
+# Command to run the application
+CMD ["./server"]
\ No newline at end of file
diff --git a/README.md b/README.md
index c2bec0368b..6443b54d33 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-# learn-cicd-starter (Notely)
+
+
+go ver# 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).
@@ -21,3 +23,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!
+
+Sam James's version of Boot.dev's Notely app
\ No newline at end of file
diff --git a/go1.21.2.darwin-amd64.pkg b/go1.21.2.darwin-amd64.pkg
new file mode 100644
index 0000000000..8fe652e325
--- /dev/null
+++ b/go1.21.2.darwin-amd64.pkg
@@ -0,0 +1,2 @@
+Moved Permanently.
+
diff --git a/internal/auth/auth.go b/internal/auth/auth.go
index f969aacf63..e4ac3cabb4 100644
--- a/internal/auth/auth.go
+++ b/internal/auth/auth.go
@@ -14,7 +14,7 @@ func GetAPIKey(headers http.Header) (string, error) {
if authHeader == "" {
return "", ErrNoAuthHeaderIncluded
}
- splitAuth := strings.Split(authHeader, " ")
+ splitAuth := strings.Split(authHeader, " ") // Fix the code
if len(splitAuth) < 2 || splitAuth[0] != "ApiKey" {
return "", errors.New("malformed authorization header")
}
diff --git a/internal/auth/unittests.go b/internal/auth/unittests.go
new file mode 100644
index 0000000000..a084ee414f
--- /dev/null
+++ b/internal/auth/unittests.go
@@ -0,0 +1,39 @@
+package auth
+
+import (
+ "errors"
+ "net/http"
+ "testing"
+)
+
+func TestGetAPIKey_Success(t *testing.T) {
+ headers := http.Header{}
+ expectedKey := "secret-key-123"
+ headers.Set("Authorization", "ApiKey "+expectedKey)
+
+ key, err := GetAPIKey(headers)
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ if key != expectedKey {
+ t.Errorf("expected key %q, got %q", expectedKey, key)
+ }
+}
+
+func TestGetAPIKey_MissingHeader(t *testing.T) {
+ headers := http.Header{}
+
+ key, err := GetAPIKey(headers)
+ if err == nil {
+ t.Fatal("expected error, got nil")
+ }
+
+ if key != "" {
+ t.Errorf("expected empty key, got %q", key)
+ }
+
+ if !errors.Is(err, ErrNoAuthHeaderIncluded) {
+ t.Errorf("expected error %v, got %v", ErrNoAuthHeaderIncluded, err)
+ }
+}
diff --git a/json.go b/json.go
index 1e6e7985e1..7fd6ae29ec 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: %v", err)
+ }
}
diff --git a/main.go b/main.go
index 19d7366c5f..15ee531c5d 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: 5 * time.Second,
}
log.Printf("Serving on port: %s\n", port)
diff --git a/notely b/notely
new file mode 100755
index 0000000000..50963bea3c
Binary files /dev/null and b/notely differ