common

package module
v0.0.0-...-c80c147 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2026 License: Apache-2.0 Imports: 9 Imported by: 0

README

codefloe/common

A high-performance Go library for parsing environment variables into structs using struct tags, with pre-built CI/CD environment variable bindings.

Features

  • High Performance: Reflection-based field mapping is cached per type; hot-path operations use direct unsafe.Pointer arithmetic
  • 🎯 Type Safe: Support for 15+ basic types including pointers, time.Duration, and slices
  • 📦 JSON Objects: Parse JSON environment variables directly into Go structs with the object property
  • 🔗 Embedded Structs: Automatic traversal of anonymous struct fields
  • 🚀 Zero Allocation (scalars): Scalar field assignments are zero-allocation
  • 📋 CI/CD Ready: Pre-defined environment bindings for standard CI/CD platforms

Installation

go get codefloe.com/actions/common

Table of Contents

Quick Start

Using Pre-defined CI Environment
package main

import (
    "fmt"
    "codefloe.com/actions/common"
)

func main() {
    env := common.NewEnvironment()

    fmt.Println("Repository:", env.Repo)
    fmt.Println("Branch:", env.CommitBranch)
    fmt.Println("Pipeline #:", env.PipelineNumber)
    fmt.Println("Full environment:", env) // JSON representation
}
Parsing Custom Configuration
package main

import (
    "time"
    "codefloe.com/actions/common"
)

type AppConfig struct {
    Host    string        `env:"APP_HOST"`
    Port    int           `env:"APP_PORT"`
    Debug   bool          `env:"APP_DEBUG"`
    Timeout time.Duration `env:"APP_TIMEOUT"`
}

func main() {
    var cfg AppConfig
    common.ParseEnvironment(&cfg)
    // cfg is now populated from environment variables
}

ParseEnvironment

ParseEnvironment(dst any) populates struct fields from environment variables using the env struct tag.

How it works:

  1. Struct metadata is analyzed via reflection (done once and cached per type)
  2. Environment variables are read via os.LookupEnv()
  3. Values are parsed based on field type and written via direct pointer arithmetic
  4. Subsequent calls use the cached metadata with zero reflection overhead
Supported Types
Type Format Example Env Value
string any string hello APP_NAME=hello
bool true/false/1/0 true APP_DEBUG=true
int, int8..int64 decimal integer -42 APP_COUNT=-42
uint, uint8..uint64 unsigned decimal 42 APP_COUNT=42
float32, float64 decimal float 3.14 APP_RATIO=3.14
time.Duration Go duration syntax 5s APP_TIMEOUT=5s
[]string comma-separated values a,b,c APP_TAGS=a,b,c

Duration examples: 100ms, 30s, 2m30s, 1h

Pointer Types

Pointer variants of all basic types are supported. This allows distinguishing between "not set" (nil) and "zero value":

type Config struct {
    // Value types: missing env var → zero value
    Name    string `env:"APP_NAME"`    // unset → ""
    Port    int    `env:"APP_PORT"`    // unset → 0
    Debug   bool   `env:"APP_DEBUG"`   // unset → false

    // Pointer types: missing env var → nil
    Verbose *bool  `env:"APP_VERBOSE"`   // unset → nil
    Workers *int   `env:"APP_WORKERS"`   // unset → nil
    Secret  *string `env:"APP_SECRET"`   // unset → nil
}
Field Type Env Var Not Set Env Var Set
T (value) zero value ("", false, 0) parsed value
*T (pointer) nil pointer to parsed value
JSON Objects

Parse JSON environment variables directly into structs, maps, or slices using the object property:

type DatabaseConfig struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Username string `json:"username"`
}

type AppConfig struct {
    Name string            `env:"APP_NAME"`
    DB   DatabaseConfig    `env:"DB_CONFIG,object"`    // ← object property
    Features map[string]interface{} `env:"FEATURES,object"`
    AllowedHosts []string `env:"ALLOWED_HOSTS,object"`
}

Environment:

export APP_NAME=MyApp
export DB_CONFIG='{"host":"localhost","port":5432,"username":"admin"}'
export FEATURES='{"auth":true,"cache":false,"ratelimit":10}'
export ALLOWED_HOSTS='["localhost","127.0.0.1","api.example.com"]'

Result:

var cfg AppConfig
common.ParseEnvironment(&cfg)

// cfg.DB is now populated from JSON in DB_CONFIG
fmt.Println(cfg.DB.Host)  // "localhost"
fmt.Println(cfg.DB.Port)  // 5432

