Merge pull request #18 from coder/mes/tighten-schema

chore: update README files and validation to reflect new requirements
This commit is contained in:
Michael Smith 2025-04-28 15:41:27 -04:00 committed by GitHub
commit d4d307451f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 16 additions and 88 deletions

View File

@ -18,58 +18,21 @@ var validContributorStatuses = []string{"official", "partner", "community"}
type contributorProfileFrontmatter struct { type contributorProfileFrontmatter struct {
DisplayName string `yaml:"display_name"` DisplayName string `yaml:"display_name"`
Bio string `yaml:"bio"` Bio string `yaml:"bio"`
GithubUsername string `yaml:"github"`
// Script assumes that if value is nil, the Registry site build step will // Script assumes that if value is nil, the Registry site build step will
// backfill the value with the user's GitHub avatar URL // backfill the value with the user's GitHub avatar URL
AvatarURL *string `yaml:"avatar"` AvatarURL *string `yaml:"avatar"`
LinkedinURL *string `yaml:"linkedin"` LinkedinURL *string `yaml:"linkedin"`
WebsiteURL *string `yaml:"website"` WebsiteURL *string `yaml:"website"`
SupportEmail *string `yaml:"support_email"` SupportEmail *string `yaml:"support_email"`
EmployerGithubUsername *string `yaml:"employer_github"`
ContributorStatus *string `yaml:"status"` ContributorStatus *string `yaml:"status"`
} }
type contributorProfile struct { type contributorProfile struct {
frontmatter contributorProfileFrontmatter frontmatter contributorProfileFrontmatter
namespace string
filePath string filePath string
} }
func validateContributorGithubUsername(githubUsername string) error {
if githubUsername == "" {
return errors.New("missing GitHub username")
}
lower := strings.ToLower(githubUsername)
if uriSafe := url.PathEscape(lower); uriSafe != lower {
return fmt.Errorf("gitHub username %q is not a valid URL path segment", githubUsername)
}
return nil
}
func validateContributorEmployerGithubUsername(employerGithubUsername *string, githubUsername string) []error {
if employerGithubUsername == nil {
return nil
}
errs := []error{}
if *employerGithubUsername == "" {
errs = append(errs, errors.New("company_github field is defined but has empty value"))
return errs
}
lower := strings.ToLower(*employerGithubUsername)
if uriSafe := url.PathEscape(lower); uriSafe != lower {
errs = append(errs, fmt.Errorf("gitHub company username %q is not a valid URL path segment", *employerGithubUsername))
}
if *employerGithubUsername == githubUsername {
errs = append(errs, fmt.Errorf("cannot list own GitHub name (%q) as employer", githubUsername))
}
return errs
}
func validateContributorDisplayName(displayName string) error { func validateContributorDisplayName(displayName string) error {
if displayName == "" { if displayName == "" {
return fmt.Errorf("missing display_name") return fmt.Errorf("missing display_name")
@ -195,9 +158,6 @@ func validateContributorAvatarURL(avatarURL *string) []error {
func validateContributorYaml(yml contributorProfile) []error { func validateContributorYaml(yml contributorProfile) []error {
allErrs := []error{} allErrs := []error{}
if err := validateContributorGithubUsername(yml.frontmatter.GithubUsername); err != nil {
allErrs = append(allErrs, addFilePathToError(yml.filePath, err))
}
if err := validateContributorDisplayName(yml.frontmatter.DisplayName); err != nil { if err := validateContributorDisplayName(yml.frontmatter.DisplayName); err != nil {
allErrs = append(allErrs, addFilePathToError(yml.filePath, err)) allErrs = append(allErrs, addFilePathToError(yml.filePath, err))
} }
@ -211,9 +171,6 @@ func validateContributorYaml(yml contributorProfile) []error {
allErrs = append(allErrs, addFilePathToError(yml.filePath, err)) allErrs = append(allErrs, addFilePathToError(yml.filePath, err))
} }
for _, err := range validateContributorEmployerGithubUsername(yml.frontmatter.EmployerGithubUsername, yml.frontmatter.GithubUsername) {
allErrs = append(allErrs, addFilePathToError(yml.filePath, err))
}
for _, err := range validateContributorSupportEmail(yml.frontmatter.SupportEmail) { for _, err := range validateContributorSupportEmail(yml.frontmatter.SupportEmail) {
allErrs = append(allErrs, addFilePathToError(yml.filePath, err)) allErrs = append(allErrs, addFilePathToError(yml.filePath, err))
} }
@ -238,11 +195,12 @@ func parseContributorProfile(rm readme) (contributorProfile, error) {
return contributorProfile{ return contributorProfile{
filePath: rm.filePath, filePath: rm.filePath,
frontmatter: yml, frontmatter: yml,
namespace: strings.TrimSuffix(strings.TrimPrefix(rm.filePath, "registry/"), "/README.md"),
}, nil }, nil
} }
func parseContributorFiles(readmeEntries []readme) (map[string]contributorProfile, error) { func parseContributorFiles(readmeEntries []readme) (map[string]contributorProfile, error) {
profilesByUsername := map[string]contributorProfile{} profilesByNamespace := map[string]contributorProfile{}
yamlParsingErrors := []error{} yamlParsingErrors := []error{}
for _, rm := range readmeEntries { for _, rm := range readmeEntries {
p, err := parseContributorProfile(rm) p, err := parseContributorProfile(rm)
@ -251,11 +209,11 @@ func parseContributorFiles(readmeEntries []readme) (map[string]contributorProfil
continue continue
} }
if prev, alreadyExists := profilesByUsername[p.frontmatter.GithubUsername]; alreadyExists { if prev, alreadyExists := profilesByNamespace[p.namespace]; alreadyExists {
yamlParsingErrors = append(yamlParsingErrors, fmt.Errorf("%q: GitHub name %s conflicts with field defined in %q", p.filePath, p.frontmatter.GithubUsername, prev.filePath)) yamlParsingErrors = append(yamlParsingErrors, fmt.Errorf("%q: namespace %q conflicts with namespace from %q", p.filePath, p.namespace, prev.filePath))
continue continue
} }
profilesByUsername[p.frontmatter.GithubUsername] = p profilesByNamespace[p.namespace] = p
} }
if len(yamlParsingErrors) != 0 { if len(yamlParsingErrors) != 0 {
return nil, validationPhaseError{ return nil, validationPhaseError{
@ -264,27 +222,13 @@ func parseContributorFiles(readmeEntries []readme) (map[string]contributorProfil
} }
} }
employeeGithubGroups := map[string][]string{}
yamlValidationErrors := []error{} yamlValidationErrors := []error{}
for _, p := range profilesByUsername { for _, p := range profilesByNamespace {
errors := validateContributorYaml(p) errors := validateContributorYaml(p)
if len(errors) > 0 { if len(errors) > 0 {
yamlValidationErrors = append(yamlValidationErrors, errors...) yamlValidationErrors = append(yamlValidationErrors, errors...)
continue continue
} }
if p.frontmatter.EmployerGithubUsername != nil {
employeeGithubGroups[*p.frontmatter.EmployerGithubUsername] = append(
employeeGithubGroups[*p.frontmatter.EmployerGithubUsername],
p.frontmatter.GithubUsername,
)
}
}
for companyName, group := range employeeGithubGroups {
if _, found := profilesByUsername[companyName]; found {
continue
}
yamlValidationErrors = append(yamlValidationErrors, fmt.Errorf("%q: company %q does not exist but is referenced by these profiles: [%s]", rootRegistryPath, companyName, strings.Join(group, ", ")))
} }
if len(yamlValidationErrors) != 0 { if len(yamlValidationErrors) != 0 {
return nil, validationPhaseError{ return nil, validationPhaseError{
@ -293,7 +237,7 @@ func parseContributorFiles(readmeEntries []readme) (map[string]contributorProfil
} }
} }
return profilesByUsername, nil return profilesByNamespace, nil
} }
func aggregateContributorReadmeFiles() ([]readme, error) { func aggregateContributorReadmeFiles() ([]readme, error) {

View File

@ -1,8 +0,0 @@
---
display_name: HashiCorp
bio: HashiCorp, an IBM company, empowers organizations to automate and secure multi-cloud and hybrid environments with The Infrastructure Cloud™. Our suite of Infrastructure Lifecycle Management and Security Lifecycle Management solutions are built on projects with source code freely available at their core. The HashiCorp suite underpins the world's most critical applications, helping enterprises achieve efficiency, security, and scalability at any stage of their cloud journey.
github: hashicorp
linkedin: https://www.linkedin.com/company/hashicorp
website: https://www.hashicorp.com/
status: partner
---

View File

@ -1,8 +0,0 @@
---
display_name: Jfrog
bio: At JFrog, we are making endless software versions a thing of the past, with liquid software that flows continuously and automatically from build all the way through to production.
github: jfrog
linkedin: https://www.linkedin.com/company/jfrog-ltd
website: https://jfrog.com/
status: partner
---

View File

@ -3,5 +3,5 @@ display_name: Nataindata
bio: Data engineer bio: Data engineer
github: nataindata github: nataindata
website: https://www.nataindata.com website: https://www.nataindata.com
status: community status: partner
--- ---