e0882cc4c8ccfd2e0eed5cdf81cf4796737924b3

Author
Ayman Bagabas <ayman.bagabas@gmail.com>
Committer
GitHub <noreply@github.com>
Date

Message

feat: store repo meta data in repo directory (#338)

* feat: store repo meta data in repo directory

- Store auth'd user in context.
- Write description, owner, and git-daemon-export-ok files.

Fixes: https://github.com/charmbracelet/soft-serve/issues/255
Fixes: https://github.com/charmbracelet/soft-serve/issues/256

* fix: add tests

Diff

  1diff --git a/server/backend/repo.go b/server/backend/repo.go
  2index 5f07e61f175140795062d954ce67e04897d6e06f..ebd846f4e9328ea71fa10ca1b56d592f5a62b2b4 100644
  3--- a/server/backend/repo.go
  4+++ b/server/backend/repo.go
  5@@ -5,6 +5,7 @@ import (
  6 	"context"
  7 	"errors"
  8 	"fmt"
  9+	"io/fs"
 10 	"os"
 11 	"path/filepath"
 12 	"time"
 13@@ -53,6 +54,18 @@ func (d *Backend) CreateRepository(ctx context.Context, name string, opts proto.
 14 			return err
 15 		}
 16 
 17+		if err := os.WriteFile(filepath.Join(rp, "description"), []byte(opts.Description), fs.ModePerm); err != nil {
 18+			d.logger.Error("failed to write description", "repo", name, "err", err)
 19+			return err
 20+		}
 21+
 22+		if !opts.Private {
 23+			if err := os.WriteFile(filepath.Join(rp, "git-daemon-export-ok"), []byte{}, fs.ModePerm); err != nil {
 24+				d.logger.Error("failed to write git-daemon-export-ok", "repo", name, "err", err)
 25+				return err
 26+			}
 27+		}
 28+
 29 		return hooks.GenerateHooks(ctx, d.cfg, repo)
 30 	}); err != nil {
 31 		d.logger.Debug("failed to create repository in database", "err", err)
 32@@ -341,29 +354,51 @@ func (d *Backend) SetHidden(ctx context.Context, name string, hidden bool) error
 33 // SetDescription sets the description of a repository.
 34 //
 35 // It implements backend.Backend.
 36-func (d *Backend) SetDescription(ctx context.Context, repo string, desc string) error {
 37-	repo = utils.SanitizeRepo(repo)
 38+func (d *Backend) SetDescription(ctx context.Context, name string, desc string) error {
 39+	name = utils.SanitizeRepo(name)
 40+	rp := filepath.Join(d.reposPath(), name+".git")
 41 
 42 	// Delete cache
 43-	d.cache.Delete(repo)
 44+	d.cache.Delete(name)
 45 
 46 	return d.db.TransactionContext(ctx, func(tx *db.Tx) error {
 47-		return d.store.SetRepoDescriptionByName(ctx, tx, repo, desc)
 48+		if err := os.WriteFile(filepath.Join(rp, "description"), []byte(desc), fs.ModePerm); err != nil {
 49+			d.logger.Error("failed to write description", "repo", name, "err", err)
 50+			return err
 51+		}
 52+
 53+		return d.store.SetRepoDescriptionByName(ctx, tx, name, desc)
 54 	})
 55 }
 56 
 57 // SetPrivate sets the private flag of a repository.
 58 //
 59 // It implements backend.Backend.
 60-func (d *Backend) SetPrivate(ctx context.Context, repo string, private bool) error {
 61-	repo = utils.SanitizeRepo(repo)
 62+func (d *Backend) SetPrivate(ctx context.Context, name string, private bool) error {
 63+	name = utils.SanitizeRepo(name)
 64+	rp := filepath.Join(d.reposPath(), name+".git")
 65 
 66 	// Delete cache
 67-	d.cache.Delete(repo)
 68+	d.cache.Delete(name)
 69 
 70 	return db.WrapError(
 71 		d.db.TransactionContext(ctx, func(tx *db.Tx) error {
 72-			return d.store.SetRepoIsPrivateByName(ctx, tx, repo, private)
 73+			fp := filepath.Join(rp, "git-daemon-export-ok")
 74+			if !private {
 75+				if err := os.WriteFile(fp, []byte{}, fs.ModePerm); err != nil {
 76+					d.logger.Error("failed to write git-daemon-export-ok", "repo", name, "err", err)
 77+					return err
 78+				}
 79+			} else {
 80+				if _, err := os.Stat(fp); err == nil {
 81+					if err := os.Remove(fp); err != nil {
 82+						d.logger.Error("failed to remove git-daemon-export-ok", "repo", name, "err", err)
 83+						return err
 84+					}
 85+				}
 86+			}
 87+
 88+			return d.store.SetRepoIsPrivateByName(ctx, tx, name, private)
 89 		}),
 90 	)
 91 }
 92diff --git a/testscript/script_test.go b/testscript/script_test.go
 93index 032bd47eb2be04bb400925a9f589eac1766e429a..dbb8b956f53b173c38df509acfb379bb600bca23 100644
 94--- a/testscript/script_test.go
 95+++ b/testscript/script_test.go
 96@@ -52,6 +52,7 @@ func TestScript(t *testing.T) {
 97 			"usoft":    cmdSoft(user1.Signer()),
 98 			"git":      cmdGit(key),
 99 			"mkfile":   cmdMkfile,
100+			"readfile": cmdReadfile,
101 			"dos2unix": cmdDos2Unix,
102 		},
103 		Setup: func(e *testscript.Env) error {
104@@ -67,6 +68,7 @@ func TestScript(t *testing.T) {
105 			statsListen := fmt.Sprintf("localhost:%d", statsPort)
106 			serverName := "Test Soft Serve"
107 
108+			e.Setenv("DATA_PATH", data)
109 			e.Setenv("SSH_PORT", fmt.Sprintf("%d", sshPort))
110 			e.Setenv("ADMIN1_AUTHORIZED_KEY", admin1.AuthorizedKey())
111 			e.Setenv("ADMIN2_AUTHORIZED_KEY", admin2.AuthorizedKey())
112@@ -250,3 +252,7 @@ func check(ts *testscript.TestScript, err error, neg bool) {
113 		ts.Check(err)
114 	}
115 }
116+
117+func cmdReadfile(ts *testscript.TestScript, neg bool, args []string) {
118+	ts.Stdout().Write([]byte(ts.ReadFile(args[0])))
119+}
120diff --git a/testscript/testdata/mirror.txtar b/testscript/testdata/mirror.txtar
121index 7dcdb6c59b40aa11edc15f6d7f90d1414f4aabc5..77b4bc281414c98620f0ba3b34cfc5e6447baa68 100644
122--- a/testscript/testdata/mirror.txtar
123+++ b/testscript/testdata/mirror.txtar
124@@ -6,6 +6,9 @@
125 # import a repo
126 soft repo import --mirror charmbracelet/catwalk https://github.com/charmbracelet/catwalk.git
127 
128+# check empty description file
129+readfile $DATA_PATH/repos/charmbracelet/catwalk.git/description ''
130+
131 # check repo info
132 soft repo info charmbracelet/catwalk
133 cmp stdout info1.txt
134@@ -32,6 +35,7 @@ soft repo description charmbracelet/catwalk
135 soft repo description charmbracelet/catwalk "testing repo"
136 soft repo description charmbracelet/catwalk
137 stdout 'testing repo'
138+readfile $DATA_PATH/repos/charmbracelet/catwalk.git/description 'testing repo'
139 
140 # rename
141 soft repo rename charmbracelet/catwalk charmbracelet/test
142@@ -41,11 +45,13 @@ stdout charmbracelet/test # TODO: shouldn't this still show the project-name?
143 # check its not private
144 soft repo private charmbracelet/test
145 stdout false
146+exists $DATA_PATH/repos/charmbracelet/test.git/git-daemon-export-ok
147 
148 # make it private
149 soft repo private charmbracelet/test  true
150 soft repo private charmbracelet/test
151 stdout true
152+! exists $DATA_PATH/repos/charmbracelet/test.git/git-daemon-export-ok
153 
154 # check its not hidden
155 soft repo hidden charmbracelet/test
156diff --git a/testscript/testdata/repo-create.txtar b/testscript/testdata/repo-create.txtar
157index 633142202c19dae41e0878d1431a1a66027a4e31..31c7028b8faaea888e34fad9f2b6f2211b08dfbf 100644
158--- a/testscript/testdata/repo-create.txtar
159+++ b/testscript/testdata/repo-create.txtar
160@@ -9,8 +9,10 @@ soft repo hidden repo1
161 stdout true
162 soft repo private repo1
163 stdout true
164+! exists $DATA_PATH/repos/repo1.git/git-daemon-export-ok
165 soft repo description repo1
166 stdout 'description'
167+readfile $DATA_PATH/repos/repo1.git/description 'description'
168 soft repo project-name repo1
169 stdout 'repo1'
170