// cfg.Features contains parsed JSON object
fmt.Println(cfg.Features["auth"])  // true

Key behaviors:

  • Invalid JSON silently fails (field remains unchanged)
  • Missing environment variable leaves field unchanged
  • Works with any struct type, map, slice, or primitive that implements json.Unmarshaler
  • When object is specified, the field is treated as a complete JSON unit (no inner field recursion)
String Slices

String slices support three formats:

# Format 1: Single value
APP_TAGS=single

# Format 2: Comma-separated
APP_TAGS=a,b,c,d

# Format 3: Comma-separated with brackets
APP_TAGS=[a,b,c,d]

All three formats parse to []string{"..."}

Embedded Structs

Anonymous (embedded) struct fields are traversed automatically, exposing their env-tagged fields:

type BaseConfig struct {
    Name string `env:"APP_NAME"`
    Port int    `env:"APP_PORT"`
}

type ServerConfig struct {
    BaseConfig  // ← fields automatically exposed
    TLS bool    `env:"SERVER_TLS"`
}

var cfg ServerConfig
common.ParseEnvironment(&cfg)

// cfg.Name comes from APP_NAME
// cfg.Port comes from APP_PORT
// cfg.TLS comes from SERVER_TLS

Important: If an embedded struct field has an env tag with object property, it's treated as JSON data and not recursively processed.

Behavior Notes
  • Skipped fields: Fields without an env tag or with env:"-" are ignored
  • Missing variables: If an env var is not set, the field is left untouched (preserves initialization)
  • Parse errors: If parsing fails (e.g., "abc" for int), the field retains its current value
  • Nil on unset: Only pointer-type fields become nil when env var is not set; value types retain zero values

CI Environment

NewEnvironment() returns a *Environment struct pre-populated with standard CI/CD variables. The struct covers these variable groups:

Group Prefix Examples
General CI_ CI, CI_WORKSPACE, CI_REPO, CI_REPO_PRIVATE
Commit CI_COMMIT_ CI_COMMIT_SHA, CI_COMMIT_BRANCH, CI_COMMIT_TAG, CI_COMMIT_MESSAGE
Pipeline CI_PIPELINE_ CI_PIPELINE_NUMBER, CI_PIPELINE_EVENT, CI_PIPELINE_URL, CI_PIPELINE_CREATED
Workflow CI_WORKFLOW_ CI_WORKFLOW_NAME
Previous Commit CI_PREV_COMMIT_ CI_PREV_COMMIT_SHA, CI_PREV_COMMIT_BRANCH
Previous Pipeline CI_PREV_PIPELINE_ CI_PREV_PIPELINE_NUMBER, CI_PREV_PIPELINE_STATUS
System CI_SYSTEM_ CI_SYSTEM_NAME, CI_SYSTEM_URL, CI_SYSTEM_VERSION
Forge CI_FORGE_ CI_FORGE_TYPE, CI_FORGE_URL
Example
env := common.NewEnvironment()

// General
fmt.Println(env.Repo)              // e.g., "owner/repo"
fmt.Println(env.RepoPrivate)       // e.g., true

// Commit
fmt.Println(env.CommitSHA)         // e.g., "abc123..."
fmt.Println(env.CommitBranch)      // e.g., "main"
fmt.Println(env.CommitMessage)     // commit message

// Pipeline
fmt.Println(env.PipelineNumber)    // e.g., 42
fmt.Println(env.PipelineEvent)     // e.g., []string{"push"}

// System
fmt.Println(env.SystemName)        // e.g., "woodpecker"
fmt.Println(env.ForgeType)         // e.g., "github"

Performance

Caching Strategy

Struct metadata is compiled once via reflection per unique struct type and cached globally in a sync.Map. This means:

  • First call: ~microseconds (reflection overhead)
  • Subsequent calls: nanoseconds (cached metadata)
Benchmark Results
BenchmarkParseEnvironment-20    ~340,000 ops    ~3.3 µs/op    1104 B/op    3 allocs/op

Allocation breakdown:

  • 3 allocations from []string fields split via strings.Split() (PipelineEvent, PipelineFiles)
  • 0 allocations for all scalar field assignments (due to direct pointer writes)
Optimization Details

On the hot path, ParseEnvironment:

  1. Retrieves cached field metadata (lock-free read from sync.Map)
  2. Reads environment variables via os.LookupEnv() (OS call)
  3. Writes parsed values via unsafe pointer arithmetic (direct memory write)
  4. Zero reflection calls after first invocation

