Initial commit: AetherForge Linux (forge-mesh) v0.1.0-dev
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
This commit is contained in:
0
cmd/forge/.gitkeep
Normal file
0
cmd/forge/.gitkeep
Normal file
122
cmd/forge/main.go
Normal file
122
cmd/forge/main.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"forge-mesh/internal/config"
|
||||
"forge-mesh/internal/db"
|
||||
"forge-mesh/internal/forge"
|
||||
)
|
||||
|
||||
const defaultVersion = "0.1.0-dev"
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case "build":
|
||||
runBuild(os.Args[2:])
|
||||
case "pubkey":
|
||||
runPubkey(os.Args[2:])
|
||||
default:
|
||||
printUsage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func runBuild(args []string) {
|
||||
fs := flag.NewFlagSet("build", flag.ExitOnError)
|
||||
configPath := fs.String("config", "data/config.json", "config path")
|
||||
version := fs.String("version", defaultVersion, "build version")
|
||||
public := fs.Bool("public", true, "mark builds as public")
|
||||
_ = fs.Parse(args)
|
||||
|
||||
cfg, err := config.Load(*configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("config: %v", err)
|
||||
}
|
||||
if err := cfg.EnsureDataDirs(); err != nil {
|
||||
log.Fatalf("data dirs: %v", err)
|
||||
}
|
||||
|
||||
conn, err := db.Open(cfg.DatabasePath)
|
||||
if err != nil {
|
||||
log.Fatalf("database: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
root := moduleRoot()
|
||||
agentPath := filepath.Join(root, "cmd", "agent")
|
||||
pipe, err := forge.NewPipeline(conn, cfg.Forge.ArtifactsDir, cfg.Forge.SigningKeyPath, agentPath, *version)
|
||||
if err != nil {
|
||||
log.Fatalf("pipeline: %v", err)
|
||||
}
|
||||
|
||||
builds, err := pipe.BuildAll(*public)
|
||||
if err != nil {
|
||||
log.Fatalf("build: %v", err)
|
||||
}
|
||||
|
||||
for _, b := range builds {
|
||||
fmt.Printf("built %s/%s id=%s checksum=%s path=%s\n", b.OS, b.Arch, b.ID, b.Checksum, b.Path)
|
||||
}
|
||||
fmt.Printf("public key: %s\n", pipe.PublicKey())
|
||||
}
|
||||
|
||||
func runPubkey(args []string) {
|
||||
fs := flag.NewFlagSet("pubkey", flag.ExitOnError)
|
||||
configPath := fs.String("config", "data/config.json", "config path")
|
||||
_ = fs.Parse(args)
|
||||
|
||||
cfg, err := config.Load(*configPath)
|
||||
if err != nil {
|
||||
log.Fatalf("config: %v", err)
|
||||
}
|
||||
|
||||
kp, err := forge.LoadOrCreateKey(cfg.Forge.SigningKeyPath)
|
||||
if err != nil {
|
||||
log.Fatalf("key: %v", err)
|
||||
}
|
||||
fmt.Println(kp.PublicKeyHex())
|
||||
}
|
||||
|
||||
func moduleRoot() string {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "."
|
||||
}
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
|
||||
if _, err := os.Stat(filepath.Join(dir, "cmd", "agent")); err == nil {
|
||||
return dir
|
||||
}
|
||||
}
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
break
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
wd, _ := os.Getwd()
|
||||
return wd
|
||||
}
|
||||
|
||||
func printUsage() {
|
||||
fmt.Fprintf(os.Stderr, `forge-mesh forge CLI
|
||||
|
||||
Usage:
|
||||
forge build [--config data/config.json] [--version 0.1.0-dev] [--public]
|
||||
forge pubkey [--config data/config.json]
|
||||
|
||||
Cross-compiles linux/amd64 and linux/arm64 agent binaries, ed25519-signs artifacts,
|
||||
and records builds in SQLite (--public marks builds served on /api/v1/public/*).
|
||||
|
||||
`)
|
||||
}
|
||||
241
cmd/forge/main_test.go
Normal file
241
cmd/forge/main_test.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"forge-mesh/internal/db"
|
||||
"forge-mesh/internal/forge"
|
||||
)
|
||||
|
||||
func TestForgeBuildCrossCompileAndSign(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfgPath := writeForgeConfig(t, dir)
|
||||
bin := buildForgeBinary(t)
|
||||
|
||||
out, err := exec.Command(bin, "build",
|
||||
"--config", cfgPath,
|
||||
"--version", "test-1.0",
|
||||
"--public",
|
||||
).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("forge build: %v\n%s", err, out)
|
||||
}
|
||||
|
||||
output := string(out)
|
||||
if !strings.Contains(output, "linux/amd64") || !strings.Contains(output, "linux/arm64") {
|
||||
t.Fatalf("expected amd64 and arm64 builds in output:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, "checksum=") {
|
||||
t.Fatalf("expected checksum in build output:\n%s", output)
|
||||
}
|
||||
if !strings.Contains(output, "public key:") {
|
||||
t.Fatalf("expected public key in build output:\n%s", output)
|
||||
}
|
||||
|
||||
artifactsDir := filepath.Join(dir, "artifacts")
|
||||
for _, arch := range []string{"amd64", "arm64"} {
|
||||
path := filepath.Join(artifactsDir, "forge-mesh-agent-linux-"+arch)
|
||||
assertSignedArtifact(t, path, cfgPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgeBuildPublicFlag(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfgPath := writeForgeConfig(t, dir)
|
||||
bin := buildForgeBinary(t)
|
||||
|
||||
if out, err := exec.Command(bin, "build", "--config", cfgPath, "--public=false").CombinedOutput(); err != nil {
|
||||
t.Fatalf("forge build private: %v\n%s", err, out)
|
||||
}
|
||||
|
||||
conn := openDB(t, filepath.Join(dir, "forge-mesh.db"))
|
||||
defer conn.Close()
|
||||
|
||||
var publicCount int
|
||||
if err := conn.QueryRow(`SELECT COUNT(*) FROM builds WHERE public = 1`).Scan(&publicCount); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if publicCount != 0 {
|
||||
t.Fatalf("expected no public builds with --public=false, got %d", publicCount)
|
||||
}
|
||||
|
||||
if out, err := exec.Command(bin, "build", "--config", cfgPath, "--public").CombinedOutput(); err != nil {
|
||||
t.Fatalf("forge build public: %v\n%s", err, out)
|
||||
}
|
||||
if err := conn.QueryRow(`SELECT COUNT(*) FROM builds WHERE public = 1`).Scan(&publicCount); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if publicCount < 2 {
|
||||
t.Fatalf("expected public builds after --public, got %d", publicCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgePubkey(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfgPath := writeForgeConfig(t, dir)
|
||||
bin := buildForgeBinary(t)
|
||||
|
||||
out, err := exec.Command(bin, "pubkey", "--config", cfgPath).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("forge pubkey: %v\n%s", err, out)
|
||||
}
|
||||
|
||||
hexKey := strings.TrimSpace(string(out))
|
||||
if len(hexKey) != 64 {
|
||||
t.Fatalf("expected 64-char ed25519 pubkey hex, got %q", hexKey)
|
||||
}
|
||||
|
||||
cfg := readForgeConfigFile(t, cfgPath)
|
||||
kp, err := forge.LoadOrCreateKey(cfg.Forge.SigningKeyPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if kp.PublicKeyHex() != hexKey {
|
||||
t.Fatalf("pubkey mismatch: CLI %s key file %s", hexKey, kp.PublicKeyHex())
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgeUsageExitsNonZero(t *testing.T) {
|
||||
bin := buildForgeBinary(t)
|
||||
cmd := exec.Command(bin)
|
||||
err := cmd.Run()
|
||||
if err == nil {
|
||||
t.Fatal("expected non-zero exit without subcommand")
|
||||
}
|
||||
}
|
||||
|
||||
func assertSignedArtifact(t *testing.T, path, cfgPath string) {
|
||||
t.Helper()
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
t.Fatalf("artifact %s: %v", path, err)
|
||||
}
|
||||
if info.Size() == 0 {
|
||||
t.Fatalf("empty artifact %s", path)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sum := sha256.Sum256(data)
|
||||
checksum := hex.EncodeToString(sum[:])
|
||||
|
||||
cfg := readForgeConfigFile(t, cfgPath)
|
||||
kp, err := forge.LoadOrCreateKey(cfg.Forge.SigningKeyPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sig := kp.Sign(data)
|
||||
if !forge.Verify(kp.Public, data, sig) {
|
||||
t.Fatalf("artifact %s failed ed25519 verification", path)
|
||||
}
|
||||
|
||||
conn := openDB(t, cfg.DatabasePath)
|
||||
defer conn.Close()
|
||||
|
||||
var dbChecksum, dbSig string
|
||||
err = conn.QueryRow(`
|
||||
SELECT checksum, signature FROM builds
|
||||
WHERE path = ? ORDER BY created_at DESC LIMIT 1
|
||||
`, path).Scan(&dbChecksum, &dbSig)
|
||||
if err != nil {
|
||||
t.Fatalf("build row for %s: %v", path, err)
|
||||
}
|
||||
if dbChecksum != checksum {
|
||||
t.Fatalf("checksum mismatch for %s: db %s file %s", path, dbChecksum, checksum)
|
||||
}
|
||||
if !forge.Verify(kp.Public, data, dbSig) {
|
||||
t.Fatalf("db signature invalid for %s", path)
|
||||
}
|
||||
}
|
||||
|
||||
func buildForgeBinary(t *testing.T) string {
|
||||
t.Helper()
|
||||
out := filepath.Join(t.TempDir(), "forge-mesh-forge")
|
||||
cmd := exec.Command("go", "build", "-o", out, "./cmd/forge")
|
||||
cmd.Dir = repoRoot(t)
|
||||
if outBytes, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("build forge: %v\n%s", err, outBytes)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func writeForgeConfig(t *testing.T, dir string) string {
|
||||
t.Helper()
|
||||
cfgPath := filepath.Join(dir, "config.json")
|
||||
content := fmt.Sprintf(`{
|
||||
"listen_addr": ":0",
|
||||
"data_dir": %q,
|
||||
"database_path": %q,
|
||||
"auth": {
|
||||
"fleet_secret": "forge-test-secret"
|
||||
},
|
||||
"forge": {
|
||||
"signing_key_path": %q,
|
||||
"artifacts_dir": %q
|
||||
}
|
||||
}`, dir, filepath.Join(dir, "forge-mesh.db"),
|
||||
filepath.Join(dir, "signing.key"), filepath.Join(dir, "artifacts"))
|
||||
if err := os.WriteFile(cfgPath, []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return cfgPath
|
||||
}
|
||||
|
||||
type forgeConfigSnippet struct {
|
||||
DatabasePath string `json:"database_path"`
|
||||
Forge struct {
|
||||
SigningKeyPath string `json:"signing_key_path"`
|
||||
} `json:"forge"`
|
||||
}
|
||||
|
||||
func readForgeConfigFile(t *testing.T, path string) forgeConfigSnippet {
|
||||
t.Helper()
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var cfg forgeConfigSnippet
|
||||
if err := json.Unmarshal(data, &cfg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
|
||||
func openDB(t *testing.T, path string) *sql.DB {
|
||||
t.Helper()
|
||||
conn, err := db.Open(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
func repoRoot(t *testing.T) string {
|
||||
t.Helper()
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(wd, "go.mod")); err == nil {
|
||||
return wd
|
||||
}
|
||||
parent := filepath.Dir(wd)
|
||||
if parent == wd {
|
||||
t.Fatal("go.mod not found")
|
||||
}
|
||||
wd = parent
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user