diff --git a/go.mod b/go.mod index 9c0a0c9ae77..0ea2cd6e9a9 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,8 @@ go 1.21 require ( github.com/PuerkitoBio/goquery v1.8.1 github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774 + github.com/google/go-github/v63 v63.0.0 + github.com/kr/pretty v0.3.1 github.com/otiai10/copy v1.14.0 github.com/yuin/goldmark v1.6.0 golang.org/x/oauth2 v0.15.0 @@ -13,6 +15,9 @@ require ( require ( github.com/andybalholm/cascadia v1.3.1 // indirect github.com/golang/protobuf v1.5.3 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.18.0 // indirect diff --git a/go.sum b/go.sum index 95538c178ec..6c2e9fc2dfd 100644 --- a/go.sum +++ b/go.sum @@ -4,17 +4,30 @@ github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x0 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774 h1:HrMVYtly2IVqg9EBooHsakQ256ueojP7QuG32K71X/U= github.com/avelino/slugify v0.0.0-20180501145920-855f152bd774/go.mod h1:5wi5YYOpfuAKwL5XLFYopbgIl/v7NZxaJpa/4X6yFKE= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-github/v63 v63.0.0 h1:13xwK/wk9alSokujB9lJkuzdmQuVn2QCPeck76wR3nE= +github.com/google/go-github/v63 v63.0.0/go.mod h1:IqbcrgUmIcEaioWrGYei/09o+ge5vhffGOcxrO0AfmA= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68= github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= diff --git a/identify_old_repositories_test.go b/identify_old_repositories_test.go new file mode 100644 index 00000000000..7090d737302 --- /dev/null +++ b/identify_old_repositories_test.go @@ -0,0 +1,174 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "regexp" + "testing" + "time" + + "github.com/kr/pretty" +) + +const gqlQuery = ` +query { + repository(owner:"%s", name:"%s") { + name + openIssues: issues(states: OPEN) { + totalCount + } + lastUpdatedIssue: issues(first: 1, orderBy: {field: UPDATED_AT, direction: DESC}) { + edges { + node { + updatedAt + } + } + } + defaultBranchRef { + target { + ... on Commit { + committedDate + } + } + } + forks { + totalCount + } + stargazers { + totalCount + } + } +} +` + +var authToken = os.ExpandEnv("$GITHUB_PAT") + +func repoTimeSinceLastUpdated(repo GithubRepoResponse) time.Duration { + return time.Since(repo.Data.Repository.DefaultBranchRef.Target.CommittedDate) +} + +type awesomeRepo struct { + RepoName string + RepoOrganization string + ReadmeLine int +} + +type GithubRepoResponse struct { + Data struct { + Repository struct { + Name string `json:"name"` + OpenIssues struct { + TotalCount int `json:"totalCount"` + } `json:"openIssues"` + LastUpdatedIssue struct { + Edges []struct { + Node struct { + UpdatedAt time.Time `json:"updatedAt"` + } `json:"node"` + } `json:"edges"` + } `json:"lastUpdatedIssue"` + DefaultBranchRef struct { + Target struct { + CommittedDate time.Time `json:"committedDate"` + } `json:"target"` + } `json:"defaultBranchRef"` + Forks struct { + TotalCount int `json:"totalCount"` + } `json:"forks"` + Stargazers struct { + TotalCount int `json:"totalCount"` + } `json:"stargazers"` + } `json:"repository"` + } `json:"data"` +} + +type repoStats struct { + TimeLastUpdated time.Duration + Stars int + Forks int +} + +func checkHealth(repo awesomeRepo) (*repoStats, error) { + requestBody := struct { + Query string `json:"query"` + }{ + fmt.Sprintf(gqlQuery, repo.RepoOrganization, repo.RepoName), + } + body, err := json.Marshal(requestBody) + if err != nil { + return nil, err + } + + req, err := http.NewRequestWithContext(context.Background(), "POST", "https://api.github.com/graphql", bytes.NewReader(body)) + if err != nil { + return nil, err + } + + req.Header.Add("Authorization", "Bearer "+authToken) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer res.Body.Close() + resbody, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + var response GithubRepoResponse + + if err := json.Unmarshal(resbody, &response); err != nil { + return nil, err + } + timeSinceUpdated := repoTimeSinceLastUpdated(response) + health := repoStats{ + TimeLastUpdated: timeSinceUpdated, + } + return &health, nil +} + +func TestCleanUpLibs(t *testing.T) { + bytes, err := os.ReadFile("README.md") + if err != nil { + t.Error(err) + } + + r := regexp.MustCompile(`\((https.*)\)`) + + // only support github right now + for _, result := range r.FindAllSubmatch(bytes, -1) { + org, p, err := extractGithubRepo(result[1]) + if err != nil { + continue + } + fmt.Println("org", org, "package", p, "err", err) + + a := awesomeRepo{ + RepoOrganization: org, + RepoName: p, + } + + health, err := checkHealth(a) + if err != nil { + t.Error(err) + } + pretty.Println(health) + } +} + +var getRepo = regexp.MustCompile(`https://github.com/(.*)/(.*)[\)]?`) + +func extractGithubRepo(b []byte) (string, string, error) { + results := getRepo.FindSubmatch(b) + if len(results) == 0 { + return "", "", fmt.Errorf("no match found for %s", string(b)) + } + + return string(results[1]), string(results[2]), nil +}