This design makes it suitable for high-frequency parsing scenarios.

Examples

Example 1: Web Server Configuration
package main

import (
    "log"
    "codefloe.com/actions/common"
)

type ServerConfig struct {
    Host    string        `env:"SERVER_HOST"`
    Port    int           `env:"SERVER_PORT"`
    TLS     bool          `env:"SERVER_TLS"`
    Timeout time.Duration `env:"SERVER_TIMEOUT"`
}

func main() {
    var cfg ServerConfig
    common.ParseEnvironment(&cfg)

    log.Printf("Server: %s:%d (TLS=%v, Timeout=%v)",
        cfg.Host, cfg.Port, cfg.TLS, cfg.Timeout)
}

Environment:

SERVER_HOST=0.0.0.0
SERVER_PORT=8080
SERVER_TLS=true
SERVER_TIMEOUT=30s
Example 2: Database with JSON Configuration
type DatabaseConfig struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Username string `json:"username"`
    Password string `json:"password"`
}

type AppConfig struct {
    AppName string           `env:"APP_NAME"`
    DB      DatabaseConfig   `env:"DB_CONFIG,object"`
    Debug   bool             `env:"DEBUG"`
}

func main() {
    var cfg AppConfig
    common.ParseEnvironment(&cfg)

    // Connect with cfg.DB.Host, cfg.DB.Port, etc.
}

Environment:

APP_NAME=MyService
DB_CONFIG='{"host":"db.local","port":5432,"username":"admin","password":"secret"}'
DEBUG=false
Example 3: Multiple Environments with Embedded Structs
type CommonConfig struct {
    Name string `env:"APP_NAME"`
    Env  string `env:"APP_ENV"`
}

type ProductionConfig struct {
    CommonConfig
    LogLevel string   `env:"LOG_LEVEL"`
    Replicas int      `env:"REPLICAS"`
}

func main() {
    var cfg ProductionConfig
    common.ParseEnvironment(&cfg)

    fmt.Printf("App: %s (env=%s, level=%s, replicas=%d)\n",
        cfg.Name, cfg.Env, cfg.LogLevel, cfg.Replicas)
}
Example 4: Feature Flags via JSON
type FeatureFlags struct {
    Flags map[string]bool `env:"FEATURE_FLAGS,object"`
}

func main() {
    var cfg FeatureFlags
    common.ParseEnvironment(&cfg)

    if cfg.Flags["newUI"] {
        // Use new UI
    }
}

Environment:

FEATURE_FLAGS='{"newUI":true,"betaAPI":false,"darkMode":true}'

License

Apache License 2.0. See LICENSE for details.

Documentation

Index

Constants

View Source
const (
	DefaultEndpoint = "https://codefloe.com"
)

Variables

This section is empty.

Functions

func Error

func Error(format string, args ...any)

func Fatal

func Fatal(format string, args ...any)

func Info

func Info(format string, args ...any)

func MaskPasswordWithStars

func MaskPasswordWithStars(password string, starCount int) string

func ParseEnvironment

func ParseEnvironment(dst any)

ParseEnvironment reads environment variables and populates struct fields tagged with `env:"VAR_NAME"`. The argument must be a non-nil pointer to a struct. Slice fields ([]string) are split by comma.

Types

type CommitEnvironment

type CommitEnvironment struct {
	CommitSHA               string `env:"CI_COMMIT_SHA" json:"CI_COMMIT_SHA"`
	CommitRef               string `env:"CI_COMMIT_REF" json:"CI_COMMIT_REF"`
	CommitRefSpec           string `env:"CI_COMMIT_REF_SPEC" json:"CI_COMMIT_REF_SPEC"`
	CommitBranch            string `env:"CI_COMMIT_BRANCH" json:"CI_COMMIT_BRANCH"`
	CommitSourceBranch      string `env:"CI_COMMIT_SOURCE_BRANCH" json:"CI_COMMIT_SOURCE_BRANCH"`
	CommitTargetBranch      string `env:"CI_COMMIT_TARGET_BRANCH" json:"CI_COMMIT_TARGET_BRANCH"`
	CommitTag               string `env:"CI_COMMIT_TAG" json:"CI_COMMIT_TAG"`
	CommitPullRequest       int64  `env:"CI_COMMIT_PULL_REQUEST" json:"CI_COMMIT_PULL_REQUEST"`
	CommitPullRequestLabels string `env:"CI_COMMIT_PULL_REQUEST_LABELS" json:"CI_COMMIT_PULL_REQUEST_LABELS"`
	CommitMessage           string `env:"CI_COMMIT_MESSAGE" json:"CI_COMMIT_MESSAGE"`
	CommitAuthor            string `env:"CI_COMMIT_AUTHOR" json:"CI_COMMIT_AUTHOR"`
	CommitAuthorEmail       string `env:"CI_COMMIT_AUTHOR_EMAIL" json:"CI_COMMIT_AUTHOR_EMAIL"`
	CommitPrerelease        bool   `env:"CI_COMMIT_PRERELEASE" json:"CI_COMMIT_PRERELEASE"`
}

