Skip to content

Commit

Permalink
feat: change the sample application (#33)
Browse files Browse the repository at this point in the history
* feat: change the sample application
* ci: add `docker-publish`
* ci: add `makefile`
* ci(pre-commit): add `actionlint` hook
* docs(README): add `image` info
  • Loading branch information
DeadNews committed Sep 18, 2023
1 parent 9d8c646 commit c22ecdf
Show file tree
Hide file tree
Showing 11 changed files with 308 additions and 18 deletions.
100 changes: 100 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Docker

on:
push:
branches: ["main"]
tags: ["v*.*.*"]
pull_request:
branches: ["main"]

permissions:
contents: read

env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}

jobs:
build:
runs-on: ubuntu-latest
permissions:
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
# To upload sarif files.
security-events: write

steps:
- name: Checkout repository
uses: actions/checkout@v4

# Install the cosign tool except on PR.
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2

# Add support for more platforms with QEMU.
- name: Setup QEMU
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0

# Using it to be able to build multi-platform images, export cache, etc.
- name: Setup Docker buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0

# Login against a Docker registry except on PR.
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Extract metadata (tags, labels) for Docker.
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

# Build and push Docker image with Buildx (don't push on PR).
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64

# Sign the resulting Docker image digest except on PRs.
- name: Sign the published Docker image
if: ${{ github.event_name != 'pull_request' }}
env:
TAGS: ${{ steps.meta.outputs.tags }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
# This step uses the identity token to provision an ephemeral certificate
# against the sigstore community Fulcio instance.
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}

# Analyze vulnerabilities.
- name: Run Trivy vulnerability scanner
if: ${{ github.event_name != 'pull_request' }}
uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # v0.12.0
with:
image-ref: ${{ steps.meta.outputs.tags }}
format: "sarif"
output: "trivy-results.sarif"

# Upload report to GitHub code scanning.
- name: Upload Trivy scan results to GitHub Security tab
if: ${{ github.event_name != 'pull_request' }}
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: "trivy-results.sarif"
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest]
go: ["oldstable", "stable"]
runs-on: ${{ matrix.os }}
steps:
Expand All @@ -55,7 +55,7 @@ jobs:
go-version: ${{ matrix.go }}

- name: Run tests
run: go test -v -race -covermode=atomic -coverprofile='coverage.txt' ./...
run: make test

- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-latest' && matrix.go == 'stable'
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
*.so
*.dylib

# Distribution
dist/

# Test binary, built with `go test -c`
*.test

Expand Down
11 changes: 8 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,26 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.2
rev: v3.0.3
hooks:
- id: prettier

- repo: https://github.com/crate-ci/typos
rev: v1.16.8
rev: v1.16.11
hooks:
- id: typos

- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.26.1
rev: 0.26.3
hooks:
- id: check-github-workflows
- id: check-renovate

- repo: https://github.com/rhysd/actionlint
rev: v1.6.25
hooks:
- id: actionlint

- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
Expand Down
18 changes: 18 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Build the application from source.
FROM golang:1.21-alpine@sha256:96634e55b363cb93d39f78fb18aa64abc7f96d372c176660d7b8b6118939d97b AS go-builder

WORKDIR /tmp/app
COPY go.mod go.sum cmd ./
RUN go build -o /tmp/deadnews-template-go ./...

# Deploy the application binary into a lean image.
FROM gcr.io/distroless/static-debian12:latest@sha256:98e138282ba524ff4f5124fec603f82ee2331df4ba981d169b3ded8bcd83ca52 AS final

WORKDIR /
COPY --from=go-builder /tmp/deadnews-template-go /bin/deadnews-template-go

USER nonroot:nonroot
EXPOSE 1271
HEALTHCHECK --interval=60s --timeout=3s CMD curl --fail http://127.0.0.1:1271/health || exit 1

CMD ["/bin/deadnews-template-go"]
10 changes: 10 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
run_app:
go run cmd/deadnews-template-go/main.go

test:
go test -v -race -covermode=atomic -coverprofile='coverage.txt' ./...

build:
go build -o ./dist/ ./...

