Skip to content

Commit d5eb375

Browse files
initial commit
Signed-off-by: Dusan Malusev <dusan@dusanmalusev.dev>
0 parents  commit d5eb375

31 files changed

Lines changed: 7849 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: Go
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
10+
workflow_dispatch:
11+
workflow_call:
12+
13+
jobs:
14+
build:
15+
name: Lint Test and Build
16+
runs-on: ubuntu-24.04
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
- name: Setup Go
23+
uses: actions/setup-go@v6
24+
with:
25+
go-version: '1.25'
26+
check-latest: true
27+
- name: Set up gotestfmt
28+
uses: GoTestTools/gotestfmt-action@v2
29+
with:
30+
repo: gotestfmt
31+
- name: Unit Tests
32+
run: |
33+
sudo apt install libnss3-tools
34+
make setup
35+
make test
36+
- name: Linting
37+
uses: golangci/golangci-lint-action@v8
38+
- name: Run Gosec
39+
uses: securego/gosec@master
40+
with:
41+
args: ./...
42+
- name: Upload coverage reports to Codecov
43+
uses: codecov/codecov-action@v5
44+
with:
45+
token: ${{ secrets.CODECOV_TOKEN }}
46+
slug: CodeLieutenant/utils

.github/workflows/release.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
packages: write
11+
issues: write
12+
id-token: write
13+
14+
jobs:
15+
release:
16+
runs-on: ubuntu-24.04
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
- name: Get Tag
23+
if: startsWith(github.ref, 'refs/tags/v')
24+
uses: olegtarasov/get-tag@v2.1.3
25+
id: version_tag
26+
with:
27+
tagRegex: "v(.*)"
28+
- name: Upload Release Asset
29+
uses: softprops/action-gh-release@v1
30+
id: release
31+
with:
32+
name: "v${{ steps.version_tag.outputs.tag }}"
33+
tag_name: "v${{ steps.version_tag.outputs.tag }}"
34+
generate_release_notes: true
35+
append_body: true
36+
prerelease: false
37+
fail_on_unmatched_files: true
38+
- name: "Generate release changelog"
39+
uses: heinrichreimer/action-github-changelog-generator@v2.3
40+
with:
41+
token: ${{ secrets.GITHUB_TOKEN }}
42+
author: true
43+
releaseUrl: ${{ steps.release.outputs.url }}
44+
issues: false
45+
pullRequests: true
46+
- uses: stefanzweifel/git-auto-commit-action@v5
47+
with:
48+
commit_message: "Update CHANGELOG.md"
49+
branch: master
50+
commit_options: "--no-verify --signoff"
51+
file_pattern: CHANGELOG.md

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
certs/
2+
bin/
3+
coverage.txt
4+
coverage.out
5+
.DS_Store
6+
*.html
7+
*.log

