krz/gitbay
A CLI-first git forge.
clone: git clone https://gitbay.org/krz/gitbay.git
24c0aad9bf75f925497b0f6de2c8e831e67eaac9
verified · cmc
author: Christian Cleberg <hello@cleberg.net> · 2026-08-24T00:14:19Z
.gitignore | 4 ++-- cmd/{forge => gitbay}/local.go | 36 ++++++++++++++++++------------------ cmd/{forge => gitbay}/main.go | 24 ++++++++++++------------ cmd/{forge => gitbay}/ssh.go | 8 ++++---- cmd/{forged => gitbayd}/hook.go | 10 +++++----- cmd/{forged => gitbayd}/main.go | 30 +++++++++++++++--------------- cmd/{forged => gitbayd}/system.go | 10 +++++----- e2e/cli_test.go | 14 +++++++------- e2e/http_test.go | 2 +- e2e/mr_test.go | 2 +- e2e/sig_test.go | 4 ++-- e2e/ssh_test.go | 26 +++++++++++++------------- e2e/system_test.go | 8 ++++---- e2e/web_test.go | 2 +- go.mod | 2 +- internal/cliconfig/cliconfig.go | 6 +++--- internal/cliconfig/cliconfig_test.go | 20 ++++++++++---------- internal/config/config.go | 6 +++--- internal/config/config_test.go | 12 ++++++------ internal/control/control.go | 6 +++--- internal/control/control_test.go | 2 +- internal/control/identity.go | 4 ++-- internal/control/import.go | 10 +++++----- internal/control/issue.go | 6 +++--- internal/control/mr.go | 8 ++++---- internal/control/repo.go | 8 ++++---- internal/control/sig.go | 10 +++++----- internal/control/web.go | 4 ++-- internal/gitd/gitd.go | 6 +++--- internal/gitutil/gitutil.go | 2 +- internal/gitutil/merge.go | 2 +- internal/hookd/hookd.go | 28 ++++++++++++++-------------- internal/httpd/accounts.go | 10 +++++----- internal/httpd/routes_test.go | 4 ++-- internal/httpd/smart.go | 6 +++--- internal/httpd/web.go | 12 ++++++------ internal/policy/access.go | 2 +- internal/policy/access_test.go | 2 +- internal/sshd/sshd.go | 20 ++++++++++---------- internal/store/signatures.go | 2 +- internal/store/store_test.go | 2 +- 41 files changed, 191 insertions(+), 191 deletions(-) @@ -1,3 +1,3 @@ -/forge -/forged +/gitbay +/gitbayd *.db similarity index 87% rename from cmd/forge/local.go rename to cmd/gitbay/local.go @@ -9,8 +9,8 @@ import ( "golang.org/x/term" - "github.com/krazywarez/forge/internal/cliconfig" - "github.com/krazywarez/forge/internal/protocol" + "gitbay.org/gitbay/internal/cliconfig" + "gitbay.org/gitbay/internal/protocol" ) // hasBodyFlag reports whether args already carry body/message input. @@ -37,7 +37,7 @@ func maybeEditor(args []string, kind string) ([]string, *strings.Reader, bool, e // failing — bodies are optional everywhere. return args, nil, true, nil } - f, err := os.CreateTemp("", "forge-"+kind+"-*.md") + f, err := os.CreateTemp("", "gitbay-"+kind+"-*.md") if err != nil { return nil, nil, false, err } @@ -75,7 +75,7 @@ func runGitLocal(args ...string) int { if ee, ok := err.(*exec.ExitError); ok { return ee.ExitCode() } - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } return 0 @@ -84,12 +84,12 @@ func runGitLocal(args ...string) int { // cmdRepoClone implements `forge repo clone <owner/name> [dir]`. func cmdRepoClone(args []string) int { if len(args) < 1 || strings.HasPrefix(args[0], "-") { - fmt.Fprintln(os.Stderr, "usage: forge repo clone <owner/name> [dir]") + fmt.Fprintln(os.Stderr, "usage: gitbay repo clone <owner/name> [dir]") return protocol.ExitUsage } t, err := resolveTarget() if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } gitArgs := append([]string{"clone", t.inst.CloneURL(args[0])}, args[1:]...) @@ -103,7 +103,7 @@ func cmdRepoClone(args []string) int { // origin and check it out as a local branch. func cmdMRCheckout(args []string) int { if len(args) != 1 { - fmt.Fprintln(os.Stderr, "usage: forge mr checkout <n>") + fmt.Fprintln(os.Stderr, "usage: gitbay mr checkout <n>") return protocol.ExitUsage } n := args[0] @@ -124,7 +124,7 @@ func cmdInit(args []string) int { case a == "--private": private = true case strings.HasPrefix(a, "-"): - fmt.Fprintln(os.Stderr, "usage: forge init [name] [--private]") + fmt.Fprintln(os.Stderr, "usage: gitbay init [name] [--private]") return protocol.ExitUsage default: name = a @@ -133,7 +133,7 @@ func cmdInit(args []string) int { if name == "" { wd, err := os.Getwd() if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } name = filepath.Base(wd) @@ -141,12 +141,12 @@ func cmdInit(args []string) int { cfg, err := cliconfig.Load() if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } inst, _, err := cfg.DefaultInstance() if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } t := target{inst: inst} @@ -196,7 +196,7 @@ func captureSSH(t target, serverArgv []string) (string, int) { if ee, ok := err.(*exec.ExitError); ok { return "", ee.ExitCode() } - fmt.Fprintln(os.Stderr, "forge: running ssh:", err) + fmt.Fprintln(os.Stderr, "gitbay: running ssh:", err) return "", protocol.ExitProtocol } return string(out), 0 @@ -210,7 +210,7 @@ func quoteAll(args []string) []string { return out } -// cmdRemoteAdd implements `forge remote add <name> <host> [flags]`. +// cmdRemoteAdd implements `gitbay remote add <name> <host> [flags]`. func cmdRemoteAdd(args []string) int { var name, host, user string var port int @@ -250,19 +250,19 @@ func cmdRemoteAdd(args []string) int { } else if host == "" { host = a } else { - fmt.Fprintln(os.Stderr, "usage: forge remote add <name> <host> [--port n] [--user u] [--ssh-option opt]... [--default]") + fmt.Fprintln(os.Stderr, "usage: gitbay remote add <name> <host> [--port n] [--user u] [--ssh-option opt]... [--default]") return protocol.ExitUsage } i++ } } if name == "" || host == "" { - fmt.Fprintln(os.Stderr, "usage: forge remote add <name> <host> [--port n] [--user u] [--ssh-option opt]... [--default]") + fmt.Fprintln(os.Stderr, "usage: gitbay remote add <name> <host> [--port n] [--user u] [--ssh-option opt]... [--default]") return protocol.ExitUsage } cfg, err := cliconfig.Load() if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } cfg.Instances[name] = cliconfig.Instance{Host: host, Port: port, User: user, SSHOptions: sshOptions} @@ -270,7 +270,7 @@ func cmdRemoteAdd(args []string) int { cfg.Default = name } if err := cliconfig.Save(cfg); err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } fmt.Printf("added instance %s (%s)\n", name, host) @@ -280,7 +280,7 @@ func cmdRemoteAdd(args []string) int { func cmdRemoteList() int { cfg, err := cliconfig.Load() if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } for name, inst := range cfg.Instances { similarity index 92% rename from cmd/forge/main.go rename to cmd/gitbay/main.go @@ -13,12 +13,12 @@ import ( "github.com/spf13/cobra" "github.com/spf13/cobra/doc" - "github.com/krazywarez/forge/internal/protocol" + "gitbay.org/gitbay/internal/protocol" ) func main() { root := &cobra.Command{ - Use: "forge", + Use: "gitbay", Short: "CLI-first git forge client", SilenceUsage: true, SilenceErrors: true, @@ -36,7 +36,7 @@ func main() { ) if err := root.Execute(); err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) os.Exit(protocol.ExitUsage) } } @@ -72,13 +72,13 @@ func pass(use, short string, o passOpts) *cobra.Command { func runPass(o passOpts, args []string) int { t, err := resolveTarget() if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } if o.needsRepo { args, err = withRepo(t, args) if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitUsage } } @@ -87,7 +87,7 @@ func runPass(o passOpts, args []string) int { if o.editor != "" { extended, body, ok, err := maybeEditor(args, o.editor) if err != nil { - fmt.Fprintln(os.Stderr, "forge:", err) + fmt.Fprintln(os.Stderr, "gitbay:", err) return protocol.ExitFailure } if !ok { @@ -186,14 +186,14 @@ func authCmd() *cobra.Command { func repoCmd() *cobra.Command { return group("repo", "create and manage repositories", - pass("create", "create a repository: forge repo create <owner/name> [--private]", + pass("create", "create a repository: gitbay repo create <owner/name> [--private]", passOpts{server: []string{"repo", "create"}}), pass("list", "list repositories you own or can access", passOpts{server: []string{"repo", "list"}}), pass("show", "show repository details", passOpts{server: []string{"repo", "show"}, needsRepo: true}), pass("log", "commit log with signature states", passOpts{server: []string{"repo", "log"}, needsRepo: true}), pass("delete", "delete a repository (--yes)", passOpts{server: []string{"repo", "delete"}, needsRepo: true}), pass("fork", "fork a repository under your account", passOpts{server: []string{"repo", "fork"}, needsRepo: true}), - local("clone", "clone via ssh: forge repo clone <owner/name> [dir]", cmdRepoClone), + local("clone", "clone via ssh: gitbay repo clone <owner/name> [dir]", cmdRepoClone), importCmd(), group("access", "manage access grants", pass("grant", "grant access: ... <user> read|write|admin", passOpts{server: []string{"repo", "access", "grant"}, needsRepo: true}), @@ -232,7 +232,7 @@ func mrCmd() *cobra.Command { pass("list", "list merge requests [--state ...]", passOpts{server: []string{"mr", "list"}, needsRepo: true}), pass("show", "show a merge request", passOpts{server: []string{"mr", "show"}, needsRepo: true}), pass("diff", "show the diff", passOpts{server: []string{"mr", "diff"}, needsRepo: true}), - local("checkout", "fetch and check out the MR head locally: forge mr checkout <n>", cmdMRCheckout), + local("checkout", "fetch and check out the MR head locally: gitbay mr checkout <n>", cmdMRCheckout), pass("comment", "comment on a merge request", passOpts{server: []string{"mr", "comment"}, needsRepo: true, stdinOK: true, editor: "comment"}), pass("review", "review: --approve|--request-changes|--comment", passOpts{server: []string{"mr", "review"}, needsRepo: true}), pass("merge", "merge (fast-forward or merge-commit): [--strategy ff|merge]", passOpts{server: []string{"mr", "merge"}, needsRepo: true}), @@ -244,7 +244,7 @@ func mrCmd() *cobra.Command { func importCmd() *cobra.Command { return &cobra.Command{ Use: "import", - Short: "server-side mirror of a foreign repo: forge repo import <owner/name> --from <url> [--private] [--token-stdin]", + Short: "server-side mirror of a foreign repo: gitbay repo import <owner/name> --from <url> [--private] [--token-stdin]", DisableFlagParsing: true, RunE: func(cmd *cobra.Command, args []string) error { for _, a := range args { @@ -283,14 +283,14 @@ func webCmd() *cobra.Command { func remoteCmd() *cobra.Command { return group("remote", "local instance profiles (no server contact)", - local("add", "add a named forge instance: forge remote add <name> <host> [--port n] [--user u] [--ssh-option o]... [--default]", + local("add", "add a named gitbay instance: gitbay remote add <name> <host> [--port n] [--user u] [--ssh-option o]... [--default]", cmdRemoteAdd), local("list", "list configured instances", func([]string) int { return cmdRemoteList() }), ) } func initCmd() *cobra.Command { - return local("init", "git init + repo create + set origin, in one step: forge init [name] [--private]", cmdInit) + return local("init", "git init + repo create + set origin, in one step: gitbay init [name] [--private]", cmdInit) } // manCmd generates man pages; a CLI-first tool without man pages is not similarity index 93% rename from cmd/forge/ssh.go rename to cmd/gitbay/ssh.go @@ -9,8 +9,8 @@ import ( "strconv" "strings" - "github.com/krazywarez/forge/internal/cliconfig" - "github.com/krazywarez/forge/internal/protocol" + "gitbay.org/gitbay/internal/cliconfig" + "gitbay.org/gitbay/internal/protocol" ) // context is the resolved target for a command: which instance to talk to @@ -105,7 +105,7 @@ func runSSH(t target, serverArgv []string, stdin io.Reader) int { } return code } - fmt.Fprintln(os.Stderr, "forge: running ssh:", err) + fmt.Fprintln(os.Stderr, "gitbay: running ssh:", err) return protocol.ExitProtocol } @@ -117,7 +117,7 @@ func withRepo(t target, args []string) ([]string, error) { return args, nil // explicit owner/name } if t.repo == "" { - return nil, fmt.Errorf("no repository given and none inferable: pass <owner/name> or run inside a clone of a forge repository") + return nil, fmt.Errorf("no repository given and none inferable: pass <owner/name> or run inside a clone of a gitbay repository") } return append([]string{t.repo}, args...), nil } similarity index 90% rename from cmd/forged/hook.go rename to cmd/gitbayd/hook.go @@ -10,9 +10,9 @@ import ( "github.com/spf13/cobra" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/hookd" - "github.com/krazywarez/forge/internal/policy" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/hookd" + "gitbay.org/gitbay/internal/policy" ) // collectIncomingCommits lists the commits this push introduces and reads @@ -58,7 +58,7 @@ func hookCmd() *cobra.Command { repoID, err1 := strconv.ParseInt(os.Getenv(hookd.EnvRepoID), 10, 64) userID, err2 := strconv.ParseInt(os.Getenv(hookd.EnvUserID), 10, 64) if sock == "" || err1 != nil || err2 != nil { - return fmt.Errorf("missing FORGE_* environment; this command only runs as a git hook") + return fmt.Errorf("missing GITBAY_* environment; this command only runs as a git hook") } var updates []policy.RefUpdate @@ -92,7 +92,7 @@ func hookCmd() *cobra.Command { return collectIncomingCommits(updates) }) if err != nil { - return fmt.Errorf("forge daemon unreachable: %w", err) + return fmt.Errorf("gitbay daemon unreachable: %w", err) } if !resp.Allow { fmt.Fprintln(os.Stderr, resp.Message) similarity index 90% rename from cmd/forged/main.go rename to cmd/gitbayd/main.go @@ -1,4 +1,4 @@ -// forged is the forge server daemon. The same binary also runs in hook mode +// gitbayd is the forge server daemon. The same binary also runs in hook mode // (invoked by git via core.hooksPath) and hosts the host-local admin commands. package main @@ -14,18 +14,18 @@ import ( "github.com/spf13/cobra" "golang.org/x/crypto/ssh" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/control" - "github.com/krazywarez/forge/internal/gitd" - "github.com/krazywarez/forge/internal/hookd" - "github.com/krazywarez/forge/internal/httpd" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/sshd" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/control" + "gitbay.org/gitbay/internal/gitd" + "gitbay.org/gitbay/internal/hookd" + "gitbay.org/gitbay/internal/httpd" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/sshd" + "gitbay.org/gitbay/internal/store" ) func openStore(cfg config.Config) (*store.Store, error) { - s, err := store.Open(filepath.Join(cfg.Server.Root, "forge.db")) + s, err := store.Open(filepath.Join(cfg.Server.Root, "gitbay.db")) if err != nil { return nil, err } @@ -40,12 +40,12 @@ var configPath string func main() { root := &cobra.Command{ - Use: "forged", - Short: "forge server daemon", + Use: "gitbayd", + Short: "gitbay server daemon", SilenceUsage: true, SilenceErrors: true, } - root.PersistentFlags().StringVar(&configPath, "config", "/etc/forge/config.toml", "path to config file") + root.PersistentFlags().StringVar(&configPath, "config", "/etc/gitbay/config.toml", "path to config file") root.AddCommand( checkConfigCmd(), @@ -58,7 +58,7 @@ func main() { ) if err := root.Execute(); err != nil { - fmt.Fprintln(os.Stderr, "forged:", err) + fmt.Fprintln(os.Stderr, "gitbayd:", err) os.Exit(1) } } @@ -173,7 +173,7 @@ func migrateCmd() *cobra.Command { if err != nil { return err } - s, err := store.Open(cfg.Server.Root + "/forge.db") + s, err := store.Open(cfg.Server.Root + "/gitbay.db") if err != nil { return err } similarity index 88% rename from cmd/forged/system.go rename to cmd/gitbayd/system.go @@ -7,14 +7,14 @@ import ( "github.com/spf13/cobra" "golang.org/x/crypto/ssh" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/sshd" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/sshd" ) // authorizedKeysCmd backs sshd's AuthorizedKeysCommand in system mode: // -// AuthorizedKeysCommand /usr/bin/forged --config /etc/forge/config.toml authorized-keys %t %k +// AuthorizedKeysCommand /usr/bin/gitbayd --config /etc/gitbay/config.toml authorized-keys %t %k // AuthorizedKeysCommandUser git // // It prints a forced-command authorized_keys line for registered keys and @@ -90,7 +90,7 @@ func shellCmd() *cobra.Command { cmdline := os.Getenv("SSH_ORIGINAL_COMMAND") if cmdline == "" { - fmt.Fprintf(os.Stderr, "forge control plane: interactive shells are not available.\nTry: ssh <host> help\n") + fmt.Fprintf(os.Stderr, "gitbay control plane: interactive shells are not available.\nTry: ssh <host> help\n") os.Exit(protocol.ExitUsage) } code := sshd.Exec(cfg, st, user, key.Scope, cmdline, os.Stdin, os.Stdout, os.Stderr) @@ -10,13 +10,13 @@ import ( "testing" ) -func buildForgeCLI(t *testing.T) string { +func buildGitbayCLI(t *testing.T) string { t.Helper() - bin := filepath.Join(t.TempDir(), "forge") - cmd := exec.Command("go", "build", "-o", bin, "github.com/krazywarez/forge/cmd/forge") + bin := filepath.Join(t.TempDir(), "gitbay") + cmd := exec.Command("go", "build", "-o", bin, "gitbay.org/gitbay/cmd/gitbay") cmd.Dir = ".." if out, err := cmd.CombinedOutput(); err != nil { - t.Fatalf("build forge: %v\n%s", err, out) + t.Fatalf("build gitbay: %v\n%s", err, out) } return bin } @@ -51,7 +51,7 @@ func (c *cli) run(t *testing.T, dir, stdin string, args ...string) (string, stri if ee, ok := err.(*exec.ExitError); ok { code = ee.ExitCode() } else if err != nil { - t.Fatalf("forge %v: %v", args, err) + t.Fatalf("gitbay %v: %v", args, err) } return out.String(), errOut.String(), code } @@ -60,7 +60,7 @@ func (c *cli) must(t *testing.T, dir, stdin string, args ...string) string { t.Helper() out, errOut, code := c.run(t, dir, stdin, args...) if code != 0 { - t.Fatalf("forge %v: exit %d\nstdout: %s\nstderr: %s", args, code, out, errOut) + t.Fatalf("gitbay %v: exit %d\nstdout: %s\nstderr: %s", args, code, out, errOut) } return out } @@ -72,7 +72,7 @@ func TestCLI(t *testing.T) { "--key", aliceKey+".pub", "--email", "alice@example.test", "--verified") c := &cli{ - bin: buildForgeCLI(t), + bin: buildGitbayCLI(t), configDir: t.TempDir(), inst: inst, key: aliceKey, @@ -90,7 +90,7 @@ func TestHTTPTransport(t *testing.T) { } if !strings.Contains(out, "remote error:") || !strings.Contains(out, "pushes to this forge go over SSH") || - !strings.Contains(out, "git@forge.test:alice/pub.git") { + !strings.Contains(out, "git@gitbay.test:alice/pub.git") { t.Fatalf("[%s] push refusal output:\n%s", gitBin, out) } for _, banned := range []string{"Username", "Password", "Authentication failed", "terminal prompts disabled", "401", "403"} { @@ -9,7 +9,7 @@ import ( "golang.org/x/crypto/ssh" - "github.com/krazywarez/forge/internal/sig" + "gitbay.org/gitbay/internal/sig" ) type mrShow struct { @@ -16,7 +16,7 @@ import ( "github.com/ProtonMail/go-crypto/openpgp/packet" "golang.org/x/crypto/ssh" - "github.com/krazywarez/forge/internal/sig" + "gitbay.org/gitbay/internal/sig" ) // --- fixture key helpers ------------------------------------------------- @@ -361,7 +361,7 @@ func TestSignatureVerification(t *testing.T) { vc.Dir = dir vc.Env = gpgEnv if out, err := vc.CombinedOutput(); err != nil { - t.Errorf("git verify-commit disagrees with forge verification: %v\n%s", err, out) + t.Errorf("git verify-commit disagrees with gitbay verification: %v\n%s", err, out) } } else { t.Log("gpg not installed; skipping git verify-commit cross-check") @@ -1,4 +1,4 @@ -// Package e2e drives a real forged with the real ssh and git clients. +// Package e2e drives a real gitbayd with the real ssh and git clients. package e2e import ( @@ -14,7 +14,7 @@ import ( ) type instance struct { - forged string // path to built binary + gitbayd string // path to built binary root string config string port int @@ -24,13 +24,13 @@ type instance struct { sshDir string // per-user client keys live here } -func buildForged(t *testing.T) string { +func buildGitbayd(t *testing.T) string { t.Helper() - bin := filepath.Join(t.TempDir(), "forged") - cmd := exec.Command("go", "build", "-o", bin, "github.com/krazywarez/forge/cmd/forged") + bin := filepath.Join(t.TempDir(), "gitbayd") + cmd := exec.Command("go", "build", "-o", bin, "gitbay.org/gitbay/cmd/gitbayd") cmd.Dir = ".." if out, err := cmd.CombinedOutput(); err != nil { - t.Fatalf("build forged: %v\n%s", err, out) + t.Fatalf("build gitbayd: %v\n%s", err, out) } return bin } @@ -53,7 +53,7 @@ func startInstance(t *testing.T) *instance { func startInstanceWith(t *testing.T, extra string) *instance { t.Helper() inst := &instance{ - forged: buildForged(t), + gitbayd: buildGitbayd(t), root: t.TempDir(), port: freePort(t), httpPort: freePort(t), @@ -64,7 +64,7 @@ func startInstanceWith(t *testing.T, extra string) *instance { cfg := fmt.Sprintf(` [server] root = %q -site_url = "https://forge.test" +site_url = "https://gitbay.test" [ssh] port = %d [http] @@ -79,7 +79,7 @@ port = %d t.Fatal(err) } - inst.proc = exec.Command(inst.forged, "--config", inst.config, "serve") + inst.proc = exec.Command(inst.gitbayd, "--config", inst.config, "serve") inst.proc.Stderr = os.Stderr if err := inst.proc.Start(); err != nil { t.Fatal(err) @@ -98,19 +98,19 @@ port = %d return inst } if time.Now().After(deadline) { - t.Fatal("forged did not start listening") + t.Fatal("gitbayd did not start listening") } time.Sleep(50 * time.Millisecond) } } -// admin runs a forged admin command against the instance's database. +// admin runs a gitbayd admin command against the instance's database. func (i *instance) admin(t *testing.T, args ...string) string { t.Helper() - cmd := exec.Command(i.forged, append([]string{"--config", i.config}, args...)...) + cmd := exec.Command(i.gitbayd, append([]string{"--config", i.config}, args...)...) out, err := cmd.CombinedOutput() if err != nil { - t.Fatalf("forged %v: %v\n%s", args, err, out) + t.Fatalf("gitbayd %v: %v\n%s", args, err, out) } return string(out) } @@ -24,14 +24,14 @@ func TestSystemSSHMode(t *testing.T) { t.Fatal(err) } - // forged in system mode: no embedded SSH listener; hookd + http still run. + // gitbayd in system mode: no embedded SSH listener; hookd + http still run. inst := startInstanceWith(t, "") // placeholder to reuse helpers; killed below inst.proc.Process.Kill() inst.proc.Wait() cfg := fmt.Sprintf(` [server] root = %q -site_url = "https://forge.test" +site_url = "https://gitbay.test" [ssh] mode = "system" [http] @@ -41,7 +41,7 @@ tls = "off" if err := os.WriteFile(inst.config, []byte(cfg), 0o600); err != nil { t.Fatal(err) } - inst.proc = exec.Command(inst.forged, "--config", inst.config, "serve") + inst.proc = exec.Command(inst.gitbayd, "--config", inst.config, "serve") inst.proc.Stderr = os.Stderr if err := inst.proc.Start(); err != nil { t.Fatal(err) @@ -65,7 +65,7 @@ tls = "off" // wrapper script argument — only the command path is ownership-checked. wrapper := filepath.Join(sshdDir, "akc.sh") script := fmt.Sprintf("#!/bin/sh\nexec %q --config %q authorized-keys \"$1\" \"$2\"\n", - inst.forged, inst.config) + inst.gitbayd, inst.config) if err := os.WriteFile(wrapper, []byte(script), 0o755); err != nil { t.Fatal(err) } @@ -12,7 +12,7 @@ import ( "golang.org/x/crypto/ssh" - "github.com/krazywarez/forge/internal/sig" + "gitbay.org/gitbay/internal/sig" ) func (i *instance) get(t *testing.T, path string) (int, string) { @@ -1,4 +1,4 @@ -module github.com/krazywarez/forge +module gitbay.org/gitbay go 1.27.0 @@ -46,10 +46,10 @@ type Config struct { func Path() string { if x := os.Getenv("XDG_CONFIG_HOME"); x != "" { - return filepath.Join(x, "forge", "config.toml") + return filepath.Join(x, "gitbay", "config.toml") } home, _ := os.UserHomeDir() - return filepath.Join(home, ".config", "forge", "config.toml") + return filepath.Join(home, ".config", "gitbay", "config.toml") } func Load() (Config, error) { @@ -95,7 +95,7 @@ func (c Config) DefaultInstance() (Instance, string, error) { return inst, name, nil } } - return Instance{}, "", fmt.Errorf("no forge instance configured; run: forge remote add <name> <host>") + return Instance{}, "", fmt.Errorf("no gitbay instance configured; run: gitbay remote add <name> <host>") } var ( @@ -11,12 +11,12 @@ func TestParseRemoteURL(t *testing.T) { repo string ok bool }{ - {"ssh://git@forge.example/alice/proj.git", "forge.example", 0, "git", "alice/proj", true}, - {"ssh://git@forge.example:2222/alice/proj.git", "forge.example", 2222, "git", "alice/proj", true}, - {"ssh://forge.example/alice/proj", "forge.example", 0, "", "alice/proj", true}, - {"git@forge.example:alice/proj.git", "forge.example", 0, "git", "alice/proj", true}, - {"git@forge.example:alice/proj", "forge.example", 0, "git", "alice/proj", true}, - {"https://forge.example/alice/proj.git", "", 0, "", "", false}, + {"ssh://git@gitbay.example/alice/proj.git", "gitbay.example", 0, "git", "alice/proj", true}, + {"ssh://git@gitbay.example:2222/alice/proj.git", "gitbay.example", 2222, "git", "alice/proj", true}, + {"ssh://gitbay.example/alice/proj", "gitbay.example", 0, "", "alice/proj", true}, + {"git@gitbay.example:alice/proj.git", "gitbay.example", 0, "git", "alice/proj", true}, + {"git@gitbay.example:alice/proj", "gitbay.example", 0, "git", "alice/proj", true}, + {"https://gitbay.example/alice/proj.git", "", 0, "", "", false}, {"/local/path/repo.git", "", 0, "", "", false}, } for _, tc := range cases { @@ -35,12 +35,12 @@ func TestParseRemoteURL(t *testing.T) { } func TestCloneURL(t *testing.T) { - i := Instance{Host: "forge.example"} - if got := i.CloneURL("a/b"); got != "ssh://git@forge.example/a/b.git" { + i := Instance{Host: "gitbay.example"} + if got := i.CloneURL("a/b"); got != "ssh://git@gitbay.example/a/b.git" { t.Errorf("CloneURL = %s", got) } - i = Instance{Host: "forge.example", Port: 2222, User: "u"} - if got := i.CloneURL("a/b"); got != "ssh://u@forge.example:2222/a/b.git" { + i = Instance{Host: "gitbay.example", Port: 2222, User: "u"} + if got := i.CloneURL("a/b"); got != "ssh://u@gitbay.example:2222/a/b.git" { t.Errorf("CloneURL = %s", got) } } @@ -1,4 +1,4 @@ -// Package config loads and validates the forged server configuration. +// Package config loads and validates the gitbayd server configuration. package config import ( @@ -69,7 +69,7 @@ type Mail struct { // Default returns the configuration used when a key is absent from the file. func Default() Config { return Config{ - Server: Server{Root: "/var/lib/forge"}, + Server: Server{Root: "/var/lib/gitbay"}, SSH: SSH{Mode: "embedded", Port: 22}, HTTP: HTTP{Addr: ":443", TLS: "acme"}, Web: Web{Mode: "view_only"}, @@ -154,7 +154,7 @@ func (c Config) Validate() error { } if c.Web.PasswordAuth && c.Web.Mode == "accounts" { errs = append(errs, errors.New( - "web.password_auth is not implemented yet; browser sessions are minted over SSH (forge web login)")) + "web.password_auth is not implemented yet; browser sessions are minted over SSH (gitbay web login)")) } return errors.Join(errs...) @@ -18,8 +18,8 @@ func writeConfig(t *testing.T, body string) string { const minimal = ` [server] -root = "/var/lib/forge" -site_url = "https://forge.example" +root = "/var/lib/gitbay" +site_url = "https://gitbay.example" ` func TestLoadMinimal(t *testing.T) { @@ -52,7 +52,7 @@ func TestContradictions(t *testing.T) { }, { "system ssh with open registration", - minimal + "\n[ssh]\nmode = \"system\"\n[registration]\nmode = \"open\"\n[mail]\nsmtp_host = \"mx.example\"\nfrom = \"forge@example\"\n", + minimal + "\n[ssh]\nmode = \"system\"\n[registration]\nmode = \"open\"\n[mail]\nsmtp_host = \"mx.example\"\nfrom = \"gitbay@example\"\n", "requires registration.mode = \"closed\"", }, { @@ -72,12 +72,12 @@ func TestContradictions(t *testing.T) { }, { "unknown key", - "[server]\nroot = \"/var/lib/forge\"\nsite_url = \"https://forge.example\"\nbogus = 1\n", + "[server]\nroot = \"/var/lib/gitbay\"\nsite_url = \"https://gitbay.example\"\nbogus = 1\n", "unknown config key", }, { "missing site_url", - "[server]\nroot = \"/var/lib/forge\"\n", + "[server]\nroot = \"/var/lib/gitbay\"\n", "site_url", }, } @@ -101,7 +101,7 @@ func TestValidCombinations(t *testing.T) { }{ { "invite with smtp", - minimal + "\n[registration]\nmode = \"invite\"\n[mail]\nsmtp_host = \"mx.example\"\nfrom = \"forge@example\"\n", + minimal + "\n[registration]\nmode = \"invite\"\n[mail]\nsmtp_host = \"mx.example\"\nfrom = \"gitbay@example\"\n", }, { "system ssh closed registration", @@ -10,9 +10,9 @@ import ( "reflect" "slices" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/store" ) type Ctx struct { @@ -4,7 +4,7 @@ import ( "strings" "testing" - "github.com/krazywarez/forge/internal/protocol" + "gitbay.org/gitbay/internal/protocol" ) // TestEveryCommandReachableFromBareSSH asserts that each registered command's @@ -7,8 +7,8 @@ import ( "golang.org/x/crypto/ssh" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/store" ) func init() { @@ -10,9 +10,9 @@ import ( "strings" "time" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/protocol" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/protocol" ) func init() { @@ -27,7 +27,7 @@ func init() { const askpassScript = `#!/bin/sh case "$1" in Username*) echo "x-access-token" ;; - *) echo "${FORGE_IMPORT_TOKEN}" ;; + *) echo "${GITBAY_IMPORT_TOKEN}" ;; esac ` @@ -96,7 +96,7 @@ func runRepoImport(c *Ctx, args []string) int { } env = []string{ "GIT_ASKPASS=" + askpass, - "FORGE_IMPORT_TOKEN=" + token, + "GITBAY_IMPORT_TOKEN=" + token, "GIT_TERMINAL_PROMPT=0", } } else { @@ -7,9 +7,9 @@ import ( "strconv" "strings" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/store" ) const maxBodyBytes = 64 << 10 @@ -7,10 +7,10 @@ import ( "strconv" "strings" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/store" ) func init() { @@ -9,10 +9,10 @@ import ( "slices" "strings" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/store" ) // RepoDir returns the on-disk path for a repository. @@ -8,11 +8,11 @@ import ( "strconv" "time" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/sig" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/sig" + "gitbay.org/gitbay/internal/store" ) func init() { @@ -5,8 +5,8 @@ import ( "io" "time" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/store" ) func newStoredToken() (token, hash string, err error) { return store.NewToken() } @@ -13,9 +13,9 @@ import ( "strings" "time" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/control" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/control" + "gitbay.org/gitbay/internal/store" ) type Server struct { @@ -31,7 +31,7 @@ func InitBare(path, defaultBranch, hooksPath string) error { // Transport streams one git transport service (upload-pack, receive-pack, // upload-archive). extraEnv entries are appended to the process environment; -// hooks read the FORGE_* variables from it. +// hooks read the GITBAY_* variables from it. func Transport(service, repoPath string, stdin io.Reader, stdout, errW io.Writer, extraEnv []string) error { var args []string switch service { @@ -137,7 +137,7 @@ func CommitFileChange(dir, branch, path string, content []byte, name, email, mes // Stage the parent tree in a temporary index, splice the blob in, and // write the new tree. - idx, err := os.CreateTemp("", "forge-index-*") + idx, err := os.CreateTemp("", "gitbay-index-*") if err != nil { return "", err } @@ -1,5 +1,5 @@ // Package hookd is the unix-socket bridge between git hooks and the daemon. -// The hook process (forged in hook mode) computes git facts — it inherits +// The hook process (gitbayd in hook mode) computes git facts — it inherits // git's quarantine environment, which the daemon does not see — and sends // them here; the daemon answers with a policy decision. // @@ -17,20 +17,20 @@ import ( "os" "path/filepath" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/control" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/sig" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/control" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/sig" + "gitbay.org/gitbay/internal/store" ) // Env variable names passed to git transport subprocesses and inherited by // hooks. const ( - EnvSocket = "FORGE_HOOK_SOCKET" - EnvRepoID = "FORGE_REPO_ID" - EnvUserID = "FORGE_USER_ID" + EnvSocket = "GITBAY_HOOK_SOCKET" + EnvRepoID = "GITBAY_REPO_ID" + EnvUserID = "GITBAY_USER_ID" ) type Request struct { @@ -59,7 +59,7 @@ type Response struct { // SocketPath returns the hook socket location. It prefers the server root, // but unix socket paths are capped (~104 bytes on macOS, 108 on Linux), so // deep roots fall back to a hashed name under the system temp directory. -// Hooks receive the chosen path via FORGE_HOOK_SOCKET, so both sides always +// Hooks receive the chosen path via GITBAY_HOOK_SOCKET, so both sides always // agree. func SocketPath(root string) string { p := filepath.Join(root, "hook.sock") @@ -67,7 +67,7 @@ func SocketPath(root string) string { return p } sum := sha256.Sum256([]byte(root)) - return filepath.Join(os.TempDir(), fmt.Sprintf("forge-%x.sock", sum[:8])) + return filepath.Join(os.TempDir(), fmt.Sprintf("gitbay-%x.sock", sum[:8])) } type Server struct { @@ -250,12 +250,12 @@ func Ask(socketPath string, req Request, commits func() (CommitsPayload, error)) // WriteHookScripts (re)generates the shared hooks directory. Called at // daemon startup so a moved binary self-heals; every repo points here via // core.hooksPath. -func WriteHookScripts(hooksDir, forgedPath string) error { +func WriteHookScripts(hooksDir, gitbaydPath string) error { if err := os.MkdirAll(hooksDir, 0o755); err != nil { return err } for _, hook := range []string{"pre-receive", "post-receive"} { - script := fmt.Sprintf("#!/bin/sh\nexec %q hook %s\n", forgedPath, hook) + script := fmt.Sprintf("#!/bin/sh\nexec %q hook %s\n", gitbaydPath, hook) if err := os.WriteFile(filepath.Join(hooksDir, hook), []byte(script), 0o755); err != nil { return err } @@ -7,13 +7,13 @@ import ( "strings" "time" - "github.com/krazywarez/forge/internal/control" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/control" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/store" ) -const sessionCookie = "forge_session" +const sessionCookie = "gitbay_session" // viewer returns the logged-in user, or a zero User for anonymous visitors. // Only meaningful in accounts mode; in view_only no session route exists so @@ -4,8 +4,8 @@ import ( "strings" "testing" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/policy" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/policy" ) // TestViewOnlyHasNoMutatingRoutes is the structural guarantee from the plan: @@ -14,9 +14,9 @@ import ( "os/exec" "strings" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/control" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/control" + "gitbay.org/gitbay/internal/store" ) type Server struct { @@ -4,7 +4,7 @@ import ( "bytes" "fmt" - "github.com/krazywarez/forge/internal/policy" + "gitbay.org/gitbay/internal/policy" "html/template" "net/http" "path" @@ -17,11 +17,11 @@ import ( "github.com/alecthomas/chroma/v2/styles" "github.com/yuin/goldmark" - "github.com/krazywarez/forge/internal/control" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/sig" - "github.com/krazywarez/forge/internal/store" - "github.com/krazywarez/forge/internal/web" + "gitbay.org/gitbay/internal/control" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/sig" + "gitbay.org/gitbay/internal/store" + "gitbay.org/gitbay/internal/web" ) const maxRenderBytes = 1 << 20 // largest blob rendered inline @@ -3,7 +3,7 @@ package policy import ( "strings" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/store" ) // CanRead reports whether user may read repo over an authenticated channel. @@ -3,7 +3,7 @@ package policy import ( "testing" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/store" ) var ( @@ -17,13 +17,13 @@ import ( "golang.org/x/crypto/ssh" - "github.com/krazywarez/forge/internal/config" - "github.com/krazywarez/forge/internal/control" - "github.com/krazywarez/forge/internal/gitutil" - "github.com/krazywarez/forge/internal/hookd" - "github.com/krazywarez/forge/internal/policy" - "github.com/krazywarez/forge/internal/protocol" - "github.com/krazywarez/forge/internal/store" + "gitbay.org/gitbay/internal/config" + "gitbay.org/gitbay/internal/control" + "gitbay.org/gitbay/internal/gitutil" + "gitbay.org/gitbay/internal/hookd" + "gitbay.org/gitbay/internal/policy" + "gitbay.org/gitbay/internal/protocol" + "gitbay.org/gitbay/internal/store" ) type Server struct { @@ -37,7 +37,7 @@ func New(cfg config.Config, st *store.Store) (*Server, error) { sc := &ssh.ServerConfig{ PublicKeyCallback: s.authenticate, - ServerVersion: "SSH-2.0-forged", + ServerVersion: "SSH-2.0-gitbayd", } signers, err := loadHostKeys(cfg) if err != nil { @@ -158,7 +158,7 @@ func (s *Server) handleSession(sconn *ssh.ServerConn, ch ssh.Channel, reqs <-cha return case "shell": req.Reply(true, nil) - fmt.Fprintf(ch, "forge control plane: interactive shells are not available.\nTry: ssh %s help\n", s.cfg.Server.SiteURL) + fmt.Fprintf(ch, "gitbay control plane: interactive shells are not available.\nTry: ssh %s help\n", s.cfg.Server.SiteURL) sendExit(ch, protocol.ExitUsage) return case "pty-req", "env": @@ -190,7 +190,7 @@ func (s *Server) runExec(sconn *ssh.ServerConn, ch ssh.Channel, cmdline string) // Exec runs one SSH exec command line for an authenticated key. It is the // single dispatch path shared by the embedded listener and the system-sshd -// forced command (forged shell). +// forced command (gitbayd shell). func Exec(cfg config.Config, st *store.Store, user store.User, scope, cmdline string, stdin io.Reader, stdout, stderr io.Writer) int { argv, err := protocol.Tokenize(cmdline) @@ -5,7 +5,7 @@ import ( "errors" "time" - "github.com/krazywarez/forge/internal/sig" + "gitbay.org/gitbay/internal/sig" ) // AddPGPKey registers an OpenPGP key and bumps the key epoch. @@ -8,7 +8,7 @@ import ( func open(t *testing.T) *Store { t.Helper() - s, err := Open(filepath.Join(t.TempDir(), "forge.db")) + s, err := Open(filepath.Join(t.TempDir(), "gitbay.db")) if err != nil { t.Fatal(err) }