wip: commit progress
This commit is contained in:
parent
d2c5f8d3bd
commit
25d301c654
5
.env_example
Normal file
5
.env_example
Normal file
@ -0,0 +1,5 @@
|
||||
ACTOR=
|
||||
BASE_REF=
|
||||
HEAD_REF=
|
||||
GITHUB_API_URL=
|
||||
GITHUB_API_TOKEN=
|
||||
8
.github/workflows/ci.yaml
vendored
8
.github/workflows/ci.yaml
vendored
@ -10,9 +10,11 @@ jobs:
|
||||
validate-contributors:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
actor: ${{ github.actor }}
|
||||
base_ref: ${{ github.base_ref }}
|
||||
head_ref: ${{ github.head_ref }}
|
||||
ACTOR: ${{ github.actor }}
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
GITHUB_API_URL: ${{ github.api_url }}
|
||||
GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
@ -3,15 +3,28 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultGithubAPIRoute = "https://api.github.com/"
|
||||
|
||||
const (
|
||||
actionsActorKey = "ACTOR"
|
||||
actionsBaseRefKey = "BASE_REF"
|
||||
actionsHeadRefKey = "HEAD_REF"
|
||||
)
|
||||
|
||||
const (
|
||||
actionsActorKey = "actor"
|
||||
actionsBaseRefKey = "base_ref"
|
||||
actionsHeadRefKey = "head_ref"
|
||||
githubAPIURLKey = "GITHUB_API_URL"
|
||||
githubAPITokenKey = "GITHUB_API_TOKEN"
|
||||
)
|
||||
|
||||
// ActionsActor returns the username of the GitHub user who triggered the
|
||||
@ -20,7 +33,7 @@ const (
|
||||
func ActionsActor() (string, error) {
|
||||
username := os.Getenv(actionsActorKey)
|
||||
if username == "" {
|
||||
return "", fmt.Errorf("value for %q is not in env. Please update the CI script to load the value in during CI", actionsActorKey)
|
||||
return "", fmt.Errorf("value for %q is not in env. If running from CI, please add value via ci.yaml file", actionsActorKey)
|
||||
}
|
||||
return username, nil
|
||||
}
|
||||
@ -31,8 +44,113 @@ func ActionsActor() (string, error) {
|
||||
func ActionsRefs() (string, string, error) {
|
||||
baseRef := os.Getenv(actionsBaseRefKey)
|
||||
headRef := os.Getenv(actionsHeadRefKey)
|
||||
fmt.Println("Base ref: ", baseRef)
|
||||
fmt.Println("Head ref: ", headRef)
|
||||
|
||||
return "", "", errors.New("we ain't ready yet")
|
||||
if baseRef == "" && headRef == "" {
|
||||
return "", "", fmt.Errorf("values for %q and %q are not in env. If running from CI, please add values via ci.yaml file", actionsHeadRefKey, actionsBaseRefKey)
|
||||
} else if headRef == "" {
|
||||
return "", "", fmt.Errorf("value for %q is not in env. If running from CI, please add value via ci.yaml file", actionsHeadRefKey)
|
||||
} else if baseRef == "" {
|
||||
return "", "", fmt.Errorf("value for %q is not in env. If running from CI, please add value via ci.yaml file", actionsBaseRefKey)
|
||||
}
|
||||
|
||||
return headRef, baseRef, nil
|
||||
}
|
||||
|
||||
// CoderEmployees represents all members of the Coder GitHub organization. This
|
||||
// value should not be instantiated from outside the package, and should instead
|
||||
// be created via one of the package's exported functions.
|
||||
type CoderEmployees struct {
|
||||
// Have map defined as private field to make sure that it can't ever be
|
||||
// mutated from an outside package
|
||||
_employees map[string]struct{}
|
||||
}
|
||||
|
||||
// IsEmployee takes a GitHub username and indicates whether the matching user is
|
||||
// a member of the Coder organization
|
||||
func (ce *CoderEmployees) IsEmployee(username string) bool {
|
||||
if ce._employees == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := ce._employees[username]
|
||||
return ok
|
||||
}
|
||||
|
||||
// TotalEmployees returns the number of members in the Coder organization
|
||||
func (ce *CoderEmployees) TotalEmployees() int {
|
||||
return len(ce._employees)
|
||||
}
|
||||
|
||||
type ghOrganizationMember struct {
|
||||
Login string `json:"login"`
|
||||
}
|
||||
|
||||
type ghRateLimitedRes struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func parseResponse[V any](b []byte) (V, error) {
|
||||
var want V
|
||||
var rateLimitedRes ghRateLimitedRes
|
||||
|
||||
if err := json.Unmarshal(b, &rateLimitedRes); err != nil {
|
||||
return want, err
|
||||
}
|
||||
if isRateLimited := strings.Contains(rateLimitedRes.Message, "API rate limit exceeded for "); isRateLimited {
|
||||
return want, errors.New("request was rate-limited")
|
||||
}
|
||||
if err := json.Unmarshal(b, &want); err != nil {
|
||||
return want, err
|
||||
}
|
||||
|
||||
return want, nil
|
||||
}
|
||||
|
||||
// CoderEmployeeUsernames requests from the GitHub API the list of all usernames
|
||||
// of people who are employees of Coder.
|
||||
func CoderEmployeeUsernames() (CoderEmployees, error) {
|
||||
apiURL := os.Getenv(githubAPIURLKey)
|
||||
if apiURL == "" {
|
||||
log.Printf("API URL not set via env key %q. Defaulting to %q\n", githubAPIURLKey, defaultGithubAPIRoute)
|
||||
apiURL = defaultGithubAPIRoute
|
||||
}
|
||||
token := os.Getenv(githubAPITokenKey)
|
||||
if token == "" {
|
||||
log.Printf("API token not set via env key %q. All requests will be non-authenticated and subject to more aggressive rate limiting", githubAPITokenKey)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", apiURL+"/orgs/coder/members", nil)
|
||||
if err != nil {
|
||||
return CoderEmployees{}, fmt.Errorf("coder employee names: %v", err)
|
||||
}
|
||||
if token != "" {
|
||||
req.Header.Add("Authorization", "Bearer "+token)
|
||||
}
|
||||
|
||||
client := http.Client{Timeout: 5 * time.Second}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return CoderEmployees{}, fmt.Errorf("coder employee names: %v", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return CoderEmployees{}, fmt.Errorf("coder employee names: got back status code %d", res.StatusCode)
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return CoderEmployees{}, fmt.Errorf("coder employee names: %v", err)
|
||||
}
|
||||
rawMembers, err := parseResponse[[]ghOrganizationMember](b)
|
||||
if err != nil {
|
||||
return CoderEmployees{}, fmt.Errorf("coder employee names: %v", err)
|
||||
}
|
||||
|
||||
employeesSet := map[string]struct{}{}
|
||||
for _, m := range rawMembers {
|
||||
employeesSet[m.Login] = struct{}{}
|
||||
}
|
||||
return CoderEmployees{
|
||||
_employees: employeesSet,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -10,18 +10,30 @@ import (
|
||||
"log"
|
||||
|
||||
"coder.com/coder-registry/cmd/github"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
username, err := github.ActionsActor()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
log.Printf("running as %q\n", username)
|
||||
_, _, err = github.ActionsRefs()
|
||||
log.Printf("Running validation for user %q", username)
|
||||
headRef, baseRef, err := github.ActionsRefs()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
log.Printf("Using branches %q and %q for validation comparison", headRef, baseRef)
|
||||
|
||||
employees, err := github.CoderEmployeeUsernames()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
log.Printf("got back %d employees\n", employees.TotalEmployees())
|
||||
|
||||
log.Println("Starting README validation")
|
||||
allReadmeFiles, err := aggregateContributorReadmeFiles()
|
||||
|
||||
2
go.mod
2
go.mod
@ -3,3 +3,5 @@ module coder.com/coder-registry
|
||||
go 1.23.2
|
||||
|
||||
require gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
require github.com/joho/godotenv v1.5.1 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@ -1,3 +1,5 @@
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user