feat: publish Tend v0.2 preview source

Publish the reviewed allowlisted snapshot whose exact binary completed maintenance deployment, rollback, and reactivation exercises for Gamertan and Sandwich Hime.

Private-Source-Commit: 4d7094c8b7c61991bfb67b11fc1558724c874eb2

Private-Source-Tree: 54a2f74804f7acddf3755d7d4da5b97f5fc28381

AI-Assistance: OpenAI Codex assisted implementation, testing, security review, and release verification.
Signed-off-by: Cole Speelman <crspeelman@gmail.com>
This commit is contained in:
2026-08-16 19:02:08 -04:00
parent b2cc4482f6
commit 00d1dd4209
47 changed files with 1590 additions and 124 deletions
+45 -25
View File
@@ -9,13 +9,17 @@ import (
"fmt"
"io"
"net/netip"
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
)
const SchemaVersion = 1
const (
SchemaVersion = 2
SharedLockFile = "/run/lock/tend-deploy.lock"
)
var (
namePattern = regexp.MustCompile(`^[a-z][a-z0-9-]{1,62}$`)
@@ -33,8 +37,9 @@ type Config struct {
}
type Service struct {
Name string `json:"name"`
AllowedHost string `json:"allowed_host"`
Name string `json:"name"`
AllowedHost string `json:"allowed_host"`
EnvironmentFile string `json:"environment_file"`
}
type Build struct {
@@ -47,16 +52,17 @@ type Build struct {
}
type Deployment struct {
Strategy string `json:"strategy"`
Root string `json:"root"`
LockFile string `json:"lock_file"`
StateFile string `json:"state_file"`
HealthPath string `json:"health_path"`
ReadinessPath string `json:"readiness_path"`
CandidateTimeoutSecs int `json:"candidate_timeout_seconds"`
Smoke []Smoke `json:"smoke"`
BlueGreen *BlueGreen `json:"blue_green,omitempty"`
Singleton *Singleton `json:"singleton,omitempty"`
Strategy string `json:"strategy"`
Root string `json:"root"`
LockFile string `json:"lock_file"`
StateFile string `json:"state_file"`
HealthPath string `json:"health_path"`
ReadinessPath string `json:"readiness_path"`
CandidateTimeoutSecs int `json:"candidate_timeout_seconds"`
Smoke []Smoke `json:"smoke"`
PublicSmoke []PublicSmoke `json:"public_smoke"`
BlueGreen *BlueGreen `json:"blue_green,omitempty"`
Singleton *Singleton `json:"singleton,omitempty"`
}
type Smoke struct {
@@ -64,6 +70,11 @@ type Smoke struct {
Contains string `json:"contains"`
}
type PublicSmoke struct {
URL string `json:"url"`
Contains string `json:"contains"`
}
type BlueGreen struct {
CaddyConfig string `json:"caddy_config"`
CaddyHandler string `json:"caddy_handler"`
@@ -80,13 +91,12 @@ type Slot struct {
}
type Singleton struct {
Unit string `json:"unit"`
Address string `json:"address"`
CandidateAddress string `json:"candidate_address"`
ListenEnv string `json:"listen_env"`
Environment map[string]string `json:"environment,omitempty"`
CurrentLink string `json:"current_link"`
PreviousLink string `json:"previous_link"`
Unit string `json:"unit"`
Address string `json:"address"`
CandidateAddress string `json:"candidate_address"`
ListenEnv string `json:"listen_env"`
CurrentLink string `json:"current_link"`
PreviousLink string `json:"previous_link"`
}
func Load(path string) (Config, error) {
@@ -140,6 +150,12 @@ func (c Config) Validate() error {
if c.Service.AllowedHost == "" || strings.ContainsAny(c.Service.AllowedHost, "/\\\x00\r\n\t ") {
return errors.New("service.allowed_host is invalid")
}
if err := safeAbsolute("service.environment_file", c.Service.EnvironmentFile); err != nil {
return err
}
if !within("/etc/tend/environment", c.Service.EnvironmentFile) {
return errors.New("service.environment_file must be below /etc/tend/environment")
}
if !packagePattern.MatchString(c.Build.Package) || strings.Contains(c.Build.Package, "..") {
return errors.New("build.package must be a local package without traversal")
}
@@ -185,6 +201,15 @@ func (c Config) Validate() error {
return fmt.Errorf("deployment.smoke[%d] is invalid", i)
}
}
if len(d.PublicSmoke) == 0 || len(d.PublicSmoke) > 16 {
return errors.New("deployment.public_smoke must contain 1 to 16 checks")
}
for i, smoke := range d.PublicSmoke {
parsed, err := url.Parse(smoke.URL)
if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil || parsed.Fragment != "" || parsed.RawQuery != "" || parsed.Opaque != "" || smoke.Contains == "" || len(smoke.Contains) > 4096 || strings.ContainsRune(smoke.Contains, '\x00') {
return fmt.Errorf("deployment.public_smoke[%d] is invalid", i)
}
}
switch d.Strategy {
case "blue_green":
if d.BlueGreen == nil || d.Singleton != nil {
@@ -273,11 +298,6 @@ func validateSingleton(root string, s Singleton) error {
if s.CurrentLink == s.PreviousLink {
return errors.New("current and previous links must differ")
}
for key, value := range s.Environment {
if !regexp.MustCompile(`^[A-Z][A-Z0-9_]{0,63}$`).MatchString(key) || strings.ContainsAny(value, "\x00\r\n") {
return fmt.Errorf("singleton environment entry %q is invalid", key)
}
}
return nil
}