diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..a651da77b --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,61 @@ +name: CI/CD +on: + push: + branches: [main] + +permissions: + contents: read + packages: write + +jobs: + test-frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: frontend/go.mod + + - name: Run frontend tests + working-directory: frontend + run: go test ./... -v + + test-backend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: backend/go.mod + + - name: Run backend tests + working-directory: backend + run: go test ./... -v + + build-and-deploy: + runs-on: ubuntu-latest + needs: [test-frontend, test-backend] + steps: + - uses: actions/checkout@v4 + + - name: Build and push Backend + uses: docker/build-push-action@v5 + with: + context: ./backend + push: true + tags: ghcr.io/${{ github.repository_owner }}/simple-fortune-backend:latest + + - name: Build and push Frontend + uses: docker/build-push-action@v5 + with: + context: ./frontend + push: true + tags: ghcr.io/${{ github.repository_owner }}/simple-fortune-frontend:latest + + - name: Deploy + run: | + docker pull ghcr.io/${{ github.repository_owner }}/simple-fortune-backend:latest + docker pull ghcr.io/${{ github.repository_owner }}/simple-fortune-frontend:latest + echo "Deployed from GHCR" diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..13566b81b --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..639900d13 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..b16efce0e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/simple-fortune-cookie.iml b/.idea/simple-fortune-cookie.iml new file mode 100644 index 000000000..25ed3f6e7 --- /dev/null +++ b/.idea/simple-fortune-cookie.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 000000000..baf83374f --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,7 @@ +FROM golang:1.22 +WORKDIR /app +COPY . . +RUN go mod download +RUN go build -o main . +EXPOSE 9000 +CMD ["./main"] \ No newline at end of file diff --git a/backend/backend.exe b/backend/backend.exe new file mode 100644 index 000000000..f79dff97f Binary files /dev/null and b/backend/backend.exe differ diff --git a/backend/main.go b/backend/main.go index 650ef3963..4d140fa61 100644 --- a/backend/main.go +++ b/backend/main.go @@ -178,5 +178,5 @@ func main() { mux.Handle("/fortunes/", fortuneH) err := http.ListenAndServe(":9000", mux) - fmt.Println("%v", err) + fmt.Printf("Result: %v\n", err) } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..f756fe055 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + backend: + build: + context: ./backend + ports: + - "9000:9000" + + frontend: + build: + context: ./frontend + ports: + - "8080:8080" + environment: + BACKEND_DNS: backend + BACKEND_PORT: 9000 \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 000000000..4c397b7e9 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,7 @@ +FROM golang:1.22 +WORKDIR /app +COPY . . +RUN go mod download +RUN go build -o main . +EXPOSE 8080 +CMD ["./main"] \ No newline at end of file diff --git a/frontend/frontend.exe b/frontend/frontend.exe new file mode 100644 index 000000000..61543eb82 Binary files /dev/null and b/frontend/frontend.exe differ diff --git a/frontend/main.go b/frontend/main.go index bede472b4..3a85c474b 100644 --- a/frontend/main.go +++ b/frontend/main.go @@ -1,19 +1,19 @@ package main import ( - "io" - "fmt" - "encoding/json" - "html/template" - "net/http" - "log" - "time" - "bytes" - "math/rand" + "bytes" + "encoding/json" + "fmt" + "html/template" + "io" + "log" + "math/rand" + "net/http" + "time" ) -var BACKEND_DNS=getEnv("BACKEND_DNS", "localhost") -var BACKEND_PORT=getEnv("BACKEND_PORT", "9000") +var BACKEND_DNS = getEnv("BACKEND_DNS", "localhost") +var BACKEND_PORT = getEnv("BACKEND_PORT", "9000") type fortune struct { ID string `json:"id" redis:"id"` @@ -21,85 +21,85 @@ type fortune struct { } type newFortune struct { - Message string `json:"message"` + Message string `json:"message"` } // use a custom client, because we don't do blocking operations wihout timeouts var myClient = &http.Client{Timeout: 10 * time.Second} func HealthzHandler(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - io.WriteString(w, "healthy") + w.WriteHeader(http.StatusOK) + io.WriteString(w, "healthy") } func main() { - http.HandleFunc("/healthz", HealthzHandler) + http.HandleFunc("/healthz", HealthzHandler) - http.HandleFunc("/api/random", func (w http.ResponseWriter, r *http.Request) { - resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes/random", BACKEND_DNS, BACKEND_PORT)) - if err != nil { - log.Fatalln(err) - fmt.Fprint(w, err) - return - } + http.HandleFunc("/api/random", func(w http.ResponseWriter, r *http.Request) { + resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes/random", BACKEND_DNS, BACKEND_PORT)) + if err != nil { + log.Fatalln(err) + fmt.Fprint(w, err) + return + } - f := new(fortune) - json.NewDecoder(resp.Body).Decode(f) + f := new(fortune) + json.NewDecoder(resp.Body).Decode(f) - fmt.Fprint(w, f.Message) - return - }) + fmt.Fprint(w, f.Message) + return + }) - http.HandleFunc("/api/all", func (w http.ResponseWriter, r *http.Request) { - resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT)) - if err != nil { - log.Fatalln(err) - fmt.Fprint(w, err) - return - } + http.HandleFunc("/api/all", func(w http.ResponseWriter, r *http.Request) { + resp, err := myClient.Get(fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT)) + if err != nil { + log.Fatalln(err) + fmt.Fprint(w, err) + return + } - fortunes := new([]fortune) - json.NewDecoder(resp.Body).Decode(fortunes) + fortunes := new([]fortune) + json.NewDecoder(resp.Body).Decode(fortunes) - tmpl, err := template.ParseFiles("./templates/fortunes.html") + tmpl, err := template.ParseFiles("./templates/fortunes.html") - if err != nil { - log.Fatalln(err) - fmt.Fprint(w, err) - return - } + if err != nil { + log.Fatalln(err) + fmt.Fprint(w, err) + return + } - tmpl.Execute(w, fortunes) - return - }) + tmpl.Execute(w, fortunes) + return + }) - http.HandleFunc("/api/add", func (w http.ResponseWriter, r *http.Request) { + http.HandleFunc("/api/add", func(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { - http.Error(w, "Use POST", http.StatusMethodNotAllowed) - return - } + if r.Method != "POST" { + http.Error(w, "Use POST", http.StatusMethodNotAllowed) + return + } - f := new(newFortune) - json.NewDecoder(r.Body).Decode(f) + f := new(newFortune) + json.NewDecoder(r.Body).Decode(f) - var postUrl = fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT) - var jsonStr = []byte(fmt.Sprintf(`{"id": "%d", "message": "%s"}`, rand.Intn(10000), f.Message)) + var postUrl = fmt.Sprintf("http://%s:%s/fortunes", BACKEND_DNS, BACKEND_PORT) + var jsonStr = []byte(fmt.Sprintf(`{"id": "%d", "message": "%s"}`, rand.Intn(10000), f.Message)) - _, err := myClient.Post(postUrl, "application/json", bytes.NewBuffer(jsonStr)) - if err != nil { - log.Fatalln(err) - fmt.Fprint(w, err) - return - } + _, err := myClient.Post(postUrl, "application/json", bytes.NewBuffer(jsonStr)) + if err != nil { + log.Fatalln(err) + fmt.Fprint(w, err) + return + } - fmt.Fprint(w, "Cookie added!") + fmt.Fprint(w, "Cookie added!") // comment - return - }) + return + }) - http.Handle("/", http.FileServer(http.Dir("./static"))) - err := http.ListenAndServe(":8080", nil) - fmt.Println("%v", err) + http.Handle("/", http.FileServer(http.Dir("./static"))) + err := http.ListenAndServe(":8080", nil) + fmt.Printf("Result: %v\n", err) }