.PHONY: test
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@
[![Main](https://github.com/DeadNews/deadnews-template-go/actions/workflows/main.yml/badge.svg)](https://github.com/DeadNews/deadnews-template-go/actions/workflows/main.yml)
[![pre-commit.ci](https://results.pre-commit.ci/badge/github/DeadNews/deadnews-template-go/main.svg)](https://results.pre-commit.ci/latest/github/DeadNews/deadnews-template-go/main)
[![codecov](https://codecov.io/gh/DeadNews/deadnews-template-go/branch/main/graph/badge.svg?token=OCZDZIYPMC)](https://codecov.io/gh/DeadNews/deadnews-template-go)
[![image_version](https://ghcr-badge.egpl.dev/DeadNews/deadnews-template-go/latest_tag?trim=major&ignore=sha256*)](https://github.com/DeadNews/deadnews-template-go/pkgs/container/deadnews-template-go)
[![image_size](https://ghcr-badge.egpl.dev/DeadNews/deadnews-template-go/size)](https://github.com/DeadNews/deadnews-template-go/pkgs/container/deadnews-template-go)
61 changes: 53 additions & 8 deletions cmd/deadnews-template-go/main.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,61 @@
package main

import (
"fmt"
"net/http"
"os"

"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)

// Get answer to the Meaning of Life, the Universe, and Everything.
func GetAnswer() int {
result := 40 + 2
return result
// This code is the main function of a Go program
// that creates and starts a server using the Echo framework.
// It retrieves the value of the "PORT" environment variable
// and if it is not set, it defaults to port 1271.
func main() {
// Create a new Echo instance.
e := makeServer()

// Get the value of the "PORT" environment variable.
httpPort := os.Getenv("PORT")
if httpPort == "" {
httpPort = "1271"
}

// Start the server on the specified port.
e.Logger.Fatal(e.Start(":" + httpPort))
}

func main() {
fmt.Println(GetAnswer())
// Output: 42
// makeServer creates a new instance of the Echo framework
// and configures it with middleware for logging and error recovery.
// It also defines two route handlers: one for the root ("/") route that returns an HTML response,
// and another for the "/health" route that returns a JSON response.
//
// Returns:
// - A configured instance of the Echo framework.
func makeServer() *echo.Echo {
// Create a new Echo instance.
e := echo.New()

// Use middleware for logging and error recovery.
e.Use(middleware.Logger())
e.Use(middleware.Recover())

// Define the "/" route handler.
e.GET("/", handleRoot)

// Define the "/health" route handler.
e.GET("/health", handleHealth)

return e
}

// handleRoot handles the "/" route and returns an HTML response.
func handleRoot(c echo.Context) error {
return c.HTML(http.StatusOK, "Hello, World!")
}

// handleHealth handles the "/health" route and returns a JSON response.
func handleHealth(c echo.Context) error {
return c.JSON(http.StatusOK, struct{ Status string }{Status: "OK"})
}
61 changes: 58 additions & 3 deletions cmd/deadnews-template-go/main_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,68 @@
package main

import (
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
)

func TestGetAnswer(t *testing.T) {
assert := assert.New(t)
func TestServerCreation(t *testing.T) {
e := makeServer()

assert.Equal(42, GetAnswer(), "they should be equal")
assert.NotNil(t, e.GET("/", handleRoot))
assert.NotNil(t, e.GET("/health", handleHealth))
}

func TestServerResponseRoot(t *testing.T) {
// Create a new Echo instance.
e := makeServer()

// Create a new HTTP request to the root route.
req := httptest.NewRequest(http.MethodGet, "/", nil)

// Create a new HTTP response recorder.
rec := httptest.NewRecorder()

// Handle the request.
e.ServeHTTP(rec, req)

// Check the actual status code against the expected status code.
expectedStatus := http.StatusOK
assert.Equal(t, expectedStatus, rec.Code)

// Check the actual response body against the expected response body.
expectedBody := "Hello, World!"
assert.Equal(t, expectedBody, rec.Body.String())

// Check the response content type header.
contentType := "text/html; charset=UTF-8"
assert.Equal(t, contentType, rec.Header().Get("Content-Type"))
}

func TestServerResponseHealth(t *testing.T) {
// Create a new Echo instance.
e := makeServer()

// Create a new HTTP request with the "/health" route.
req := httptest.NewRequest(http.MethodGet, "/health", nil)

// Create a new HTTP response recorder.
rec := httptest.NewRecorder()

// Handle the request.
e.ServeHTTP(rec, req)

// Check the actual status code against the expected status code.
expectedStatus := http.StatusOK
assert.Equal(t, expectedStatus, rec.Code)

// Check the actual response body against the expected response body.
expectedBody := `{"Status":"OK"}` + "\n"
assert.Equal(t, expectedBody, rec.Body.String())

// Check the response content type header.
contentType := "application/json; charset=UTF-8"
assert.Equal(t, contentType, rec.Header().Get("Content-Type"))
}
18 changes: 16 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
module github.com/DeadNews/deadnews-template-go

go 1.20
go 1.21

require github.com/stretchr/testify v1.8.4
require (
github.com/labstack/echo/v4 v4.11.1
github.com/stretchr/testify v1.8.4
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit c22ecdf

Please sign in to comment.