Update generator to reformat sigs.yaml
The following formatting is enforced: - Consistent identation of 2 spaces - Keys are sorted by order of struct fields - Lists are sorted by struct-dependent keys - Comments are stripped I had hopes of using yaml.Node to preserve comments, but found no obvious way to marshal from struct to Node, meaning all data manipulation had to be done against Nodes. This loses us all of the benefits of using a struct: types, known fields, field order. Given that there aren't many comments in sigs.yaml to begin with, I'm erring on the side of dropping support for them.
This commit is contained in:
parent
b91b05da2e
commit
89cde82503
181
generator/app.go
181
generator/app.go
|
@ -52,11 +52,23 @@ var (
|
||||||
templateDir = "generator"
|
templateDir = "generator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FoldedString is a string that will be serialized in FoldedStyle by go-yaml
|
||||||
|
type FoldedString string
|
||||||
|
|
||||||
|
// MarshalYAML customizes how FoldedStrings will be serialized by go-yaml
|
||||||
|
func (x FoldedString) MarshalYAML() (interface{}, error) {
|
||||||
|
return &yaml.Node{
|
||||||
|
Kind: yaml.ScalarNode,
|
||||||
|
Style: yaml.FoldedStyle,
|
||||||
|
Value: string(x),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Person represents an individual person holding a role in a group.
|
// Person represents an individual person holding a role in a group.
|
||||||
type Person struct {
|
type Person struct {
|
||||||
|
GitHub string
|
||||||
Name string
|
Name string
|
||||||
Company string
|
Company string
|
||||||
GitHub string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Meeting represents a regular meeting for a group.
|
// Meeting represents a regular meeting for a group.
|
||||||
|
@ -66,53 +78,53 @@ type Meeting struct {
|
||||||
Time string
|
Time string
|
||||||
TZ string
|
TZ string
|
||||||
Frequency string
|
Frequency string
|
||||||
URL string
|
URL string `yaml:",omitempty"`
|
||||||
ArchiveURL string `yaml:"archive_url"`
|
ArchiveURL string `yaml:"archive_url,omitempty"`
|
||||||
RecordingsURL string `yaml:"recordings_url"`
|
RecordingsURL string `yaml:"recordings_url,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contact represents the various contact points for a group.
|
// Contact represents the various contact points for a group.
|
||||||
type Contact struct {
|
type Contact struct {
|
||||||
Slack string
|
Slack string `yaml:",omitempty"`
|
||||||
MailingList string `yaml:"mailing_list"`
|
MailingList string `yaml:"mailing_list,omitempty"`
|
||||||
PrivateMailingList string `yaml:"private_mailing_list"`
|
PrivateMailingList string `yaml:"private_mailing_list,omitempty"`
|
||||||
GithubTeams []GithubTeams `yaml:"teams"`
|
GithubTeams []GithubTeam `yaml:"teams,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GithubTeams represents a specific Github Team.
|
// GithubTeam represents a specific Github Team.
|
||||||
type GithubTeams struct {
|
type GithubTeam struct {
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string `yaml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subproject represents a specific subproject owned by the group
|
// Subproject represents a specific subproject owned by the group
|
||||||
type Subproject struct {
|
type Subproject struct {
|
||||||
Name string
|
Name string
|
||||||
Description string
|
Description string `yaml:",omitempty"`
|
||||||
Contact *Contact
|
Contact *Contact `yaml:",omitempty"`
|
||||||
Owners []string
|
Owners []string
|
||||||
Meetings []Meeting
|
Meetings []Meeting `yaml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// LeadershipGroup represents the different groups of leaders within a group
|
// LeadershipGroup represents the different groups of leaders within a group
|
||||||
type LeadershipGroup struct {
|
type LeadershipGroup struct {
|
||||||
Chairs []Person
|
Chairs []Person
|
||||||
TechnicalLeads []Person `yaml:"tech_leads"`
|
TechnicalLeads []Person `yaml:"tech_leads,omitempty"`
|
||||||
EmeritusLeads []Person `yaml:"emeritus_leads"`
|
EmeritusLeads []Person `yaml:"emeritus_leads,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Group represents either a Special Interest Group (SIG) or a Working Group (WG)
|
// Group represents either a Special Interest Group (SIG) or a Working Group (WG)
|
||||||
type Group struct {
|
type Group struct {
|
||||||
Name string
|
|
||||||
Dir string
|
Dir string
|
||||||
MissionStatement string `yaml:"mission_statement,omitempty"`
|
Name string
|
||||||
CharterLink string `yaml:"charter_link,omitempty"`
|
MissionStatement FoldedString `yaml:"mission_statement,omitempty"`
|
||||||
|
CharterLink string `yaml:"charter_link,omitempty"`
|
||||||
|
StakeholderSIGs []string `yaml:"stakeholder_sigs,omitempty"`
|
||||||
Label string
|
Label string
|
||||||
Leadership LeadershipGroup `yaml:"leadership"`
|
Leadership LeadershipGroup `yaml:"leadership"`
|
||||||
Meetings []Meeting
|
Meetings []Meeting
|
||||||
Contact Contact
|
Contact Contact
|
||||||
Subprojects []Subproject
|
Subprojects []Subproject `yaml:",omitempty"`
|
||||||
StakeholderSIGs []string `yaml:"stakeholder_sigs,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DirName returns the directory that a group's documentation will be
|
// DirName returns the directory that a group's documentation will be
|
||||||
|
@ -130,6 +142,57 @@ type Context struct {
|
||||||
Committees []Group
|
Committees []Group
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrefixToGroupMap returns a map of prefix to groups, useful for iteration over all groups
|
||||||
|
func (c *Context) PrefixToGroupMap() map[string][]Group {
|
||||||
|
return map[string][]Group{
|
||||||
|
"sig": c.Sigs,
|
||||||
|
"wg": c.WorkingGroups,
|
||||||
|
"ug": c.UserGroups,
|
||||||
|
"committee": c.Committees,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort sorts all lists within the Context struct
|
||||||
|
func (c *Context) Sort() {
|
||||||
|
for _, groups := range c.PrefixToGroupMap() {
|
||||||
|
sort.Slice(groups, func(i, j int) bool {
|
||||||
|
return groups[i].Dir < groups[j].Dir
|
||||||
|
})
|
||||||
|
for _, group := range groups {
|
||||||
|
sort.Strings(group.StakeholderSIGs)
|
||||||
|
for _, people := range [][]Person{
|
||||||
|
group.Leadership.Chairs,
|
||||||
|
group.Leadership.TechnicalLeads,
|
||||||
|
group.Leadership.EmeritusLeads} {
|
||||||
|
sort.Slice(people, func(i, j int) bool {
|
||||||
|
// This ensure OWNERS / OWNERS_ALIAS files are ordered by github
|
||||||
|
return people[i].GitHub < people[j].GitHub
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(group.Meetings, func(i, j int) bool {
|
||||||
|
return group.Meetings[i].Description < group.Meetings[j].Description
|
||||||
|
})
|
||||||
|
sort.Slice(group.Contact.GithubTeams, func(i, j int) bool {
|
||||||
|
return group.Contact.GithubTeams[i].Name < group.Contact.GithubTeams[j].Name
|
||||||
|
})
|
||||||
|
sort.Slice(group.Subprojects, func(i, j int) bool {
|
||||||
|
return group.Subprojects[i].Name < group.Subprojects[j].Name
|
||||||
|
})
|
||||||
|
for _, subproject := range group.Subprojects {
|
||||||
|
if subproject.Contact != nil {
|
||||||
|
sort.Slice(subproject.Contact.GithubTeams, func(i, j int) bool {
|
||||||
|
return subproject.Contact.GithubTeams[i].Name < subproject.Contact.GithubTeams[j].Name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Strings(subproject.Owners)
|
||||||
|
sort.Slice(subproject.Meetings, func(i, j int) bool {
|
||||||
|
return subproject.Meetings[i].Description < subproject.Meetings[j].Description
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func pathExists(path string) bool {
|
func pathExists(path string) bool {
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
return err == nil
|
return err == nil
|
||||||
|
@ -182,14 +245,14 @@ func getExistingContent(path string, fileFormat string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var funcMap = template.FuncMap{
|
var funcMap = template.FuncMap{
|
||||||
"tzUrlEncode": tzUrlEncode,
|
"tzUrlEncode": tzURLEncode,
|
||||||
"trimSpace": strings.TrimSpace,
|
"trimSpace": strings.TrimSpace,
|
||||||
}
|
}
|
||||||
|
|
||||||
// tzUrlEncode returns a url encoded string without the + shortcut. This is
|
// tzUrlEncode returns a url encoded string without the + shortcut. This is
|
||||||
// required as the timezone conversion site we are using doesn't recognize + as
|
// required as the timezone conversion site we are using doesn't recognize + as
|
||||||
// a valid url escape character.
|
// a valid url escape character.
|
||||||
func tzUrlEncode(tz string) string {
|
func tzURLEncode(tz string) string {
|
||||||
return strings.Replace(url.QueryEscape(tz), "+", "%20", -1)
|
return strings.Replace(url.QueryEscape(tz), "+", "%20", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,52 +352,54 @@ func createGroupReadme(groups []Group, prefix string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readSigsYaml decodes yaml stored in a file at path into the
|
||||||
|
// specified yaml.Node
|
||||||
|
func readYaml(path string, data interface{}) error {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
decoder := yaml.NewDecoder(file)
|
||||||
|
decoder.KnownFields(true)
|
||||||
|
return decoder.Decode(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSigsYaml writes the specified data to a file at path
|
||||||
|
// indent is set to 2 spaces
|
||||||
|
func writeYaml(data interface{}, path string) error {
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
enc := yaml.NewEncoder(file)
|
||||||
|
enc.SetIndent(2)
|
||||||
|
return enc.Encode(data)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
yamlData, err := ioutil.ReadFile(filepath.Join(baseGeneratorDir, sigsYamlFile))
|
yamlPath := filepath.Join(baseGeneratorDir, sigsYamlFile)
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ctx Context
|
var ctx Context
|
||||||
err = yaml.Unmarshal(yamlData, &ctx)
|
|
||||||
|
err := readYaml(yamlPath, &ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(ctx.Sigs, func(i, j int) bool {
|
ctx.Sort()
|
||||||
return strings.ToLower(ctx.Sigs[i].Name) <= strings.ToLower(ctx.Sigs[j].Name)
|
|
||||||
})
|
|
||||||
|
|
||||||
sort.Slice(ctx.WorkingGroups, func(i, j int) bool {
|
// Write the Context struct back to yaml to enforce formatting
|
||||||
return strings.ToLower(ctx.WorkingGroups[i].Name) <= strings.ToLower(ctx.WorkingGroups[j].Name)
|
err = writeYaml(&ctx, yamlPath)
|
||||||
})
|
|
||||||
|
|
||||||
sort.Slice(ctx.UserGroups, func(i, j int) bool {
|
|
||||||
return strings.ToLower(ctx.UserGroups[i].Name) <= strings.ToLower(ctx.UserGroups[j].Name)
|
|
||||||
})
|
|
||||||
|
|
||||||
sort.Slice(ctx.Committees, func(i, j int) bool {
|
|
||||||
return strings.ToLower(ctx.Committees[i].Name) <= strings.ToLower(ctx.Committees[j].Name)
|
|
||||||
})
|
|
||||||
|
|
||||||
err = createGroupReadme(ctx.Sigs, "sig")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = createGroupReadme(ctx.WorkingGroups, "wg")
|
for prefix, groups := range ctx.PrefixToGroupMap() {
|
||||||
if err != nil {
|
err = createGroupReadme(groups, prefix)
|
||||||
log.Fatal(err)
|
if err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
|
}
|
||||||
err = createGroupReadme(ctx.UserGroups, "ug")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = createGroupReadme(ctx.Committees, "committee")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Generating sig-list.md")
|
fmt.Println("Generating sig-list.md")
|
||||||
|
|
|
@ -37,7 +37,7 @@ make 1>/dev/null
|
||||||
mismatches=0
|
mismatches=0
|
||||||
break=$(printf "=%.0s" $(seq 1 68))
|
break=$(printf "=%.0s" $(seq 1 68))
|
||||||
|
|
||||||
for file in $(ls ${CRT_DIR}/sig-*/README.md ${CRT_DIR}/wg-*/README.md ${CRT_DIR}/ug-*/README.md ${CRT_DIR}/committee-*/README.md ${CRT_DIR}/sig-list.md ${CRT_DIR}/OWNERS_ALIASES); do
|
for file in $(ls ${CRT_DIR}/sigs.yaml ${CRT_DIR}/sig-*/README.md ${CRT_DIR}/wg-*/README.md ${CRT_DIR}/ug-*/README.md ${CRT_DIR}/committee-*/README.md ${CRT_DIR}/sig-list.md ${CRT_DIR}/OWNERS_ALIASES); do
|
||||||
real=${file#$CRT_DIR/}
|
real=${file#$CRT_DIR/}
|
||||||
if ! diff -q ${file} ${WORKING_DIR}/${real} &>/dev/null; then
|
if ! diff -q ${file} ${WORKING_DIR}/${real} &>/dev/null; then
|
||||||
echo "${file} does not match ${WORKING_DIR}/${real}";
|
echo "${file} does not match ${WORKING_DIR}/${real}";
|
||||||
|
|
Loading…
Reference in New Issue