type ForgeEnvironment

type ForgeEnvironment struct {
	ForgeType string `env:"CI_FORGE_TYPE" json:"CI_FORGE_TYPE"`
	ForgeURL  string `env:"CI_FORGE_URL" json:"CI_FORGE_URL"`
}

type GeneralEnvironment

type GeneralEnvironment struct {
	CI                  string `env:"CI" json:"CI"`
	Workspace           string `env:"CI_WORKSPACE" json:"CI_WORKSPACE"`
	Repo                string `env:"CI_REPO" json:"CI_REPO"`
	RepoOwner           string `env:"CI_REPO_OWNER" json:"CI_REPO_OWNER"`
	RepoName            string `env:"CI_REPO_NAME" json:"CI_REPO_NAME"`
	RepoRemoteID        string `env:"CI_REPO_REMOTE_ID" json:"CI_REPO_REMOTE_ID"`
	RepoURL             string `env:"CI_REPO_URL" json:"CI_REPO_URL"`
	RepoCloneURL        string `env:"CI_REPO_CLONE_URL" json:"CI_REPO_CLONE_URL"`
	RepoCloneSSHURL     string `env:"CI_REPO_CLONE_SSH_URL" json:"CI_REPO_CLONE_SSH_URL"`
	RepoDefaultBranch   string `env:"CI_REPO_DEFAULT_BRANCH" json:"CI_REPO_DEFAULT_BRANCH"`
	RepoPrivate         bool   `env:"CI_REPO_PRIVATE" json:"CI_REPO_PRIVATE"`
	RepoTrustedNetwork  bool   `env:"CI_REPO_TRUSTED_NETWORK" json:"CI_REPO_TRUSTED_NETWORK"`
	RepoTrustedVolumes  bool   `env:"CI_REPO_TRUSTED_VOLUMES" json:"CI_REPO_TRUSTED_VOLUMES"`
	RepoTrustedSecurity bool   `env:"CI_REPO_TRUSTED_SECURITY" json:"CI_REPO_TRUSTED_SECURITY"`
}

type PipelineEnvironment

type PipelineEnvironment struct {
	PipelineNumber       int64    `env:"CI_PIPELINE_NUMBER" json:"CI_PIPELINE_NUMBER"`
	PipelineParent       int64    `env:"CI_PIPELINE_PARENT" json:"CI_PIPELINE_PARENT"`
	PipelineEvent        []string `env:"CI_PIPELINE_EVENT" json:"CI_PIPELINE_EVENT"`
	PipelineURL          string   `env:"CI_PIPELINE_URL" json:"CI_PIPELINE_URL"`
	PipelineForgeURL     string   `env:"CI_PIPELINE_FORGE_URL" json:"CI_PIPELINE_FORGE_URL"`
	PipelineDeployTarget string   `env:"CI_PIPELINE_DEPLOY_TARGET" json:"CI_PIPELINE_DEPLOY_TARGET"`
	PipelineDeployTask   string   `env:"CI_PIPELINE_DEPLOY_TASK" json:"CI_PIPELINE_DEPLOY_TASK"`
	PipelineCreated      int64    `env:"CI_PIPELINE_CREATED" json:"CI_PIPELINE_CREATED"`
	PipelineStarted      int64    `env:"CI_PIPELINE_STARTED" json:"CI_PIPELINE_STARTED"`
	PipelineFiles        []string `env:"CI_PIPELINE_FILES" json:"CI_PIPELINE_FILES"`
	PipelineAuthor       string   `env:"CI_PIPELINE_AUTHOR" json:"CI_PIPELINE_AUTHOR"`
	PipelineAvatar       string   `env:"CI_PIPELINE_AVATAR" json:"CI_PIPELINE_AVATAR"`
}

type PreviousCommitEnvironment

