fierj
PublicTiny personal git forge
92c61a6f6f6247692cbf25b28420d5e8a7076fb7
diff --git a/git.go b/git.go
index 948e7d5..6c3967f 100644
--- a/git.go
+++ b/git.go
@@ -2,6 +2,7 @@ package main
import (
"bytes"
+ "encoding/json"
"fmt"
"os"
"os/exec"
@@ -25,6 +26,44 @@ type Commit struct {
Message string
}
+// RepoMeta is per-repository configuration stored in .fierj.json inside the bare repo.
+type RepoMeta struct {
+ Description string `json:"description,omitempty"`
+ IsPrivate bool `json:"is_private"` // requires login to view
+ AuthorizedKeys []string `json:"authorized_keys,omitempty"` // SSH public keys allowed to push
+ ProtectedBranches []string `json:"protected_branches,omitempty"` // branches that reject force-push
+}
+
+func (g *Git) metaPath() string {
+ return path.Join(g.Dir, g.Name+".git", ".fierj.json")
+}
+
+// LoadMeta reads the .fierj.json file, falling back to the git description file for Description.
+func (g *Git) LoadMeta() RepoMeta {
+ var m RepoMeta
+ if data, err := os.ReadFile(g.metaPath()); err == nil {
+ json.Unmarshal(data, &m)
+ }
+ if m.Description == "" {
+ if data, err := os.ReadFile(path.Join(g.Dir, g.Name+".git", "description")); err == nil {
+ d := strings.TrimSpace(string(data))
+ if d != "" && !strings.Contains(d, "Unnamed repository") {
+ m.Description = d
+ }
+ }
+ }
+ return m
+}
+
+// SaveMeta writes the .fierj.json file.
+func (g *Git) SaveMeta(m RepoMeta) error {
+ data, err := json.MarshalIndent(m, "", " ")
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(g.metaPath(), data, 0644)
+}
+
type VCS interface {
List(ref, path string) ([]TreeEntry, error)
Blob(ref, path string) (string, error)
@@ -94,7 +133,8 @@ func InitRepo(cfg Config, name, description string) error {
return fmt.Errorf("git init failed: %v, stderr: %s", err, stderr.String())
}
if description != "" {
- os.WriteFile(filepath.Join(repoPath, "description"), []byte(description+"\n"), 0644)
+ g := &Git{Dir: absRoot, Name: name}
+ g.SaveMeta(RepoMeta{Description: description})
}
return nil
}
@@ -119,7 +159,8 @@ func ImportRepo(cfg Config, cloneURL, description string) (string, error) {
return "", fmt.Errorf("clone failed: %v, stderr: %s", err, stderr.String())
}
if description != "" {
- os.WriteFile(filepath.Join(repoPath, "description"), []byte(description+"\n"), 0644)
+ g := &Git{Dir: absRoot, Name: name}
+ g.SaveMeta(RepoMeta{Description: description})
}
return name, nil
}
@@ -143,15 +184,11 @@ func ListRepos(cfg Config) ([]*Git, error) {
func (g *Git) Repo() string { return g.Name }
func (g *Git) Description() string {
- data, err := os.ReadFile(path.Join(g.Dir, g.Name+".git", "description"))
- if err != nil {
- return ""
- }
- desc := strings.TrimSpace(string(data))
- if desc == "" || strings.Contains(desc, "Unnamed repository") {
- return ""
- }
- return desc
+ return g.LoadMeta().Description
+}
+
+func (g *Git) IsPrivate() bool {
+ return g.LoadMeta().IsPrivate
}
func (g *Git) List(ref, path string) ([]TreeEntry, error) {