init
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
// Copyright (C) 2026 Dcntrlized
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// Package config is the complete, environment-driven runtime configuration of
|
||||
// the license bot. The binary takes no arguments: the container image carries
|
||||
// no secrets and the deployment decides everything through the environment.
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config is the complete runtime configuration of the bot.
|
||||
type Config struct {
|
||||
// GiteaURL is the instance base URL the bot calls, e.g. http://gitea:3000
|
||||
// on an instance whose Gitea shares this Docker daemon. Deliberately
|
||||
// configured here instead of taken from webhook payloads: the payload's
|
||||
// clone_url follows the instance ROOT_URL, which may be stale.
|
||||
GiteaURL string
|
||||
// PublicURL is the instance URL a *human* should follow, used only for the
|
||||
// links in the bot's PR comments. On an instance reached over the internal
|
||||
// alias (GITEA_URL=http://gitea:3000) that alias is not resolvable from a
|
||||
// browser, so the comment must link the public name instead. Defaults to
|
||||
// GiteaURL.
|
||||
PublicURL string
|
||||
// Token is a bot-account token with write access to the target repositories.
|
||||
// It lives only in this process's environment -- never in a workflow.
|
||||
Token string
|
||||
// WebhookSecret verifies X-Gitea-Signature on incoming webhook deliveries.
|
||||
// Webhooks are the only trigger, so this is required.
|
||||
WebhookSecret string
|
||||
// Repos is the allowlist of owner/name repositories the bot may touch.
|
||||
Repos []string
|
||||
// Listen is the HTTP listen address (webhook + health route).
|
||||
Listen string
|
||||
// BotName/BotEmail identify the licensing commit's committer.
|
||||
BotName string
|
||||
BotEmail string
|
||||
// Holder is passed to addlicense -c.
|
||||
Holder string
|
||||
// AddlicenseBin is the addlicense executable (in PATH by default).
|
||||
AddlicenseBin string
|
||||
// WorkRoot is where per-PR git worktrees are created.
|
||||
WorkRoot string
|
||||
// DryRun inspects and comments but never commits or pushes.
|
||||
DryRun bool
|
||||
// CommentMarker identifies the bot's own PR comments so they can be
|
||||
// replaced (deleted and reposted, never edited in place: Gitea keeps a
|
||||
// comment's position, so an edited message stays buried in the timeline).
|
||||
CommentMarker string
|
||||
// StatusContext is the commit-status context the bot posts and the merge
|
||||
// gate requires. It must match the branch-protection string byte for byte.
|
||||
StatusContext string
|
||||
}
|
||||
|
||||
// FromEnv builds the configuration from the process environment.
|
||||
func FromEnv() (Config, error) {
|
||||
c := Config{
|
||||
GiteaURL: strings.TrimRight(env("GITEA_URL", ""), "/"),
|
||||
PublicURL: strings.TrimRight(env("GITEA_PUBLIC_URL", ""), "/"),
|
||||
Token: os.Getenv("GITEA_TOKEN"),
|
||||
WebhookSecret: os.Getenv("WEBHOOK_SECRET"),
|
||||
Repos: splitList(os.Getenv("REPOS")),
|
||||
Listen: env("LISTEN", ":8080"),
|
||||
BotName: env("BOT_NAME", "Dcntrlized Auto CopyLeft Bot"),
|
||||
BotEmail: env("BOT_EMAIL", "[email protected]"),
|
||||
Holder: env("HOLDER", "Dcntrlized"),
|
||||
AddlicenseBin: env("ADDLICENSE_BIN", "addlicense"),
|
||||
WorkRoot: env("WORK_ROOT", os.TempDir()),
|
||||
DryRun: truthy(os.Getenv("DRY_RUN")),
|
||||
CommentMarker: env("COMMENT_MARKER", "<!-- dcntrlized-copyleft-bot -->"),
|
||||
StatusContext: env("STATUS_CONTEXT", "license-bot / addlicense"),
|
||||
}
|
||||
|
||||
if c.Token == "" {
|
||||
return c, fmt.Errorf("GITEA_TOKEN is required")
|
||||
}
|
||||
if len(c.Repos) == 0 {
|
||||
return c, fmt.Errorf("REPOS is required (comma-separated owner/name list)")
|
||||
}
|
||||
if c.WebhookSecret == "" {
|
||||
return c, fmt.Errorf("WEBHOOK_SECRET is required: webhooks are the only trigger")
|
||||
}
|
||||
if c.PublicURL == "" {
|
||||
c.PublicURL = c.GiteaURL
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Allows reports whether repo (owner/name) is on the allowlist.
|
||||
func (c Config) Allows(repo string) bool {
|
||||
for _, r := range c.Repos {
|
||||
if strings.EqualFold(r, repo) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func env(key, def string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func truthy(v string) bool {
|
||||
return v == "1" || strings.EqualFold(v, "true") || strings.EqualFold(v, "yes")
|
||||
}
|
||||
|
||||
func splitList(v string) []string {
|
||||
var out []string
|
||||
for _, p := range strings.Split(v, ",") {
|
||||
if p = strings.TrimSpace(p); p != "" {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user