feat(14-01): add 5 CI/CD log sources (GitHubActions, TravisCI, CircleCI, Jenkins, GitLabCI)
- GitHubActionsSource: searches GitHub code search for workflow files with provider keywords (token-gated) - TravisCISource: queries Travis CI v3 API for public build logs (credentialless) - CircleCISource: queries CircleCI v2 pipeline API for build pipelines (token-gated) - JenkinsSource: queries open Jenkins /api/json for job build consoles (credentialless) - GitLabCISource: queries GitLab projects API for CI-enabled projects (token-gated) - RegisterAll extended to 45 sources (40 Phase 10-13 + 5 Phase 14) - Integration test updated with fixtures for all 5 new sources - cmd/recon.go wires CIRCLECI_TOKEN env var
This commit is contained in:
@@ -312,6 +312,36 @@ func TestIntegration_AllSources_SweepAll(t *testing.T) {
|
||||
_, _ = w.Write([]byte(`{"packages":[{"package_id":"chart-1","name":"leaked-chart","normalized_name":"leaked-chart","repository":{"name":"bitnami","kind":0}}]}`))
|
||||
})
|
||||
|
||||
// ---- Phase 14: GitHub Actions /ghactions/search/code ----
|
||||
mux.HandleFunc("/ghactions/search/code", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"items":[{"html_url":"https://github.com/alice/repo/.github/workflows/ci.yml","repository":{"full_name":"alice/repo"}}]}`))
|
||||
})
|
||||
|
||||
// ---- Phase 14: Travis CI /travis/builds ----
|
||||
mux.HandleFunc("/travis/builds", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"builds":[{"id":12345,"state":"passed","repository":{"slug":"alice/project"}}]}`))
|
||||
})
|
||||
|
||||
// ---- Phase 14: CircleCI /circle/pipeline ----
|
||||
mux.HandleFunc("/circle/pipeline", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"items":[{"id":"pipeline-uuid-1","vcs":{"provider_name":"github","target_repository_url":"https://github.com/alice/repo"}}]}`))
|
||||
})
|
||||
|
||||
// ---- Phase 14: Jenkins /jenkins/api/json ----
|
||||
mux.HandleFunc("/jenkins/api/json", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"jobs":[{"name":"build-api","url":"https://jenkins.example.com/job/build-api/","lastBuild":{"number":42,"url":"https://jenkins.example.com/job/build-api/42/"}}]}`))
|
||||
})
|
||||
|
||||
// ---- Phase 14: GitLab CI /gitlabci/api/v4/projects ----
|
||||
mux.HandleFunc("/gitlabci/api/v4/projects", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`[{"id":100,"path_with_namespace":"alice/project","web_url":"https://gitlab.com/alice/project"}]`))
|
||||
})
|
||||
|
||||
srv := httptest.NewServer(mux)
|
||||
defer srv.Close()
|
||||
|
||||
@@ -550,9 +580,50 @@ func TestIntegration_AllSources_SweepAll(t *testing.T) {
|
||||
// helm
|
||||
eng.Register(&HelmSource{BaseURL: srv.URL + "/helm", Registry: reg, Limiters: lim, Client: NewClient()})
|
||||
|
||||
// Sanity: all 40 sources registered.
|
||||
if n := len(eng.List()); n != 40 {
|
||||
t.Fatalf("expected 40 sources on engine, got %d: %v", n, eng.List())
|
||||
// --- Phase 14: CI/CD log sources ---
|
||||
|
||||
// GitHub Actions
|
||||
eng.Register(&GitHubActionsSource{
|
||||
Token: "ghp-test",
|
||||
BaseURL: srv.URL + "/ghactions",
|
||||
Registry: reg,
|
||||
Limiters: lim,
|
||||
client: NewClient(),
|
||||
})
|
||||
// Travis CI
|
||||
eng.Register(&TravisCISource{
|
||||
BaseURL: srv.URL + "/travis",
|
||||
Registry: reg,
|
||||
Limiters: lim,
|
||||
Client: NewClient(),
|
||||
})
|
||||
// CircleCI
|
||||
eng.Register(&CircleCISource{
|
||||
Token: "test-circle-token",
|
||||
BaseURL: srv.URL + "/circle",
|
||||
Registry: reg,
|
||||
Limiters: lim,
|
||||
Client: NewClient(),
|
||||
})
|
||||
// Jenkins
|
||||
eng.Register(&JenkinsSource{
|
||||
BaseURL: srv.URL + "/jenkins",
|
||||
Registry: reg,
|
||||
Limiters: lim,
|
||||
Client: NewClient(),
|
||||
})
|
||||
// GitLab CI
|
||||
eng.Register(&GitLabCISource{
|
||||
Token: "glpat-test",
|
||||
BaseURL: srv.URL + "/gitlabci",
|
||||
Registry: reg,
|
||||
Limiters: lim,
|
||||
Client: NewClient(),
|
||||
})
|
||||
|
||||
// Sanity: all 45 sources registered.
|
||||
if n := len(eng.List()); n != 45 {
|
||||
t.Fatalf("expected 45 sources on engine, got %d: %v", n, eng.List())
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||
@@ -616,6 +687,12 @@ func TestIntegration_AllSources_SweepAll(t *testing.T) {
|
||||
"recon:k8s",
|
||||
"recon:terraform",
|
||||
"recon:helm",
|
||||
// Phase 14: CI/CD logs
|
||||
"recon:github_actions",
|
||||
"recon:travisci",
|
||||
"recon:circleci",
|
||||
"recon:jenkins",
|
||||
"recon:gitlab_ci",
|
||||
}
|
||||
for _, st := range wantTypes {
|
||||
if byType[st] == 0 {
|
||||
@@ -641,8 +718,8 @@ func TestRegisterAll_Phase12(t *testing.T) {
|
||||
})
|
||||
|
||||
names := eng.List()
|
||||
if n := len(names); n != 40 {
|
||||
t.Fatalf("expected 40 sources from RegisterAll, got %d: %v", n, names)
|
||||
if n := len(names); n != 45 {
|
||||
t.Fatalf("expected 45 sources from RegisterAll, got %d: %v", n, names)
|
||||
}
|
||||
|
||||
// Build lookup for source access.
|
||||
|
||||
Reference in New Issue
Block a user