Skip to content
This repository was archived by the owner on Jun 28, 2024. It is now read-only.

Commit 9f14ba7

Browse files
author
James O. D. Hunt
committed
cmd: add github labels tooling
Add new tooling to handle GitHub labels. This includes: - A YAML template file (the master "labels database") This contains the master set of labels to be used for all Kata repositories. All labels have descriptions and a category. - A script to generate the combined set of labels This merges the master set of labels with an optional repository-specific set of labels to produce a combined set of labels for a particular repository. - A golang tool to check and summarise the labels database This can also be used to sort the labels database. Fixes #1463. Signed-off-by: James O. D. Hunt <[email protected]>
1 parent e9b1abb commit 9f14ba7

File tree

18 files changed

+1829
-2
lines changed

18 files changed

+1829
-2
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ env:
2828
before_install:
2929
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update -qq ; fi
3030
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y -qq automake ; fi
31-
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then travis_retry brew install bash; fi
31+
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then pip install --user yamllint ; fi
32+
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then travis_retry brew install bash yamllint; fi
3233

3334
script:
3435
- bash .ci/static-checks.sh github.com/kata-containers/tests

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ ifneq ($(wildcard $(ARCH_FILE)),)
3030
include $(ARCH_FILE)
3131
endif
3232

33-
default: checkcommits
33+
default: checkcommits github-labels
3434

3535
checkcommits:
3636
make -C cmd/checkcommits
3737

38+
github-labels:
39+
make -C cmd/github-labels
40+
3841
ginkgo:
3942
ln -sf . vendor/src
4043
GOPATH=$(PWD)/vendor go build ./vendor/github.com/onsi/ginkgo/ginkgo

cmd/github-labels/Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#
2+
# Copyright (c) 2017-2019 Intel Corporation
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
TARGET = kata-github-labels
8+
SOURCES = $(shell find . -type f 2>&1 | grep -E '.*\.go$$')
9+
10+
VERSION := ${shell cat ./VERSION}
11+
COMMIT_NO := $(shell git rev-parse HEAD 2> /dev/null || true)
12+
COMMIT := $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
13+
14+
BINDIR := $(GOPATH)/bin
15+
DESTTARGET := $(abspath $(BINDIR)/$(TARGET))
16+
17+
default: install
18+
19+
check: $(SOURCES)
20+
go test -v ./...
21+
22+
$(TARGET): $(SOURCES)
23+
go build -o "$(TARGET)" -ldflags "-X main.name=${TARGET} -X main.commit=${COMMIT} -X main.version=${VERSION}" .
24+
25+
install: $(TARGET)
26+
install -d $(shell dirname $(DESTTARGET))
27+
install $(TARGET) $(DESTTARGET)
28+
29+
clean:
30+
rm -f $(TARGET)
31+
32+
.PHONY: install clean