Makefile

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
SHELL := /usr/bin/env bash
2+
.DEFAULT_GOAL := help
3+
.EXPORT_ALL_VARIABLES:
4+
5+
MKCERT_INSTALL_PATH ?= $(HOME)/.local/bin
6+
MKCERT_VERSION := v1.4.4
7+
MKCERT_ASSET := mkcert-$(MKCERT_VERSION)-linux-amd64
8+
MKCERT_URL := https://github.com/FiloSottile/mkcert/releases/latest/download/$(MKCERT_ASSET)
9+
CERT_DIR ?= ./certs
10+
HOSTS ?= localhost 127.0.0.1 ::1
11+
12+
13+
.PHONY: setup
14+
setup: install-mkcert mkcert-generate mkcert-generate mkcert-install-ca
15+
16+
17+
.PHONY: check
18+
check: ## Run linters
19+
@go tool golangci-lint run
20+
21+
.PHONY: fix
22+
fix: ## Auto-fix lint issues where possible
23+
@go tool golangci-lint run --fix
24+
25+
.PHONY: fieldalign
26+
fieldalign: ## Apply field alignment fixes
27+
@go tool fieldalignment -fix ./pkg/...
28+
29+
.PHONY: fmt
30+
fmt: ## Run project formatting helpers
31+
@go tool golangci-lint fmt
32+
33+
.PHONY: tidy
34+
tidy: ## Tidy go.mod and download modules
35+
go mod tidy
36+
go mod download
37+
38+
.PHONY: test
39+
test: ## Run unit tests with coverage (JSON output piped through gotestfmt)
40+
@go test -covermode=atomic -gcflags='all=-N -l' -tags testing -coverprofile=coverage.txt -timeout 5m -json -v ./... 2>&1 | go tool gotestfmt -showteststatus
41+
42+
.PHONY: security
43+
security: ## Run basic security checks (gosec)
44+
go tool gosec ./...
45+
46+
47+
# --- Mkcert -------------------------------
48+
install-mkcert: ## Install mkcert binary to $(MKCERT_INSTALL_PATH) if missing
49+
@if [ ! -f "$(MKCERT_INSTALL_PATH)/mkcert" ]; then \
50+
mkdir -p $(MKCERT_INSTALL_PATH) 2>/dev/null || true; \
51+
echo "Downloading mkcert from $(MKCERT_URL)..."; \
52+
tmp=$$(mktemp -d); \
53+
curl -L --fail -o $$tmp/$(MKCERT_ASSET) "$(MKCERT_URL)"; \
54+
chmod +x $$tmp/$(MKCERT_ASSET); \
55+
mv $$tmp/$(MKCERT_ASSET) $(MKCERT_INSTALL_PATH)/mkcert || { echo "Failed to move mkcert to $(MKCERT_INSTALL_PATH)"; exit 1; }; \
56+
rm -rf $$tmp; \
57+
echo "mkcert installed to $(MKCERT_INSTALL_PATH)/mkcert"; \
58+
fi
59+
60+
mkcert-install-ca: install-mkcert ## Install mkcert CA into system trust stores
61+
@echo "Installing mkcert CA..."
62+
@$(MKCERT_INSTALL_PATH)/mkcert -install
63+
64+
mkcert-generate: mkcert-install-ca ## Generate cert/key for HOSTS into $(CERT_DIR) (usage: make mkcert-generate HOSTS='example.test localhost')
65+
@mkdir -p $(CERT_DIR)
66+
@echo "Generating cert for: $(HOSTS)"
67+
@$(MKCERT_INSTALL_PATH)/mkcert -cert-file $(CERT_DIR)/smtp.crt -key-file $(CERT_DIR)/smtp.key $(HOSTS)
68+
@echo "Created $(CERT_DIR)/smtp.crt and $(CERT_DIR)/smtp.key"
69+
@$(MKCERT_INSTALL_PATH)/mkcert -cert-file $(CERT_DIR)/local.crt -key-file $(CERT_DIR)/local.key $(HOSTS)
70+
@echo "Created $(CERT_DIR)/local.crt and $(CERT_DIR)/local.key"
71+
72+
mkcert-uninstall-ca: install-mkcert ## Uninstall mkcert CA from system trust stores
73+
@echo "Uninstalling mkcert CA..."
74+
@$(MKCERT_INSTALL_PATH)/mkcert -uninstall || echo "mkcert -uninstall failed (maybe not installed)"
75+
76+
77+
.PHONY: clean
78+
clean: ## Remove build artifacts
79+
rm -rf bin/
80+
go clean
81+
82+
.PHONY: help
83+
help:
84+
@echo 'UTILS - Available commands:'
85+
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)

bytes.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package utils
2+
3+
import (
4+
"math"
5+
"slices"
6+
"strconv"
7+
)
8+
9+
type MemorySize uint64
10+
11+
const (
12+
KiB MemorySize = 1 << 10
13+
MiB MemorySize = 1 << 20
14+
GiB MemorySize = 1 << 30
15+
TiB MemorySize = 1 << 40
16+
)
17+
18+
func (m MemorySize) String() string {
19+
switch {
20+
case m >= TiB:
21+
return FormatMemorySize(m, TiB, "TiB")
22+
case m >= GiB:
23+
return FormatMemorySize(m, GiB, "GiB")
24+
case m >= MiB:
25+
return FormatMemorySize(m, MiB, "MiB")
26+
case m >= KiB:
27+
return FormatMemorySize(m, KiB, "KiB")
28+
default:
29+
return FormatMemorySize(m, 1, "B")
30+
}
31+
}
32+
33+
var largestFloatSize = len(strconv.FormatFloat(math.MaxFloat64, 'f', 0, 64))
34+
35+
func FormatMemorySize(m MemorySize, unit MemorySize, suffix string) string {
36+
bytes := make([]byte, 0, len(suffix)+largestFloatSize)
37+
38+
bytes = strconv.AppendFloat(bytes, float64(m)/float64(unit), 'f', 0, 64)
39+
bytes = append(bytes, suffix...)
40+
41+
return UnsafeString(slices.Clip(bytes))
42+
}

0 commit comments

Comments
 (0)