This commit is contained in:
Jabberwocky238
2026-07-17 05:55:31 -04:00
parent 67c07b3bcf
commit 68cda1972c
19 changed files with 897 additions and 897 deletions
+58 -58
View File
@@ -1,13 +1,13 @@
// Mutation surface (IStateMut): repo lifecycle + ACL grants.
//
// These are the Updater's write operations. NamespaceID/NameID are denormalized
// on Repo. CreateRepo reuses an existing namespace's NamespaceID if one is
// present, else mints a fresh one (max(namespace_id)+1); NameID is always fresh
// (max(name_id)+1). Both assume a single mutator (the Updater). All deletes are
// hard (Unscoped) so IDs are freed for reuse.
//
// ACL targets are polymorphic (TargetTypeRepo / TargetTypeNS): a grant on a
// namespace covers every repo sharing its NamespaceID.
package state
@@ -26,9 +26,9 @@ import (
"simplegit/common"
)
// parseRepoPath splits a repo path "ns/name[.git]" into (ns, name) on the last
// "/". ns may be nested ("org/team/name.git" -> ns "org/team", name "name").
// Mirrors cmd.splitOwnerName.
func parseRepoPath(path string) (ns, name string, err error) {
path = strings.TrimPrefix(path, "/")
path = strings.TrimSuffix(path, ".git")
@@ -38,9 +38,9 @@ func parseRepoPath(path string) (ns, name string, err error) {
return "", "", fmt.Errorf("repo path %q has no namespace segment", path)
}
// nsDir returns the absolute on-disk directory for a namespace (root/ns),
// verifying it stays under root. Unlike common.Resolve it does NOT require a
// ".git" suffix -- namespaces are directories, not repos.
func (s *LocalState) nsDir(ns string) (string, error) {
ns = strings.TrimPrefix(ns, "/")
if ns == "" || strings.Contains(ns, "..") {
@@ -60,9 +60,9 @@ func (s *LocalState) nsDir(ns string) (string, error) {
return abs, nil
}
// resolveNamespaceID returns the (shared) NamespaceID for ns by reading any
// existing repo in that namespace. Errors if the namespace has no repos yet --
// the Updater must bootstrap a namespace before CreateRepo / ACLUpsertOnNS.
func (s *LocalState) resolveNamespaceID(ns string) (int64, error) {
var r Repo
has, err := s.engine.Where("namespace = ?", ns).Get(&r)
@@ -75,9 +75,9 @@ func (s *LocalState) resolveNamespaceID(ns string) (int64, error) {
return r.NamespaceID, nil
}
// nextNameID returns a fresh NameID (max(name_id)+1). Single-writer assumption
// (the Updater is the only mutator); swap for the Updater's own scheme if it
// manages NameID externally.
func (s *LocalState) nextNameID() (int64, error) {
var row struct {
M int64 `xorm:"m"`
@@ -88,8 +88,8 @@ func (s *LocalState) nextNameID() (int64, error) {
return row.M + 1, nil
}
// nextNamespaceID returns a fresh NamespaceID (max(namespace_id)+1). Mirrors
// nextNameID; single-writer assumption (the Updater is the only mutator).
func (s *LocalState) nextNamespaceID() (int64, error) {
var row struct {
M int64 `xorm:"m"`
@@ -100,11 +100,11 @@ func (s *LocalState) nextNamespaceID() (int64, error) {
return row.M + 1, nil
}
// --- repo lifecycle ---
// CreateRepo registers a repo row and runs `git init --bare` on disk. The
// namespace must already exist (have at least one repo) so NamespaceID can be
// resolved; NameID is freshly assigned. skip-auth: disk-only (git init, no DB).
func (s *LocalState) CreateRepo(ns, name string) error {
path, err := s.cfg.RepoPath(ns, name)
if err != nil {
@@ -120,7 +120,7 @@ func (s *LocalState) CreateRepo(ns, name string) error {
if !errors.Is(err, ErrNamespaceNotFound) {
return err
}
nsID, err = s.nextNamespaceID() // brand-new namespace
nsID, err = s.nextNamespaceID()
if err != nil {
return err
}
@@ -132,10 +132,10 @@ func (s *LocalState) CreateRepo(ns, name string) error {
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return fmt.Errorf("create repo: mkdir ns: %w", err)
}
// Use the hook template (materialized by the daemon at startup) so every new
// repo gets forwarding hooks for all git hook types. Fall back to a plain
// init if the template is absent (e.g. a state test without the daemon);
// such a repo simply has no hooks and pushes still work.
initArgs := []string{"init", "--bare"}
if fi, err := os.Stat(s.cfg.TemplateDir()); err == nil && fi.IsDir() {
initArgs = append(initArgs, "--template="+s.cfg.TemplateDir())
@@ -157,14 +157,14 @@ func (s *LocalState) CreateRepo(ns, name string) error {
return nil
}
// ExistRepo returns nil if a repo is registered, ErrRepoNotFound otherwise.
func (s *LocalState) ExistRepo(ns, name string) error {
_, err := s.findRepo(ns, name)
return err
}
// DeleteRepo removes the repo row, its repo-targeted ACL grants, and the
// on-disk bare directory. Hard-deletes. skip-auth: disk-only.
func (s *LocalState) DeleteRepo(ns, name string) error {
path, _ := s.cfg.RepoPath(ns, name)
r, err := s.findRepo(ns, name)
@@ -183,9 +183,9 @@ func (s *LocalState) DeleteRepo(ns, name string) error {
return nil
}
// MoveRepo relocates a repo (transfer and/or rename): renames the on-disk dir
// and updates the row's Namespace/Name (and NamespaceID when the namespace
// changes). NameID is left stable across a rename. skip-auth: disk-only.
func (s *LocalState) MoveRepo(old, new string) error {
oldNs, oldName, err := parseRepoPath(old)
if err != nil {
@@ -234,9 +234,9 @@ func (s *LocalState) MoveRepo(old, new string) error {
return nil
}
// MoveNS renames a namespace: renames the on-disk directory and updates the
// Namespace field of all its repos. NamespaceID is stable (unchanged), so
// ns-targeted ACL grants survive a rename. skip-auth: disk-only.
func (s *LocalState) MoveNS(old, new string) error {
if old == new {
return nil
@@ -262,11 +262,11 @@ func (s *LocalState) MoveNS(old, new string) error {
return nil
}
// --- ACL grants ---
// upsertACL inserts or updates the (credType, credID, targetType, targetID)
// grant to perm and returns the ACL id. The unique(cred) group makes this one
// grant per credential per target.
func (s *LocalState) upsertACL(credType CredType, credID int64, targetType TargetType, targetID int64, perm common.Perm) (int64, error) {
var acl ACL
has, err := s.engine.Where("cred_type = ? AND cred_id = ? AND target_type = ? AND target_id = ?",
@@ -295,7 +295,7 @@ func (s *LocalState) upsertACL(credType CredType, credID int64, targetType Targe
return acl.ID, nil
}
// ACLDelete removes a single ACL grant by id. Hard-delete. Idempotent.
func (s *LocalState) ACLDelete(id int64) error {
if _, err := s.engine.ID(id).Unscoped().Delete(&ACL{}); err != nil {
return fmt.Errorf("delete acl: %w", err)
@@ -303,7 +303,7 @@ func (s *LocalState) ACLDelete(id int64) error {
return nil
}
// ensureSSHKey returns the id of a registered SSH key, inserting it if new.
func (s *LocalState) ensureSSHKey(key ssh.PublicKey) (int64, error) {
fp := fingerprint(key)
var sk SSHKey
@@ -325,9 +325,9 @@ func (s *LocalState) ensureSSHKey(key ssh.PublicKey) (int64, error) {
return sk.ID, nil
}
// ensurePAT returns the id of a PAT matching plaintext, inserting a new PAT row
// (hash + prefix) if it does not exist. PATs are created here -- there is no
// separate CreatePAT on IStateMut.
func (s *LocalState) ensurePAT(plaintext string) (int64, error) {
sum := sha256.Sum256([]byte(plaintext))
hash := hex.EncodeToString(sum[:])
@@ -350,8 +350,8 @@ func (s *LocalState) ensurePAT(plaintext string) (int64, error) {
return pat.ID, nil
}
// ACLUpsertSSHKeyOnNS grants key perm on namespace ns (resolved to its shared
// NamespaceID). Registers the key if new.
func (s *LocalState) ACLUpsertSSHKeyOnNS(ns string, key ssh.PublicKey, perm common.Perm) (int64, error) {
nsID, err := s.resolveNamespaceID(ns)
if err != nil {
@@ -364,8 +364,8 @@ func (s *LocalState) ACLUpsertSSHKeyOnNS(ns string, key ssh.PublicKey, perm comm
return s.upsertACL(CredTypeSSH, keyID, TargetTypeNS, nsID, perm)
}
// ACLUpsertPATOnNS grants pat (plaintext) perm on namespace ns. Creates the PAT
// if new.
func (s *LocalState) ACLUpsertPATOnNS(ns string, pat string, perm common.Perm) (int64, error) {
nsID, err := s.resolveNamespaceID(ns)
if err != nil {
@@ -378,7 +378,7 @@ func (s *LocalState) ACLUpsertPATOnNS(ns string, pat string, perm common.Perm) (
return s.upsertACL(CredTypePAT, patID, TargetTypeNS, nsID, perm)
}
// ACLUpsertSSHKeyOnRepo grants key perm on repo "ns/name". Registers the key if new.
func (s *LocalState) ACLUpsertSSHKeyOnRepo(reponame string, key ssh.PublicKey, perm common.Perm) (int64, error) {
ns, name, err := parseRepoPath(reponame)
if err != nil {
@@ -395,8 +395,8 @@ func (s *LocalState) ACLUpsertSSHKeyOnRepo(reponame string, key ssh.PublicKey, p
return s.upsertACL(CredTypeSSH, keyID, TargetTypeRepo, r.ID, perm)
}
// ACLUpsertPATOnRepo grants pat (plaintext) perm on repo "ns/name". Creates the
// PAT if new.
func (s *LocalState) ACLUpsertPATOnRepo(reponame string, pat string, perm common.Perm) (int64, error) {
ns, name, err := parseRepoPath(reponame)
if err != nil {