Skip to content

Commit

Permalink
init: go server (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
aboqasem authored Feb 10, 2024
1 parent b18dc5a commit bd13650
Show file tree
Hide file tree
Showing 12 changed files with 319 additions and 3 deletions.
12 changes: 11 additions & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

bun biome ci .
if (git diff --name-only --staged | grep -qvE '^(README\.md|(biome|vercel)\.json|(\.vscode|\.github|\.husky|scripts|server)/.*)$'); then
bun biome ci .
else
echo "Skipping 'biome ci'"
fi

if (git diff --quiet --name-only --staged server); then
echo "Skipping server 'make tidy'"
else
cd server && make tidy
fi
15 changes: 14 additions & 1 deletion .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

bun biome ci . && pnpm build
BRANCH="$(git rev-parse --abbrev-ref HEAD)"

if (git diff --name-only origin/"$BRANCH" HEAD | grep -qvE '^(README\.md|(biome|vercel)\.json|(\.vscode|\.github|\.husky|scripts|server)/.*)$'); then
bun biome ci .
bun run build
else
echo "Skipping 'bun run build'"
fi

if (git diff --quiet --name-only origin/"$BRANCH" HEAD server); then
echo "Skipping server 'make audit'"
else
cd server && make audit
fi
46 changes: 46 additions & 0 deletions server/.air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# root = "."
# testdata_dir = "testdata"
# tmp_dir = "tmp"

[build]
# args_bin = []
bin = "./tmp/app"
cmd = "make build"
delay = 100
# exclude_dir = ["assets", "tmp", "vendor", "testdata"]
# exclude_file = []
# exclude_regex = ["_test.go"]
# exclude_unchanged = false
# follow_symlink = false
# full_bin = ""
# include_dir = []
# include_ext = ["go", "tpl", "tmpl", "html"]
include_file = [".env"]
# kill_delay = "0s"
# log = "build-errors.log"
# poll = false
# poll_interval = 0
# post_cmd = []
# pre_cmd = []
# rerun = false
# rerun_delay = 500
# send_interrupt = false
# stop_on_error = false

[color]
# app = ""
# build = "yellow"
# main = "magenta"
# runner = "green"
# watcher = "cyan"

[log]
# main_only = false
# time = false

[misc]
clean_on_exit = true

[screen]
# clear_on_rebuild = false
# keep_scroll = true
2 changes: 2 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
PORT=8080
LOG_LEVEL=debug
27 changes: 27 additions & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Allowlisting gitignore template for GO projects prevents us
# from adding various unwanted local files, such as generated
# files, developer configurations or IDE-specific files etc.

# Ignore everything
*

# But not these files...
!/.gitignore

!*.go
!go.sum
!go.mod

!README.md
!LICENSE

!Makefile

!.env.example

!.air.toml

!staticcheck.conf

# ...even if they are in subdirectories
!*/
83 changes: 83 additions & 0 deletions server/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Change these variables as necessary.
MAIN_PACKAGE_PATH := ./
BINARY_NAME := app

# ==================================================================================== #
# HELPERS
# ==================================================================================== #

## help: print this help message
.PHONY: help
help:
@echo 'Usage:'
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'

.PHONY: confirm
confirm:
@echo -n 'Are you sure? [y/N] ' && read ans && [ $${ans:-N} = y ]

.PHONY: no-dirty
no-dirty:
git diff --exit-code

.PHONY: env
env:
@if [ ! -f .env ]; then \
cp .env.example .env; \
fi


# ==================================================================================== #
# QUALITY CONTROL
# ==================================================================================== #

## tidy: format code and tidy modfile
.PHONY: tidy
tidy:
go fmt ./...
go mod tidy -v

## audit: run quality control checks
.PHONY: audit
audit: tidy
go mod verify
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@latest ./...
go test -race -buildvcs -vet=off ./...


## vulncheck: check for known vulnerabilities
.PHONY: vulncheck
vulncheck:
go run golang.org/x/vuln/cmd/govulncheck@latest ./...


# ==================================================================================== #
# DEVELOPMENT
# ==================================================================================== #

## test: run all tests
.PHONY: test
test:
go test -v -race -buildvcs ./...

## test/cover: run all tests and display coverage
.PHONY: test/cover
test/cover:
go test -v -race -buildvcs -coverprofile=/tmp/coverage.out ./...
go tool cover -html=/tmp/coverage.out

## build: build the application
.PHONY: build
build:
go build -o=./tmp/${BINARY_NAME} ${MAIN_PACKAGE_PATH}

## run: run the application
.PHONY: run
run: env build
./tmp/${BINARY_NAME}

## run/live: run the application with reloading on file changes
.PHONY: run/live
run/live: env
go run github.com/cosmtrek/air@v1.49.0
56 changes: 56 additions & 0 deletions server/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package config

import (
"os"
"strconv"

"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)

var logger = log.With().Str("pkg", "config").Logger()

type Config struct {
Port uint16
LogLevel zerolog.Level
}

func Init() *Config {
return &Config{
Port: parseEnv("PORT", 8080, parseUint16),
LogLevel: parseEnv("LOG_LEVEL", zerolog.InfoLevel, parseLogLevel),
}
}

// UTILS

func parseEnv[V any, P func(string) (V, error)](key string, defaultVal V, parser P) V {
if str, exists := os.LookupEnv(key); exists {
if value, err := parser(str); err == nil {
return value
} else {
logger.Warn().Err(err).Msgf("Failed to parse env var '%s'", key)
}
}

return defaultVal
}

// PARSERS

func parseUint(s string, bits int) (uint64, error) {
return strconv.ParseUint(s, 10, bits)
}

func parseUint16(s string) (uint16, error) {
i, e := parseUint(s, 16)
return uint16(i), e
}

func parseLogLevel(s string) (zerolog.Level, error) {
l, err := zerolog.ParseLevel(s)
if err != nil {
return zerolog.NoLevel, err
}
return l, nil
}
12 changes: 12 additions & 0 deletions server/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module github.com/aboqasem/portfolio/server

go 1.21.7

require github.com/rs/zerolog v1.31.0

require (
github.com/joho/godotenv v1.5.1
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
golang.org/x/sys v0.13.0 // indirect
)
18 changes: 18 additions & 0 deletions server/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
45 changes: 45 additions & 0 deletions server/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package main

import (
"fmt"
"html"
"net/http"
"time"

"github.com/aboqasem/portfolio/server/config"
_ "github.com/joho/godotenv/autoload"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)

var logger = log.With().Str("pkg", "main").Logger()
var conf *config.Config

func init() {
conf = config.Init()

zerolog.SetGlobalLevel(conf.LogLevel)
}

func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
logger.Debug().Any("method", r.Method).Str("path", r.URL.Path).Send()

w.Write([]byte("<h1>Hello, world!</h1><pre>"))
w.Write([]byte(r.Method))
w.Write([]byte{' '})
w.Write([]byte(html.EscapeString(r.URL.Path)))
w.Write([]byte("</pre>"))
})

server := &http.Server{
Addr: fmt.Sprintf(":%d", conf.Port),
ReadHeaderTimeout: 3 * time.Second,
}

logger.Info().Uint16("port", conf.Port).Msg("Running server...")
err := server.ListenAndServe()
if err != nil {
logger.Fatal().Err(err).Msg("ListenAndServe")
}
}
4 changes: 4 additions & 0 deletions server/staticcheck.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
checks = ["all", "-ST1000", "-ST1020", "-ST1021", "-ST1022"]
initialisms = []
dot_import_whitelist = []
http_status_code_whitelist = []
2 changes: 1 addition & 1 deletion vercel.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
"outputDirectory": "dist",
"devCommand": "bun dev",
"framework": "vite",
"ignoreCommand": "! git diff --name-only HEAD^ HEAD | grep --quiet --invert-match --extended-regexp '^README\\.md|\\.biome\\.json|(\\.vscode|\\.github|\\.husky|scripts)/.*$'"
"ignoreCommand": "! git diff --name-only HEAD^ HEAD | grep --quiet --invert-match --extended-regexp '^README\\.md|biome\\.json|(\\.vscode|\\.github|\\.husky|scripts|server)/.*$'"
}

0 comments on commit bd13650

Please sign in to comment.