Skip to content
Merged
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
11 changes: 7 additions & 4 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,16 +264,19 @@ Owner-scoped GitHub ingestion may emit:
- `github.public_repo_count`

Notes:
- owner-scoped rows should use `scope_key = owner:<owner>`
- aggregate metrics are computed across the repositories included by that owner-scoped binding
- owner-scoped rows should use `scope_key = "owner:<owner>"`
- aggregate metrics are computed across the public repositories included by that owner-scoped binding
- the runtime resolves owner scope through the GitHub owner listing endpoint selected by `owner_type`
- when `GITHUB_TOKEN` is present in the runtime environment, GitHub requests use it to improve rate-limit headroom; unauthenticated mode remains supported but is more exposed to lower anonymous rate limits
- private repositories remain out of scope for owner aggregation even when authenticated
- public archived repositories and public forks are currently included in the owner aggregate because the binding contract does not yet define archive/fork filters
- owner-scoped `github.contributors` is intentionally deferred for `v0.3.0` and must not be implied by the presence of `scope.owner`

Examples:
```json
{
"owner": "Blockstream",
"owner_type": "organization",
"include_owner_repos": true
"owner_type": "organization"
}
```

Expand Down
6 changes: 6 additions & 0 deletions docs/reference-dataset.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ Important `v0.3.0` limitation:
- owner-scoped `github.contributors` is intentionally deferred
- contributor counts are only expected from explicit repo-scoped GitHub bindings

Operational note for owner-scoped bindings in `v0.3.0`:
- the runtime aggregates across the public repositories returned by the configured owner endpoint
- `GITHUB_TOKEN` is optional and only improves rate-limit headroom; unauthenticated mode remains supported
- public archived repositories and public forks are currently included in that aggregate
- private repositories are excluded from the owner aggregate

### 2. News RSS

Every tracked entity in the demo pack is assumed to have a keyword-based news binding.
Expand Down
4 changes: 4 additions & 0 deletions internal/config/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,10 @@ func (v *packValidator) validateGitHubScope(file, path string, scope JSONMap) {
}
}

if owner != "" && len(repos) > 0 {
v.addIssue(file, path, "must define either a non-empty repos list or an owner/owner_type pair, not both")
}

if owner == "" && len(repos) == 0 {
v.addIssue(
file,
Expand Down
71 changes: 71 additions & 0 deletions internal/config/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,77 @@ func TestValidatePackAggregatesCrossFileIssues(t *testing.T) {
}
}

func TestValidatePackRejectsMixedGitHubRepoAndOwnerScopes(t *testing.T) {
t.Parallel()

pack := Pack{
Entities: EntitiesFile{
Version: 1,
DatasetID: "demo",
DatasetName: "Demo",
Entities: []Entity{
{
ID: "org-1",
Slug: "org-1",
Kind: "organization",
Name: "Org 1",
},
},
Relationships: []Relationship{},
},
Sources: SourcesFile{
Version: 1,
DatasetID: "demo",
Sources: []Source{
{
ID: "github",
Kind: "github",
Enabled: true,
},
},
Bindings: []Binding{
{
ID: "github-binding-1",
EntityID: "org-1",
SourceID: "github",
Enabled: true,
Scope: JSONMap{
"owner": "example",
"owner_type": "organization",
"repos": []string{"example/repo-1"},
},
},
},
},
Alerts: AlertsFile{
Version: 1,
DatasetID: "demo",
Rules: []AlertRule{},
},
Schedules: SchedulesFile{
Version: 1,
DatasetID: "demo",
Jobs: []ScheduleJob{},
},
}

err := ValidatePack(pack)
if err == nil {
t.Fatal("ValidatePack() error = nil, want validation error")
}

var validationErr *ValidationError
if !errors.As(err, &validationErr) {
t.Fatalf("ValidatePack() error = %T, want *ValidationError", err)
}

got := joinIssues(validationErr.Issues)
want := `sources.json: bindings[0].scope: must define either a non-empty repos list or an owner/owner_type pair, not both`
if !strings.Contains(got, want) {
t.Fatalf("ValidatePack() issues missing %q\nGot:\n%s", want, got)
}
}

func joinIssues(issues []ValidationIssue) string {
lines := make([]string, 0, len(issues))
for _, issue := range issues {
Expand Down
Loading
Loading