type PreviousCommitEnvironment struct {
	PreviousCommitSHA          string `env:"CI_PREV_COMMIT_SHA" json:"CI_PREV_COMMIT_SHA"`
	PreviousCommitRef          string `env:"CI_PREV_COMMIT_REF" json:"CI_PREV_COMMIT_REF"`
	PreviousCommitRefSpec      string `env:"CI_PREV_COMMIT_REFSPEC" json:"CI_PREV_COMMIT_REFSPEC"`
	PreviousCommitBranch       string `env:"CI_PREV_COMMIT_BRANCH" json:"CI_PREV_COMMIT_BRANCH"`
	PreviousCommitSourceBranch string `env:"CI_PREV_COMMIT_SOURCE_BRANCH" json:"CI_PREV_COMMIT_SOURCE_BRANCH"`
	PreviousCommitTargetBranch string `env:"CI_PREV_COMMIT_TARGET_BRANCH" json:"CI_PREV_COMMIT_TARGET_BRANCH"`
	PreviousCommitURL          string `env:"CI_PREV_COMMIT_URL" json:"CI_PREV_COMMIT_URL"`
	PreviousCommitMessage      string `env:"CI_PREV_COMMIT_MESSAGE" json:"CI_PREV_COMMIT_MESSAGE"`
	PreviousCommitAuthor       string `env:"CI_PREV_COMMIT_AUTHOR" json:"CI_PREV_COMMIT_AUTHOR"`
	PreviousCommitAuthorEmail  string `env:"CI_PREV_COMMIT_AUTHOR_EMAIL" json:"CI_PREV_COMMIT_AUTHOR_EMAIL"`
}

type PreviousPipelineEnvironment

type PreviousPipelineEnvironment struct {
	PreviousPipelineNumber       int64    `env:"CI_PREV_PIPELINE_NUMBER" json:"CI_PREV_PIPELINE_NUMBER"`
	PreviousPipelineParent       int64    `env:"CI_PREV_PIPELINE_PARENT" json:"CI_PREV_PIPELINE_PARENT"`
	PreviousPipelineEvent        []string `env:"CI_PREV_PIPELINE_EVENT" json:"CI_PREV_PIPELINE_EVENT"`
	PreviousPipelineURL          string   `env:"CI_PREV_PIPELINE_URL" json:"CI_PREV_PIPELINE_URL"`
	PreviousPipelineForgeURL     string   `env:"CI_PREV_PIPELINE_FORGE_URL" json:"CI_PREV_PIPELINE_FORGE_URL"`
	PreviousPipelineDeployTarget string   `env:"CI_PREV_PIPELINE_DEPLOY_TARGET" json:"CI_PREV_PIPELINE_DEPLOY_TARGET"`
	PreviousPipelineDeployTask   string   `env:"CI_PREV_PIPELINE_DEPLOY_TASK" json:"CI_PREV_PIPELINE_DEPLOY_TASK"`
	PreviousPipelineStatus       string   `env:"CI_PREV_PIPELINE_STATUS" json:"CI_PREV_PIPELINE_STATUS"`
	PreviousPipelineCreated      int64    `env:"CI_PREV_PIPELINE_CREATED" json:"CI_PREV_PIPELINE_CREATED"`
	PreviousPipelineStarted      int64    `env:"CI_PREV_PIPELINE_STARTED" json:"CI_PREV_PIPELINE_STARTED"`
	PreviousPipelineFinished     int64    `env:"CI_PREV_PIPELINE_FINISHED" json:"CI_PREV_PIPELINE_FINISHED"`
	PreviousPipelineAuthor       string   `env:"CI_PREV_PIPELINE_AUTHOR" json:"CI_PREV_PIPELINE_AUTHOR"`
	PreviousPipelineAvatar       string   `env:"CI_PREV_PIPELINE_AVATAR" json:"CI_PREV_PIPELINE_AVATAR"`
}

type SystemEnvironment

type SystemEnvironment struct {
	SystemName    string `env:"CI_SYSTEM_NAME" json:"CI_SYSTEM_NAME"`
	SystemURL     string `env:"CI_SYSTEM_URL" json:"CI_SYSTEM_URL"`
	SystemHost    string `env:"CI_SYSTEM_HOST" json:"CI_SYSTEM_HOST"`
	SystemVersion string `env:"CI_SYSTEM_VERSION" json:"CI_SYSTEM_VERSION"`
}

type WorkflowEnvironment

type WorkflowEnvironment struct {
	WorkflowName string `env:"CI_WORKFLOW_NAME" json:"CI_WORKFLOW_NAME"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL