feat(marketplace): emit clone URLs in indexes
Some checks failed
Security Scan / security (push) Failing after 9s
Test / test (push) Successful in 2m8s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 06:24:22 +00:00
parent 2f4d2e5811
commit 89925a0e83
4 changed files with 38 additions and 8 deletions

View file

@ -7,6 +7,7 @@ import (
json "dappco.re/go/core/scm/internal/ax/jsonx"
os "dappco.re/go/core/scm/internal/ax/osx"
"sort"
"strings"
core "dappco.re/go/core"
@ -23,7 +24,7 @@ const IndexVersion = 1
type Builder struct {
// BaseURL is the prefix for constructing repository URLs, e.g.
// "https://forge.lthn.ai". When set, module Repo is derived as
// BaseURL + "/" + org + "/" + code.
// BaseURL + "/" + org + "/" + code + ".git".
BaseURL string
// Org is the default organisation used when constructing Repo URLs.
@ -160,8 +161,13 @@ func (b *Builder) loadFromDir(dir string) (*manifest.Manifest, error) {
// repoURL constructs a module repository URL from the builder config.
func (b *Builder) repoURL(code string) string {
if b.BaseURL == "" || b.Org == "" {
if b.Org == "" {
return ""
}
return b.BaseURL + "/" + b.Org + "/" + code
baseURL := b.BaseURL
if baseURL == "" {
baseURL = defaultForgeURL
}
baseURL = strings.TrimSuffix(baseURL, "/")
return baseURL + "/" + b.Org + "/" + code + ".git"
}

View file

@ -61,7 +61,7 @@ func TestBuildFromDirs_Good_ManifestYAML_Good(t *testing.T) {
require.Len(t, idx.Modules, 1)
assert.Equal(t, "my-widget", idx.Modules[0].Code)
assert.Equal(t, "My Widget", idx.Modules[0].Name)
assert.Equal(t, "https://forge.lthn.ai/core/my-widget", idx.Modules[0].Repo)
assert.Equal(t, "https://forge.lthn.ai/core/my-widget.git", idx.Modules[0].Repo)
assert.Equal(t, IndexVersion, idx.Version)
}
@ -189,6 +189,20 @@ func TestBuildFromDirs_Good_NoRepoURLWithoutConfig_Good(t *testing.T) {
assert.Empty(t, idx.Modules[0].Repo)
}
func TestBuildFromDirs_Good_DefaultForgeURL_Good(t *testing.T) {
root := t.TempDir()
modDir := filepath.Join(root, "mod")
require.NoError(t, os.MkdirAll(modDir, 0755))
writeManifestYAML(t, modDir, "mod", "Module", "1.0.0")
b := &Builder{Org: "core"}
idx, err := b.BuildFromDirs(root)
require.NoError(t, err)
require.Len(t, idx.Modules, 1)
assert.Equal(t, "https://forge.lthn.ai/core/mod.git", idx.Modules[0].Repo)
}
func TestBuildFromManifests_Good(t *testing.T) {
manifests := []*manifest.Manifest{
{Code: "bravo", Name: "Bravo", Sign: "key-bravo"},
@ -268,7 +282,7 @@ func TestWriteIndex_Good_RoundTrip_Good(t *testing.T) {
require.Len(t, parsed.Modules, 1)
assert.Equal(t, "roundtrip", parsed.Modules[0].Code)
assert.Equal(t, "https://forge.lthn.ai/core/roundtrip", parsed.Modules[0].Repo)
assert.Equal(t, "https://forge.lthn.ai/core/roundtrip.git", parsed.Modules[0].Repo)
}
func TestLoadIndex_Good(t *testing.T) {

View file

@ -3,12 +3,16 @@
package marketplace
import (
"fmt"
"sort"
"strings"
"dappco.re/go/core/io"
"dappco.re/go/core/scm/manifest"
)
const defaultForgeURL = "https://forge.lthn.ai"
// IndexOptions controls how BuildIndex populates marketplace metadata.
// Usage: IndexOptions{...}
type IndexOptions struct {
@ -16,6 +20,7 @@ type IndexOptions struct {
Org string
// ForgeURL is the base URL used when constructing repo URLs.
// If empty, the default Forge URL is used.
ForgeURL string
// CategoryFn assigns a category to a module code.
@ -27,6 +32,10 @@ type IndexOptions struct {
// Categories are deduplicated and sorted.
// Usage: BuildIndex(...)
func BuildIndex(medium io.Medium, repoPaths []string, opts IndexOptions) (*Index, error) {
if opts.ForgeURL == "" {
opts.ForgeURL = defaultForgeURL
}
idx := &Index{
Version: IndexVersion,
}
@ -52,8 +61,9 @@ func BuildIndex(medium io.Medium, repoPaths []string, opts IndexOptions) (*Index
Name: m.Name,
SignKey: m.Sign,
}
if opts.ForgeURL != "" && opts.Org != "" {
module.Repo = opts.ForgeURL + "/" + opts.Org + "/" + m.Code
if opts.Org != "" {
baseURL := strings.TrimSuffix(opts.ForgeURL, "/")
module.Repo = fmt.Sprintf("%s/%s/%s.git", baseURL, opts.Org, m.Code)
}
if opts.CategoryFn != nil {
module.Category = opts.CategoryFn(m.Code)

View file

@ -48,7 +48,7 @@ sign: key-c
require.Len(t, idx.Modules, 3)
assert.Equal(t, "a", idx.Modules[0].Code)
assert.Equal(t, "https://forge.example.com/core/a", idx.Modules[0].Repo)
assert.Equal(t, "https://forge.example.com/core/a.git", idx.Modules[0].Repo)
assert.Equal(t, "tools", idx.Modules[0].Category)
assert.Equal(t, "key-a", idx.Modules[0].SignKey)
assert.Equal(t, []string{"products", "tools"}, idx.Categories)