cmd/github-labels/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
* [Overview](#overview)
2+
* [Generating the combined labels database](#generating-the-combined-labels-database)
3+
* [Checking and summarising the labels database](#checking-and-summarising-the-labels-database)
4+
* [Show labels](#show-labels)
5+
* [Show categories](#show-categories)
6+
* [Check only](#check-only)
7+
* [Full details](#full-details)
8+
9+
# Overview
10+
11+
The Kata Project uses a number of GitHub repositories. To allow issues and PRs
12+
to be handled consistently between repositories a standard set of issue labels
13+
are used. These labels are stored in YAML format in the master [labels
14+
database template](labels.yaml.in). This file is human-readable and
15+
machine-readable and self-describing (take a look at the file for the
16+
introductory description).
17+
18+
Additionally, each repository can contain a set of additional
19+
(repository-specific) labels which are stored in a top-level YAML template
20+
file called `labels.yaml.in`.
21+
22+
Expanding the templates and merging the two databases describes the full set
23+
of labels a repository uses.
24+
25+
# Generating the combined labels database
26+
27+
The combined labels database is generated by running the
28+
`github_labels.sh` script with the `generate` parameter, also passing the
29+
repository to generate the combined labels database for and the name of a file
30+
to write the combined database to:
31+
32+
```sh
33+
$ ./github-labels.sh generate github.com/kata-containers/runtime /tmp/combined.yaml
34+
```
35+
36+
The script validates the combined labels database by performing a number of
37+
checks including running the `kata-github-labels` tool (described in the
38+
[Checking and summarising the labels database](#checking-and-summarising-the-labels-database) section)
39+
in checking mode.
40+
41+
# Checking and summarising the labels database
42+
43+
The [`kata-github-labels`](github-labels.go) tool is used to check and
44+
summarise the labels database for each repository.
45+
46+
## Show labels
47+
48+
To display a summary of the labels:
49+
50+
```sh
51+
$ kata-github-labels show labels labels.yaml
52+
```
53+
54+
## Show categories
55+
56+
To show all information about categories:
57+
58+
```sh
59+
$ kata-github-labels show categories --with-labels labels.yaml
60+
```
61+
## Check only
62+
63+
To only perform checks on specified labels database:
64+
65+
```sh
66+
$ kata-github-labels check labels.yaml
67+
```
68+
69+
## Full details
70+
71+
To list all available options:
72+
73+
```sh
74+
$ kata-github-labels -h
75+
```

cmd/github-labels/VERSION

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0.0.1

cmd/github-labels/check.go

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
// Copyright (c) 2019 Intel Corporation
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
//
5+
6+
package main
7+
8+
import (
9+
"errors"
10+
"fmt"
11+
"strings"
12+
"unicode"
13+
)
14+
15+
func containsWhitespace(s string) bool {
16+
for _, ch := range s {
17+
if unicode.IsSpace(ch) {
18+
return true
19+
}
20+
}
21+
22+
return false
23+
}
24+
25+
func isLower(s string) bool {
26+
for _, ch := range s {
27+
if !unicode.IsLetter(ch) {
28+
continue
29+
}
30+
31+
if !unicode.IsLower(ch) {
32+
return false
33+
}
34+
}
35+
36+
return true
37+
}
38+
39+
func checkCategory(c Category) error {
40+
if c.Name == "" {
41+
return fmt.Errorf("category name cannot be blank: %+v", c)
42+
}
43+
44+
if containsWhitespace(c.Name) {
45+
return fmt.Errorf("category name cannot contain whitespace: %+v", c)
46+
}
47+
48+
if !isLower(c.Name) {
49+
return fmt.Errorf("category name must be all lower case: %+v", c)
50+
}
51+
52+
if c.Description == "" {
53+
return fmt.Errorf("category description cannot be blank: %+v", c)
54+
}
55+
56+
first := c.Description[0]
57+
58+
if !unicode.IsUpper(rune(first)) {
59+
return fmt.Errorf("category description needs initial capital letter: %+v", c)
60+
}
61+
62+
if !strings.HasSuffix(c.Description, ".") {
63+
return fmt.Errorf("category description needs trailing period: %+v", c)
64+
}
65+
66+
return nil
67+
}
68+
69+
func checkLabel(l Label) error {
70+
if l.Name == "" {
71+
return fmt.Errorf("label name cannot be blank: %+v", l)
72+
}
73+
74+
if !isLower(l.Name) {
75+
return fmt.Errorf("label name must be all lower case: %+v", l)
76+
}
77+
78+
if containsWhitespace(l.Name) {
79+
return fmt.Errorf("label name cannot contain whitespace: %+v", l)
80+
}
81+
82+
if l.Description == "" {
83+
return fmt.Errorf("label description cannot be blank: %+v", l)
84+
}
85+
86+
first := l.Description[0]
87+
88+
if !unicode.IsUpper(rune(first)) {
89+
return fmt.Errorf("label description needs initial capital letter: %+v", l)
90+
}
91+
92+
if l.CategoryName == "" {
93+
return fmt.Errorf("label category name cannot be blank: %+v", l)
94+
}
95+
96+
if l.Colour == "" {
97+
return fmt.Errorf("label colour cannot be blank: %+v", l)
98+
}
99+
100+
return nil
101+
}
102+
103+
func checkLabelsAndCategories(lf *LabelsFile) error {
104+
catCount := 0
105+
106+
var catNameMap map[string]int
107+
var catDescMap map[string]int
108+
109+
var labelNameMap map[string]int
110+
var labelDescMap map[string]int
111+
112+
catNameMap = make(map[string]int)
113+
catDescMap = make(map[string]int)
114+
labelNameMap = make(map[string]int)
115+
labelDescMap = make(map[string]int)
116+
117+
for _, c := range lf.Categories {
118+
if err := checkCategory(c); err != nil {
119+
return err
120+
}
121+
122+
catCount++
123+
124+
if _, ok := catNameMap[c.Name]; ok {
125+
return fmt.Errorf("duplicate category name: %+v", c)
126+
}
127+
128+
catNameMap[c.Name] = 0
129+
130+
if _, ok := catDescMap[c.Description]; ok {
131+
return fmt.Errorf("duplicate category description: %+v", c)
132+
}
133+
134+
catDescMap[c.Description] = 0
135+
}
136+
137+
if catCount == 0 {
138+
return errors.New("no categories found")
139+
}
140+
141+
labelCount := 0
142+
143+
for _, l := range lf.Labels {
144+
if err := checkLabel(l); err != nil {
145+
return err
146+
}
147+
148+
if _, ok := labelNameMap[l.Name]; ok {
149+
return fmt.Errorf("duplicate label name: %+v", l)
150+
}
151+
152+
labelNameMap[l.Name] = 0
153+
154+
if _, ok := labelDescMap[l.Description]; ok {
155+
return fmt.Errorf("duplicate label description: %+v", l)
156+
}
157+
158+
labelDescMap[l.Description] = 0
159+
160+
labelCount++
161+
162+
catName := l.CategoryName
163+
164+
var value int
165+
var ok bool
166+
if value, ok = catNameMap[catName]; !ok {
167+
return fmt.Errorf("invalid category %v found for label %+v", catName, l)
168+
}
169+
170+
// update
171+
value++
172+
catNameMap[catName] = value
173+
}
174+
175+
if labelCount == 0 {
176+
return errors.New("no labels found")
177+
}
178+
179+
if debug {
180+
fmt.Printf("DEBUG: category count: %v\n", catCount)
181+
fmt.Printf("DEBUG: label count: %v\n", labelCount)
182+
}
183+
184+
for name, count := range catNameMap {
185+
if count == 0 {
186+
return fmt.Errorf("category %v not used", name)
187+
}
188+
189+
if debug {
190+
fmt.Printf("DEBUG: category %v: label count: %d\n",
191+
name, count)
192+
}
193+
}
194+
195+
return nil
196+
}
197+
198+
func check(lf *LabelsFile) error {
199+
if lf.Description == "" {
200+
return errors.New("description cannot be blank")
201+
}
202+
203+
if lf.Repo == "" {
204+
return errors.New("repo cannot be blank")
205+
}
206+
207+
if len(lf.Categories) == 0 {
208+
return errors.New("no categories")
209+
}
210+
211+
if len(lf.Labels) == 0 {
212+
return errors.New("no labels")
213+
}
214+
215+
return checkLabelsAndCategories(lf)
216+
}

0 commit comments

Comments
 (0)