Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Package config loads CLI configuration from file and environment.
package config

import (
"os"
"path/filepath"
"strconv"

"github.com/github/gh-actions-pin/internal/tag"
"gopkg.in/yaml.v3"
)

// Config holds process-wide settings loaded from the config file and
// environment variables. Create one via LoadConfig at startup and pass
// it through the pipeline.
type Config struct {
// Path is the resolved config file path (empty if none found).
Path string
// Cooldown controls how recently a tag must have been released to
// be excluded from upgrade suggestions.
Cooldown tag.CooldownConfig
// Workers is the concurrency limit for pool-parallelized phases.
// Defaults to 8; overridden by GH_ACTIONS_PIN_WORKERS.
Workers int
// StallHintMS is the stall-detection threshold in milliseconds.
// 0 disables the watcher. Overridden by GH_ACTIONS_PIN_STALL_HINT_MS.
StallHintMS int
// DebugProgress enables per-phase progress tracing.
DebugProgress bool
}

// Load reads the config file and environment, returning a Config
// with sensible defaults for any unset values.
func Load() Config {
p := configPath()
c := Config{
Path: p,
Workers: 8,
StallHintMS: -1, // sentinel: use pinpool default
DebugProgress: envBool("GH_ACTIONS_PIN_DEBUG_PROGRESS"),
}

if v, err := strconv.Atoi(os.Getenv("GH_ACTIONS_PIN_WORKERS")); err == nil && v > 0 {
c.Workers = v
}
if v := os.Getenv("GH_ACTIONS_PIN_STALL_HINT_MS"); v != "" {
if ms, err := strconv.Atoi(v); err == nil {
c.StallHintMS = ms
}
}

c.Cooldown = loadCooldownFromFile(p)
return c
}

func envBool(key string) bool {
v := os.Getenv(key)
return v == "1" || v == "true" || v == "yes"
}

// loadCooldownFromFile reads cooldown settings from the config file.
func loadCooldownFromFile(path string) tag.CooldownConfig {
cfg := tag.CooldownConfig{
DefaultDays: 3,
RepoOverrides: make(map[string]int),
}
if path == "" {
return cfg
}
data, err := os.ReadFile(path)
if err != nil {
return cfg
}
var file struct {
CooldownDays int `yaml:"cooldown_days"`
Repos map[string]struct {
CooldownDays int `yaml:"cooldown_days"`
} `yaml:"repos"`
}
if err := yaml.Unmarshal(data, &file); err != nil {
return cfg
}
if file.CooldownDays > 0 {
cfg.DefaultDays = file.CooldownDays
}
for nwo, repoCfg := range file.Repos {
if repoCfg.CooldownDays >= 0 {
cfg.RepoOverrides[nwo] = repoCfg.CooldownDays
}
}
return cfg
}

// configPath returns the path to the config file, respecting
// GH_ACTIONS_PIN_CONFIG for testing/demos.
func configPath() string {
if p := os.Getenv("GH_ACTIONS_PIN_CONFIG"); p != "" {
return p
}
home, err := os.UserHomeDir()
if err != nil {
return ""
}
return filepath.Join(home, ".config", "gh-actions-pin", "config.yml")
}
198 changes: 0 additions & 198 deletions internal/doctor/apply.go

This file was deleted.

Loading
Loading