aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGabriel A. Giovanini <mail@gabrielgio.me>2024-06-09 19:35:34 +0200
committerGabriel A. Giovanini <mail@gabrielgio.me>2024-06-09 19:35:34 +0200
commit02614b3781f6acdfc6df0e7b07d856b2779c4ac7 (patch)
treef23518fcf263be3236265852ba338c4fec137b41
parentb1ad6e98445cf7dafa6fec1e2e769051fe7cb748 (diff)
downloadcerrado-02614b3781f6acdfc6df0e7b07d856b2779c4ac7.tar.gz
cerrado-02614b3781f6acdfc6df0e7b07d856b2779c4ac7.tar.bz2
cerrado-02614b3781f6acdfc6df0e7b07d856b2779c4ac7.zip
feat: Per repository configuration
-rw-r--r--config.example.scfg12
-rw-r--r--pkg/config/config.go134
-rw-r--r--pkg/config/config_test.go82
-rw-r--r--pkg/service/git.go19
-rw-r--r--pkg/u/list.go8
-rw-r--r--pkg/u/list_test.go30
-rw-r--r--templates/base.qtpl2
-rw-r--r--templates/base.qtpl.go2
8 files changed, 243 insertions, 46 deletions
diff --git a/config.example.scfg b/config.example.scfg
index 1e3180f..3961e51 100644
--- a/config.example.scfg
+++ b/config.example.scfg
@@ -3,6 +3,12 @@ scan /srv/git/ {
public true
}
+repository /srv/git/cerrado.git {
+ name cerrado
+ description "Self host single person forge"
+ public true
+}
+
# TBD
#user admin:iKlvHe1g0UoXE
#
@@ -15,9 +21,3 @@ scan /srv/git/ {
# default false
#}
#
-#repository cerrado {
-# title Cerrado
-# description "Self host single person readonly forge"
-# list main
-# public true
-#}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 419d49d..3e539f7 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -6,6 +6,7 @@ import (
"io"
"os"
"path"
+ "path/filepath"
"strconv"
"git.gabrielgio.me/cerrado/pkg/u"
@@ -13,8 +14,9 @@ import (
)
var (
- ScanPathErr = errors.New("Scan path does not exist")
- RepoPathErr = errors.New("Repository path does not exist")
+ ScanPathErr = errors.New("Scan path does not exist")
+ RepoPathErr = errors.New("Repository path does not exist")
+ InvalidPropertyErr = errors.New("Invalid property")
)
type (
@@ -26,16 +28,19 @@ type (
}
// configuration represents file configuration.
+ // fields needs to be exported to cmp to work
configuration struct {
- Scan *scan
- RootReadme string
+ Scan *scan
+ RootReadme string
+ Repositories []*GitRepositoryConfiguration
}
// This is a per repository configuration.
GitRepositoryConfiguration struct {
- Name string
- Path string
- Public bool
+ Name string
+ Path string
+ Description string
+ Public bool
}
// ConfigurationRepository represents the configuration repository (as in
@@ -60,13 +65,17 @@ func LoadConfigurationRepository(configPath string) (*ConfigurationRepository, e
}
repo := &ConfigurationRepository{
- rootReadme: config.RootReadme,
+ rootReadme: config.RootReadme,
+ repositories: config.Repositories,
}
- err = repo.expandOnScanPath(config.Scan.Path, config.Scan.Public)
- if err != nil {
- return nil, err
+ if config.Scan.Path != "" {
+ err = repo.expandOnScanPath(config.Scan.Path, config.Scan.Public)
+ if err != nil {
+ return nil, err
+ }
}
+
return repo, nil
}
@@ -104,22 +113,32 @@ func (c *ConfigurationRepository) expandOnScanPath(scanPath string, public bool)
return err
}
- c.repositories = make([]*GitRepositoryConfiguration, 0)
for _, e := range entries {
if !e.IsDir() {
continue
}
fullPath := path.Join(scanPath, e.Name())
- c.repositories = append(c.repositories, &GitRepositoryConfiguration{
- Name: e.Name(),
- Path: fullPath,
- Public: public,
- })
+ if !c.repoExits(fullPath) {
+ c.repositories = append(c.repositories, &GitRepositoryConfiguration{
+ Name: e.Name(),
+ Path: fullPath,
+ Public: public,
+ })
+ }
}
return nil
}
+func (c *ConfigurationRepository) repoExits(path string) bool {
+ for _, r := range c.repositories {
+ if path == r.Path {
+ return true
+ }
+ }
+ return false
+}
+
func parse(r io.Reader) (*configuration, error) {
block, err := scfg.Read(r)
if err != nil {
@@ -138,16 +157,82 @@ func parse(r io.Reader) (*configuration, error) {
return nil, err
}
+ err = setRepositories(block, &config.Repositories)
+ if err != nil {
+ return nil, err
+ }
+
return config, nil
}
+func setRepositories(block scfg.Block, repositories *[]*GitRepositoryConfiguration) error {
+ blocks := block.GetAll("repository")
+
+ for _, r := range blocks {
+ if len(r.Params) != 1 {
+ return fmt.Errorf(
+ "Invlid number of params for repository: %w",
+ InvalidPropertyErr,
+ )
+ }
+
+ path := u.FirstOrZero(r.Params)
+ repository := defaultRepisotryConfiguration(path)
+
+ for _, d := range r.Children {
+ // under repository there is only single param properties
+ if len(d.Params) != 1 {
+ return fmt.Errorf(
+ "Invlid number of params for %s: %w",
+ d.Name,
+ InvalidPropertyErr,
+ )
+ }
+
+ switch d.Name {
+ case "name":
+ if err := setString(d, &repository.Name); err != nil {
+ return err
+ }
+ case "description":
+ if err := setString(d, &repository.Description); err != nil {
+ return err
+ }
+ case "public":
+ if err := setBool(d, &repository.Public); err != nil {
+ return err
+ }
+ }
+ }
+
+ *repositories = append(*repositories, repository)
+ }
+
+ return nil
+}
+
func defaultConfiguration() *configuration {
return &configuration{
- Scan: &scan{
- Public: true,
- Path: "",
- },
- RootReadme: "",
+ Scan: defaultScan(),
+ RootReadme: "",
+ Repositories: make([]*GitRepositoryConfiguration, 0),
+ }
+}
+
+func defaultScan() *scan {
+ return &scan{
+ Public: false,
+ Path: "",
+ }
+
+}
+
+func defaultRepisotryConfiguration(path string) *GitRepositoryConfiguration {
+ return &GitRepositoryConfiguration{
+ Path: path,
+ Name: filepath.Base(path),
+ Description: "",
+ Public: false,
}
}
@@ -158,6 +243,9 @@ func setRootReadme(block scfg.Block, readme *string) error {
func setScan(block scfg.Block, scan *scan) error {
scanDir := block.Get("scan")
+ if scanDir == nil {
+ return nil
+ }
err := setString(scanDir, &scan.Path)
if err != nil {
return err
@@ -182,7 +270,7 @@ func setBool(dir *scfg.Directive, field *bool) error {
func setString(dir *scfg.Directive, field *string) error {
if dir != nil {
- *field, _ = u.First(dir.Params)
+ *field = u.FirstOrZero(dir.Params)
}
return nil
}
diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go
index 7afbaef..9109ecb 100644
--- a/pkg/config/config_test.go
+++ b/pkg/config/config_test.go
@@ -19,21 +19,94 @@ func TestFileParsing(t *testing.T) {
config: `scan "/srv/git"`,
expectedConfig: &configuration{
Scan: &scan{
- Public: true,
+ Public: false,
Path: "/srv/git",
},
+ Repositories: []*GitRepositoryConfiguration{},
},
},
{
name: "complete scan",
- config: `scan "/srv/git" {
- public false
+ config: `
+scan "/srv/git" {
+ public true
}`,
expectedConfig: &configuration{
Scan: &scan{
- Public: false,
+ Public: true,
Path: "/srv/git",
},
+ Repositories: []*GitRepositoryConfiguration{},
+ },
+ },
+ {
+ name: "minimal repository",
+ config: `repository /srv/git/cerrado.git`,
+ expectedConfig: &configuration{
+ Scan: defaultScan(),
+ Repositories: []*GitRepositoryConfiguration{
+ {
+ Name: "cerrado.git",
+ Path: "/srv/git/cerrado.git",
+ Description: "",
+ Public: false,
+ },
+ },
+ },
+ },
+ {
+ name: "complete repository",
+ config: `
+repository /srv/git/cerrado.git {
+ name cerrado
+ description "Single person forge"
+ public true
+}`,
+ expectedConfig: &configuration{
+ Scan: defaultScan(),
+ Repositories: []*GitRepositoryConfiguration{
+ {
+ Name: "cerrado",
+ Path: "/srv/git/cerrado.git",
+ Description: "Single person forge",
+ Public: true,
+ },
+ },
+ },
+ },
+ {
+ name: "complete",
+ config: `
+scan "/srv/git" {
+ public true
+}
+
+repository /srv/git/linux.git
+
+repository /srv/git/cerrado.git {
+ name cerrado
+ description "Single person forge"
+ public true
+}`,
+ expectedConfig: &configuration{
+ Scan: &scan{
+ Public: true,
+ Path: "/srv/git",
+ },
+ Repositories: []*GitRepositoryConfiguration{
+ {
+ Name: "linux.git",
+ Path: "/srv/git/linux.git",
+ Description: "",
+ Public: false,
+ },
+ {
+ Name: "cerrado",
+ Path: "/srv/git/cerrado.git",
+ Description: "Single person forge",
+ Public: true,
+ },
+ },
},
},
}
@@ -49,7 +122,6 @@ func TestFileParsing(t *testing.T) {
if diff := cmp.Diff(tc.expectedConfig, config); diff != "" {
t.Errorf("Wrong result given - wanted + got\n %s", diff)
}
-
})
}
diff --git a/pkg/service/git.go b/pkg/service/git.go
index 94e2adc..7418d97 100644
--- a/pkg/service/git.go
+++ b/pkg/service/git.go
@@ -16,7 +16,6 @@ import (
type (
Repository struct {
Name string
- Title string
Description string
LastCommitDate string
Ref string
@@ -48,8 +47,8 @@ func NewGitService(configRepo configurationRepository) *GitService {
func (g *GitService) ListRepositories() ([]*Repository, error) {
rs := g.configRepo.List()
- repos := make([]*Repository, len(rs))
- for i, r := range rs {
+ repos := make([]*Repository, 0, len(rs))
+ for _, r := range rs {
repo, err := git.OpenRepository(r.Path)
if err != nil {
return nil, err
@@ -57,12 +56,14 @@ func (g *GitService) ListRepositories() ([]*Repository, error) {
obj, err := repo.LastCommit()
if err != nil {
- return nil, err
+ slog.Error("Error fetching last commit", "repository", r.Path, "error", err)
+ continue
}
head, err := repo.Head()
if err != nil {
- return nil, err
+ slog.Error("Error fetching head", "repository", r.Path, "error", err)
+ continue
}
d := path.Join(r.Path, "description")
@@ -75,14 +76,12 @@ func (g *GitService) ListRepositories() ([]*Repository, error) {
}
}
- baseName := path.Base(r.Path)
- repos[i] = &Repository{
- Name: baseName,
- Title: baseName,
+ repos = append(repos, &Repository{
+ Name: r.Name,
Description: description,
LastCommitDate: obj.Author.When.Format(timeFormat),
Ref: head.Name().Short(),
- }
+ })
}
return repos, nil
diff --git a/pkg/u/list.go b/pkg/u/list.go
index cf71909..7271ef3 100644
--- a/pkg/u/list.go
+++ b/pkg/u/list.go
@@ -16,6 +16,14 @@ func FirstOrZero[T any](v []T) T {
return v[0]
}
+func Map[T any, V any](ts []T, fun func(T) V) []V {
+ rs := make([]V, len(ts))
+ for i := range ts {
+ rs[i] = fun(ts[i])
+ }
+ return rs
+}
+
func ChunkBy[T any](items []T, chunkSize int) [][]T {
var chunks = make([][]T, 0, (len(items)/chunkSize)+1)
for chunkSize < len(items) {
diff --git a/pkg/u/list_test.go b/pkg/u/list_test.go
index 805a209..3a856b9 100644
--- a/pkg/u/list_test.go
+++ b/pkg/u/list_test.go
@@ -3,6 +3,7 @@
package u
import (
+ "strconv"
"testing"
"github.com/google/go-cmp/cmp"
@@ -129,3 +130,32 @@ func TestFirstOrZero(t *testing.T) {
})
}
}
+
+func TestMap(t *testing.T) {
+ testCases := []struct {
+ name string
+ in []int
+ out []string
+ }{
+ {
+ name: "empty",
+ in: []int{},
+ out: []string{},
+ },
+ {
+ name: "not empty",
+ in: []int{1, 2, 3},
+ out: []string{"1", "2", "3"},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ out := Map(tc.in, func(v int) string { return strconv.Itoa(v) })
+
+ if diff := cmp.Diff(tc.out, out); diff != "" {
+ t.Errorf("Map error:\n%s", diff)
+ }
+ })
+ }
+}
diff --git a/templates/base.qtpl b/templates/base.qtpl
index 9b0c4f5..ae9f7a6 100644
--- a/templates/base.qtpl
+++ b/templates/base.qtpl
@@ -42,7 +42,7 @@ Page prints a page implementing Page interface.
<head>
<meta charset="utf-8">
<link rel="icon" href="data:,">
- <title>cerrado | {%= p.Title() %}</title>
+ <title>{%= p.Title() %}</title>
<link rel="stylesheet" href="/static/main{%s Slug%}.css">
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta name="viewport" content="width=device-width, initial-scale=1" />
diff --git a/templates/base.qtpl.go b/templates/base.qtpl.go
index d2bcc73..bc40252 100644
--- a/templates/base.qtpl.go
+++ b/templates/base.qtpl.go
@@ -87,7 +87,7 @@ func StreamPageTemplate(qw422016 *qt422016.Writer, p Page) {
<head>
<meta charset="utf-8">
<link rel="icon" href="data:,">
- <title>cerrado | `)
+ <title>`)
//line base.qtpl:45
p.StreamTitle(qw422016)
//line base.qtpl:45