Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/cli/repos.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fullsend repos init <owner/repo> --mint-project <PROJECT>
The command discovers repos by checking:

1. **Per-repo guard variable** (`FULLSEND_PER_REPO_INSTALL`) — identifies per-repo installations
2. **Per-org config enrollment** (`config.yaml` in `.fullsend` repo) — identifies per-org installations
2. **Per-org config enrollment** (`config.yaml` in `.fullsend` repo) — identifies per-org installations; if no mint URL is set in the org config, falls back to the `FULLSEND_MINT_URL` org-level Actions variable
3. **Workflow ref** — extracts the `@ref` from scaffold shim workflow files

### Defaults computation
Expand Down
4 changes: 3 additions & 1 deletion docs/plans/repos-init.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,9 @@ manifest contains one entry.
- Mark `source: per-repo`.
- Else if repo appears in per-org enrollment with
`enabled: true`:
- Use mint URL and config from per-org `config.yaml`.
- Use mint URL and config from per-org `config.yaml`. If no
mint URL is set in `config.yaml`, fall back to reading the
`FULLSEND_MINT_URL` org-level variable via `GetOrgVariable`.
- Read the per-org shim workflow file and extract `@ref` from
the `uses:` line (same as per-repo discovery). Do not use
`config.DefaultUpstreamRef` — it is `v0`, a major-version
Expand Down
8 changes: 8 additions & 0 deletions internal/repos/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,14 @@ func discoverRepo(ctx context.Context, client forge.Client,
if mintURL := orgCfg.DispatchSettings().MintURL; mintURL != "" {
d.MintURL = mintURL
}
if d.MintURL == "" {
Comment thread
ggallen marked this conversation as resolved.
v, exists, err := client.GetOrgVariable(ctx, owner, "FULLSEND_MINT_URL")
if err != nil {
progress(fullName, "discover", fmt.Sprintf("warning: could not read org variable FULLSEND_MINT_URL: %v", err))
} else if exists {
d.MintURL = v
}
}
d.FullsendRef = ref
return d, nil
}
Expand Down
93 changes: 93 additions & 0 deletions internal/repos/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package repos
import (
"context"
"fmt"
"strings"
"testing"

"github.com/fullsend-ai/fullsend/internal/config"
Expand Down Expand Up @@ -1018,6 +1019,98 @@ func TestDiscoverRepo_New(t *testing.T) {
assert.Empty(t, d.FullsendRef)
}

// --- discoverRepo: org variable fallback tests ---

func TestDiscoverRepo_PerOrg_OrgVarFallback(t *testing.T) {
fc := forge.NewFakeClient()
setWorkflowFile(fc, "acme", "api",
" uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v2.1.0")

// Org config has no mint_url in dispatch settings.
orgCfg := &config.OrgConfig{
Repos: map[string]config.RepoConfig{
"api": {Enabled: true},
},
}

// Set org-level variable as fallback.
fc.OrgVariables = map[string]bool{
"acme/FULLSEND_MINT_URL": true,
}
fc.OrgVariableValues = map[string]string{
"acme/FULLSEND_MINT_URL": "https://mint.example.com",
}

d, err := discoverRepo(context.Background(), fc, "acme", "api", orgCfg, nopProgress)
require.NoError(t, err)
assert.Equal(t, "per-org", d.Source)
assert.Equal(t, "https://mint.example.com", d.MintURL)
}

func TestDiscoverRepo_PerOrg_OrgConfigTakesPrecedence(t *testing.T) {
fc := forge.NewFakeClient()
setWorkflowFile(fc, "acme", "api",
" uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v2.1.0")

// Org config has a mint_url.
orgCfg := &config.OrgConfig{
Dispatch: config.DispatchConfig{
MintURL: "https://config-mint.example.com",
},
Repos: map[string]config.RepoConfig{
"api": {Enabled: true},
},
}

// Org variable also set — should NOT be used.
fc.OrgVariables = map[string]bool{
"acme/FULLSEND_MINT_URL": true,
}
fc.OrgVariableValues = map[string]string{
"acme/FULLSEND_MINT_URL": "https://var-mint.example.com",
}

d, err := discoverRepo(context.Background(), fc, "acme", "api", orgCfg, nopProgress)
require.NoError(t, err)
assert.Equal(t, "per-org", d.Source)
assert.Equal(t, "https://config-mint.example.com", d.MintURL)
}

func TestDiscoverRepo_PerOrg_OrgVarError_NonFatal(t *testing.T) {
fc := forge.NewFakeClient()
setWorkflowFile(fc, "acme", "api",
" uses: fullsend-ai/fullsend/.github/workflows/reusable-dispatch.yml@v2.1.0")
fc.Errors["GetOrgVariable"] = fmt.Errorf("forbidden")

// Org config has no mint_url.
orgCfg := &config.OrgConfig{
Repos: map[string]config.RepoConfig{
"api": {Enabled: true},
},
}

var warnings []string
progress := func(_, _, msg string) {
warnings = append(warnings, msg)
}

d, err := discoverRepo(context.Background(), fc, "acme", "api", orgCfg, progress)
require.NoError(t, err)
assert.Equal(t, "per-org", d.Source)
assert.Empty(t, d.MintURL)

// Should have logged a warning about the org variable.
hasWarning := false
for _, w := range warnings {
if strings.Contains(w, "FULLSEND_MINT_URL") &&
strings.Contains(w, "warning") {
hasWarning = true
break
}
}
assert.True(t, hasWarning, "expected a warning about GetOrgVariable failure")
}

// --- readWorkflowRef tests ---

func TestReadWorkflowRef_YmlExtension(t *testing.T) {
Expand Down
Loading