From 66f3944b52d386cd995c77c9a4fc6f5d311fc16f Mon Sep 17 00:00:00 2001 From: Chelsea Mafrica Date: Tue, 21 Nov 2023 17:48:18 -0800 Subject: [PATCH] tests: move github-labels to main repo Move tool as part of static checks migration. Fixes #8187 Signed-off-by: Chelsea Mafrica Signed-off-by: Derek Lee Signed-off-by: Gabriela Cervantes Signed-off-by: Graham Whaley Signed-off-by: James O. D. Hunt Signed-off-by: Marco Vedovati Signed-off-by: Peng Tao Signed-off-by: Shiming Zhang Signed-off-by: Snir Sheriber Signed-off-by: Wainer dos Santos Moschetta --- tests/cmd/github-labels/Makefile | 32 + tests/cmd/github-labels/README.md | 71 +++ tests/cmd/github-labels/VERSION | 1 + tests/cmd/github-labels/archive/README.md | 50 ++ ...er-original-labels-kata-containers-ci.yaml | 58 ++ ...inal-labels-kata-containers-community.yaml | 27 + ...abels-kata-containers-kata-containers.yaml | 44 ++ ...original-labels-kata-containers-tests.yaml | 60 ++ tests/cmd/github-labels/check.go | 216 +++++++ tests/cmd/github-labels/clean.go | 62 ++ tests/cmd/github-labels/display.go | 83 +++ tests/cmd/github-labels/display_markdown.go | 75 +++ tests/cmd/github-labels/display_text.go | 101 ++++ tests/cmd/github-labels/display_tsv.go | 66 +++ tests/cmd/github-labels/github-labels.sh | 176 ++++++ tests/cmd/github-labels/labels.yaml | 555 ++++++++++++++++++ tests/cmd/github-labels/labels.yaml.in | 555 ++++++++++++++++++ tests/cmd/github-labels/main.go | 157 +++++ tests/cmd/github-labels/record.go | 102 ++++ tests/cmd/github-labels/types.go | 55 ++ tests/cmd/github-labels/utils.go | 24 + tests/cmd/github-labels/yaml.go | 72 +++ 22 files changed, 2642 insertions(+) create mode 100644 tests/cmd/github-labels/Makefile create mode 100644 tests/cmd/github-labels/README.md create mode 100644 tests/cmd/github-labels/VERSION create mode 100644 tests/cmd/github-labels/archive/README.md create mode 100644 tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-ci.yaml create mode 100644 tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-community.yaml create mode 100644 tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-kata-containers.yaml create mode 100644 tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-tests.yaml create mode 100644 tests/cmd/github-labels/check.go create mode 100644 tests/cmd/github-labels/clean.go create mode 100644 tests/cmd/github-labels/display.go create mode 100644 tests/cmd/github-labels/display_markdown.go create mode 100644 tests/cmd/github-labels/display_text.go create mode 100644 tests/cmd/github-labels/display_tsv.go create mode 100755 tests/cmd/github-labels/github-labels.sh create mode 100644 tests/cmd/github-labels/labels.yaml create mode 100644 tests/cmd/github-labels/labels.yaml.in create mode 100644 tests/cmd/github-labels/main.go create mode 100644 tests/cmd/github-labels/record.go create mode 100644 tests/cmd/github-labels/types.go create mode 100644 tests/cmd/github-labels/utils.go create mode 100644 tests/cmd/github-labels/yaml.go diff --git a/tests/cmd/github-labels/Makefile b/tests/cmd/github-labels/Makefile new file mode 100644 index 0000000000..c0d5e06e9e --- /dev/null +++ b/tests/cmd/github-labels/Makefile @@ -0,0 +1,32 @@ +# +# Copyright (c) 2017-2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +TARGET = kata-github-labels +SOURCES = $(shell find . -type f 2>&1 | grep -E '.*\.go$$') + +VERSION := ${shell cat ./VERSION} +COMMIT_NO := $(shell git rev-parse HEAD 2> /dev/null || true) +COMMIT := $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}") + +BINDIR := $(GOPATH)/bin +DESTTARGET := $(abspath $(BINDIR)/$(TARGET)) + +default: install + +check: $(SOURCES) + go test -v ./... + +$(TARGET): $(SOURCES) + go build -o "$(TARGET)" -ldflags "-X main.name=${TARGET} -X main.commit=${COMMIT} -X main.version=${VERSION}" . + +install: $(TARGET) + install -d $(shell dirname $(DESTTARGET)) + install $(TARGET) $(DESTTARGET) + +clean: + rm -f $(TARGET) + +.PHONY: install clean diff --git a/tests/cmd/github-labels/README.md b/tests/cmd/github-labels/README.md new file mode 100644 index 0000000000..f602fd9642 --- /dev/null +++ b/tests/cmd/github-labels/README.md @@ -0,0 +1,71 @@ +# Overview + +The Kata Project uses a number of GitHub repositories. To allow issues and PRs +to be handled consistently between repositories a standard set of issue labels +are used. These labels are stored in YAML format in the master +[labels database template](labels.yaml.in). This file is human-readable, +machine-readable, and self-describing (see the file for the introductory +description). + +Each repository can contain a set of additional (repository-specific) labels, +which are stored in a top-level YAML template file called `labels.yaml.in`. + +Expanding the templates and merging the two databases describes the full set +of labels a repository uses. + +# Generating the combined labels database + +You can run the `github_labels.sh` script with the `generate` argument to +create the combined labels database. The additional arguments specify the +repository (in order to generate the combined labels database) and the name of +a file to write the combined database: + +```sh +$ ./github-labels.sh generate github.com/kata-containers/kata-containers /tmp/combined.yaml +``` + +This script validates the combined labels database by performing a number of +checks, including running the `kata-github-labels` tool in checking mode. See +the +[Checking and summarising the labels database](#checking-and-summarising-the-labels-database) +section for more information. + +# Checking and summarising the labels database + +The `kata-github-labels` tool checks and summarizes the labels database for +each repository. + +## Show labels + +Displays a summary of the labels: + +```sh +$ kata-github-labels show labels labels.yaml +``` + +## Show categories + +Shows all information about categories: + +```sh +$ kata-github-labels show categories --with-labels labels.yaml +``` +## Check only + +Performs checks on a specified labels database: + +```sh +$ kata-github-labels check labels.yaml +``` + +## Full details + +Lists all available options: + +```sh +$ kata-github-labels -h +``` + +# Archive of old GitHub labels + +See the [archive documentation](archive). diff --git a/tests/cmd/github-labels/VERSION b/tests/cmd/github-labels/VERSION new file mode 100644 index 0000000000..8acdd82b76 --- /dev/null +++ b/tests/cmd/github-labels/VERSION @@ -0,0 +1 @@ +0.0.1 diff --git a/tests/cmd/github-labels/archive/README.md b/tests/cmd/github-labels/archive/README.md new file mode 100644 index 0000000000..358e28989b --- /dev/null +++ b/tests/cmd/github-labels/archive/README.md @@ -0,0 +1,50 @@ +# GitHub labels archive + +## Overview + +This directory contains one YAML file per repository containing the original +set of GitHub labels before the +[new ones were applied on 2019-06-04](../labels.yaml.in). + +## How the YAML files were created + +This section explains how the YAML files were created. + +The [`labeler`](https://github.com/tonglil/labeler) tool was used to read +the labels and write them to a YAML file. + +### Install and patch the `labeler` tool + +This isn't ideal but our [labels database](../labels.yaml.in) mandates +descriptions for every label. However, at the time of writing, the `labeler` +tool does not support descriptions. But, +[there is a PR](https://github.com/tonglil/labeler/pull/37) +to add in description support. + +To enable description support: + +```sh +$ go get -u github.com/tonglil/labeler +$ cd $GOPATH/src/github.com/tonglil/labeler +$ pr=37 +$ pr_branch="PR${pr}" +$ git fetch origin "refs/pull/${pr}/head:{pr_branch}" +$ git checkout "${pr_branch}" +$ go install -v ./... +``` + +### Save GitHub labels for a repository + +Run the following for reach repository: + +```sh +$ labeler scan -r ${github_repo_slug} ${output_file} +``` + +For example, to save the labels for the `tests` repository: + +```sh +$ labeler scan -r kata-containers/tests tests.yaml + +``` + diff --git a/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-ci.yaml b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-ci.yaml new file mode 100644 index 0000000000..a1e3402967 --- /dev/null +++ b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-ci.yaml @@ -0,0 +1,58 @@ +# Scanned and autogenerated by https://github.com/tonglil/labeler +--- +repo: kata-containers/ci +labels: + - name: P1 + color: b60205 + description: Highest priority issue (Critical) + - name: P2 + color: d93f0b + description: Urgent issue + - name: P3 + color: fbca04 + description: Important issue + - name: P4 + color: fef2c0 + description: Noteworthy issue + - name: backlog + color: ededed + - name: bitesize + color: d4c5f9 + description: small/easy task + - name: bug + color: d73a4a + description: Something isn't working + - name: do-not-merge + color: b60205 + - name: duplicate + color: cfd3d7 + description: This issue or pull request already exists + - name: enhancement + color: a2eeef + description: New feature or request + - name: good first issue + color: 7057ff + description: Good for newcomers + - name: help wanted + color: "008672" + description: Extra attention is needed + - name: in progress + color: ededed + - name: invalid + color: e4e669 + description: This doesn't seem right + - name: next + color: ededed + - name: question + color: d876e3 + description: Further information is requested + - name: review + color: ededed + - name: security + color: fbca04 + - name: wip + color: b60205 + description: Work In Progress + - name: wontfix + color: ffffff + description: This will not be worked on diff --git a/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-community.yaml b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-community.yaml new file mode 100644 index 0000000000..45d540ea7a --- /dev/null +++ b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-community.yaml @@ -0,0 +1,27 @@ +# Scanned and autogenerated by https://github.com/tonglil/labeler +--- +repo: kata-containers/community +labels: + - name: WIP + color: b60205 + - name: bitesize + color: d4c5f9 + description: small/easy task + - name: bug + color: ee0701 + - name: do-not-merge + color: b60205 + - name: duplicate + color: cccccc + - name: enhancement + color: 84b6eb + - name: good first issue + color: 7057ff + - name: help wanted + color: 33aa3f + - name: invalid + color: e6e6e6 + - name: question + color: cc317c + - name: wontfix + color: ffffff diff --git a/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-kata-containers.yaml b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-kata-containers.yaml new file mode 100644 index 0000000000..d88016b29c --- /dev/null +++ b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-kata-containers.yaml @@ -0,0 +1,44 @@ +# Scanned and autogenerated by https://github.com/tonglil/labeler +--- +repo: kata-containers/kata-containers +labels: + - name: P1 + color: b60205 + description: Highest priority issue (Critical) + - name: P2 + color: d93f0b + description: Urgent issue + - name: P3 + color: fbca04 + description: Important issue + - name: P4 + color: fef2c0 + description: Noteworthy issue + - name: bitesize + color: d4c5f9 + description: small/easy task + - name: bug + color: ee0701 + - name: devices + color: 006b75 + description: direct device support + - name: duplicate + color: cccccc + - name: enhancement + color: 84b6eb + - name: feature + color: ef70a3 + - name: good first issue + color: 7057ff + - name: help wanted + color: 33aa3f + - name: invalid + color: e6e6e6 + - name: limitation + color: c2e0c6 + - name: question + color: cc317c + - name: security + color: fbca04 + - name: wontfix + color: ffffff diff --git a/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-tests.yaml b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-tests.yaml new file mode 100644 index 0000000000..3d06cb62bd --- /dev/null +++ b/tests/cmd/github-labels/archive/labeler-original-labels-kata-containers-tests.yaml @@ -0,0 +1,60 @@ +# Scanned and autogenerated by https://github.com/tonglil/labeler +--- +repo: kata-containers/tests +labels: + - name: CI + color: 0052cc + description: Continuous Integration + - name: P1 + color: b60205 + description: Highest priority issue (Critical) + - name: P2 + color: d93f0b + description: Urgent issue + - name: P3 + color: fbca04 + description: Important issue + - name: P4 + color: fef2c0 + description: Noteworthy issue + - name: backlog + color: ededed + - name: bitesize + color: d4c5f9 + description: small/easy task + - name: bug + color: ee0701 + - name: do-not-merge + color: b60205 + - name: duplicate + color: cccccc + - name: enhancement + color: 84b6eb + - name: good first issue + color: 7057ff + - name: hackathon + color: 35bfa1 + description: PR/Issues in hackathon events + - name: help wanted + color: 33aa3f + - name: in progress + color: ededed + - name: invalid + color: e6e6e6 + - name: limitation + color: c2e0c6 + - name: next + color: ededed + - name: question + color: cc317c + - name: review + color: ededed + - name: security + color: fbca04 + - name: stable-candidate + color: bfdadc + description: Candidate to backport to stable branches + - name: wip + color: b60205 + - name: wontfix + color: ffffff diff --git a/tests/cmd/github-labels/check.go b/tests/cmd/github-labels/check.go new file mode 100644 index 0000000000..c462370e84 --- /dev/null +++ b/tests/cmd/github-labels/check.go @@ -0,0 +1,216 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "errors" + "fmt" + "strings" + "unicode" +) + +func containsWhitespace(s string) bool { + for _, ch := range s { + if unicode.IsSpace(ch) { + return true + } + } + + return false +} + +func isLower(s string) bool { + for _, ch := range s { + if !unicode.IsLetter(ch) { + continue + } + + if !unicode.IsLower(ch) { + return false + } + } + + return true +} + +func checkCategory(c Category) error { + if c.Name == "" { + return fmt.Errorf("category name cannot be blank: %+v", c) + } + + if containsWhitespace(c.Name) { + return fmt.Errorf("category name cannot contain whitespace: %+v", c) + } + + if !isLower(c.Name) { + return fmt.Errorf("category name must be all lower case: %+v", c) + } + + if c.Description == "" { + return fmt.Errorf("category description cannot be blank: %+v", c) + } + + first := c.Description[0] + + if !unicode.IsUpper(rune(first)) { + return fmt.Errorf("category description needs initial capital letter: %+v", c) + } + + if !strings.HasSuffix(c.Description, ".") { + return fmt.Errorf("category description needs trailing period: %+v", c) + } + + return nil +} + +func checkLabel(l Label) error { + if l.Name == "" { + return fmt.Errorf("label name cannot be blank: %+v", l) + } + + if !isLower(l.Name) { + return fmt.Errorf("label name must be all lower case: %+v", l) + } + + if containsWhitespace(l.Name) { + return fmt.Errorf("label name cannot contain whitespace: %+v", l) + } + + if l.Description == "" { + return fmt.Errorf("label description cannot be blank: %+v", l) + } + + first := l.Description[0] + + if !unicode.IsUpper(rune(first)) { + return fmt.Errorf("label description needs initial capital letter: %+v", l) + } + + if l.CategoryName == "" { + return fmt.Errorf("label category name cannot be blank: %+v", l) + } + + if l.Colour == "" { + return fmt.Errorf("label colour cannot be blank: %+v", l) + } + + return nil +} + +func checkLabelsAndCategories(lf *LabelsFile) error { + catCount := 0 + + var catNameMap map[string]int + var catDescMap map[string]int + + var labelNameMap map[string]int + var labelDescMap map[string]int + + catNameMap = make(map[string]int) + catDescMap = make(map[string]int) + labelNameMap = make(map[string]int) + labelDescMap = make(map[string]int) + + for _, c := range lf.Categories { + if err := checkCategory(c); err != nil { + return err + } + + catCount++ + + if _, ok := catNameMap[c.Name]; ok { + return fmt.Errorf("duplicate category name: %+v", c) + } + + catNameMap[c.Name] = 0 + + if _, ok := catDescMap[c.Description]; ok { + return fmt.Errorf("duplicate category description: %+v", c) + } + + catDescMap[c.Description] = 0 + } + + if catCount == 0 { + return errors.New("no categories found") + } + + labelCount := 0 + + for _, l := range lf.Labels { + if err := checkLabel(l); err != nil { + return err + } + + if _, ok := labelNameMap[l.Name]; ok { + return fmt.Errorf("duplicate label name: %+v", l) + } + + labelNameMap[l.Name] = 0 + + if _, ok := labelDescMap[l.Description]; ok { + return fmt.Errorf("duplicate label description: %+v", l) + } + + labelDescMap[l.Description] = 0 + + labelCount++ + + catName := l.CategoryName + + var value int + var ok bool + if value, ok = catNameMap[catName]; !ok { + return fmt.Errorf("invalid category %v found for label %+v", catName, l) + } + + // Record category name seen and count of occurrences + value++ + catNameMap[catName] = value + } + + if labelCount == 0 { + return errors.New("no labels found") + } + + if debug { + fmt.Printf("DEBUG: category count: %v\n", catCount) + fmt.Printf("DEBUG: label count: %v\n", labelCount) + } + + for name, count := range catNameMap { + if count == 0 { + return fmt.Errorf("category %v not used", name) + } + + if debug { + fmt.Printf("DEBUG: category %v: label count: %d\n", + name, count) + } + } + + return nil +} + +func check(lf *LabelsFile) error { + if lf.Description == "" { + return errors.New("description cannot be blank") + } + + if lf.Repo == "" { + return errors.New("repo cannot be blank") + } + + if len(lf.Categories) == 0 { + return errors.New("no categories") + } + + if len(lf.Labels) == 0 { + return errors.New("no labels") + } + + return checkLabelsAndCategories(lf) +} diff --git a/tests/cmd/github-labels/clean.go b/tests/cmd/github-labels/clean.go new file mode 100644 index 0000000000..1f834cc1fe --- /dev/null +++ b/tests/cmd/github-labels/clean.go @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import "strings" + +func cleanString(s string) string { + result := strings.Replace(s, "\n", " ", -1) + result = strings.Replace(result, "\t", "\\t", -1) + result = strings.TrimSpace(result) + + return result +} + +func cleanLabel(l Label) Label { + return Label{ + Name: cleanString(l.Name), + Description: cleanString(l.Description), + CategoryName: cleanString(l.CategoryName), + Colour: cleanString(l.Colour), + From: cleanString(l.From), + } +} + +func cleanCategory(c *Category) { + c.Name = cleanString(c.Name) + c.Description = cleanString(c.Description) + c.URL = cleanString(c.URL) +} + +func cleanCategories(lf *LabelsFile) { + var cleaned Categories + + for _, c := range lf.Categories { + cleanCategory(&c) + cleaned = append(cleaned, c) + } + + lf.Categories = cleaned +} + +func cleanLabels(lf *LabelsFile) { + var cleaned Labels + + for _, l := range lf.Labels { + new := cleanLabel(l) + cleaned = append(cleaned, new) + } + + lf.Labels = cleaned +} + +func clean(lf *LabelsFile) { + lf.Description = cleanString(lf.Description) + lf.Repo = cleanString(lf.Repo) + + cleanCategories(lf) + cleanLabels(lf) +} diff --git a/tests/cmd/github-labels/display.go b/tests/cmd/github-labels/display.go new file mode 100644 index 0000000000..888d514230 --- /dev/null +++ b/tests/cmd/github-labels/display.go @@ -0,0 +1,83 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "os" + "sort" +) + +var outputFile = os.Stdout + +// displayHandler is an interface that all output display handlers +// (formatters) must implement. +type DisplayHandler interface { + DisplayLabels(lf *LabelsFile) error + DisplayCategories(lf *LabelsFile, showLabels bool) error +} + +// DisplayHandlers encapsulates the list of available display handlers. +type DisplayHandlers struct { + handlers map[string]DisplayHandler +} + +// handlers is a map of the available output format display handling +// implementations. +var handlers map[string]DisplayHandler + +// NewDisplayHandlers create a new DisplayHandler. +func NewDisplayHandlers() *DisplayHandlers { + if handlers == nil { + handlers = make(map[string]DisplayHandler) + + handlers["md"] = NewDisplayMD(outputFile) + handlers[textFormat] = NewDisplayText(outputFile) + handlers["tsv"] = NewDisplayTSV(outputFile) + } + + h := &DisplayHandlers{ + handlers: handlers, + } + + return h +} + +// find looks for a display handler corresponding to the specified format +func (d *DisplayHandlers) find(format string) DisplayHandler { + for f, handler := range d.handlers { + if f == format { + return handler + } + } + + return nil +} + +// Get returns a list of the available formatters (display handler names). +func (d *DisplayHandlers) Get() []string { + var formats []string + + for f := range d.handlers { + formats = append(formats, f) + } + + sort.Strings(formats) + + return formats +} + +func show(inputFilename string, handler DisplayHandler, what DataToShow, withLabels bool) error { + lf, err := readYAML(inputFilename) + if err != nil { + return err + } + + if what == showLabels { + return handler.DisplayLabels(lf) + } + + return handler.DisplayCategories(lf, withLabels) +} diff --git a/tests/cmd/github-labels/display_markdown.go b/tests/cmd/github-labels/display_markdown.go new file mode 100644 index 0000000000..7ac87e7366 --- /dev/null +++ b/tests/cmd/github-labels/display_markdown.go @@ -0,0 +1,75 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "os" + + "github.com/olekukonko/tablewriter" +) + +type displayMD struct { + writer *tablewriter.Table +} + +func NewDisplayMD(file *os.File) DisplayHandler { + md := &displayMD{} + + md.writer = tablewriter.NewWriter(file) + md.writer.SetCenterSeparator("|") + + md.writer.SetBorders(tablewriter.Border{ + Left: true, + Right: true, + Top: false, + Bottom: false, + }) + + // Critical for GitHub Flavoured Markdown + md.writer.SetAutoWrapText(false) + + return md +} + +func (d *displayMD) render(headerFields []string, records [][]string) { + d.writer.SetHeader(headerFields) + d.writer.AppendBulk(records) + d.writer.Render() +} + +func (d *displayMD) DisplayLabels(lf *LabelsFile) error { + var records [][]string + + for _, l := range lf.Labels { + record := labelToRecord(l, true) + records = append(records, record) + } + + headerFields := labelHeaderRecord() + + d.render(headerFields, records) + + return nil +} + +func (d *displayMD) DisplayCategories(lf *LabelsFile, showLabels bool) error { + headerFields := categoryHeaderRecord(showLabels) + + var records [][]string + + for _, c := range lf.Categories { + record, err := categoryToRecord(lf, c, showLabels, true) + if err != nil { + return err + } + + records = append(records, record) + } + + d.render(headerFields, records) + + return nil +} diff --git a/tests/cmd/github-labels/display_text.go b/tests/cmd/github-labels/display_text.go new file mode 100644 index 0000000000..63211975d5 --- /dev/null +++ b/tests/cmd/github-labels/display_text.go @@ -0,0 +1,101 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "os" +) + +type displayText struct { + file *os.File +} + +func NewDisplayText(file *os.File) DisplayHandler { + return &displayText{ + file: file, + } +} + +func (d *displayText) DisplayLabels(lf *LabelsFile) error { + _, err := fmt.Fprintf(d.file, "Labels (count: %d):\n", len(lf.Labels)) + if err != nil { + return err + } + + for _, l := range lf.Labels { + err = d.displayLabel(l) + if err != nil { + return err + } + } + + return nil +} + +func (d *displayText) displayLabel(l Label) error { + _, err := fmt.Fprintf(d.file, " %s (%q) [category %q, colour %q, from %q]\n", + l.Name, + l.Description, + l.CategoryName, + l.Colour, + l.From) + + return err +} + +func (d *displayText) DisplayCategories(lf *LabelsFile, showLabels bool) error { + _, err := fmt.Fprintf(d.file, "Categories (count: %d):\n", len(lf.Categories)) + if err != nil { + return err + } + + for _, c := range lf.Categories { + err := d.displayCategory(c, lf, showLabels) + if err != nil { + return err + } + } + + return nil +} + +func (d *displayText) displayCategory(c Category, lf *LabelsFile, showLabels bool) error { + if showLabels { + labels, err := getLabelsByCategory(c.Name, lf) + if err != nil { + return err + } + + _, err = fmt.Fprintf(d.file, " %s (%q, label count: %d, url: %v)\n", + c.Name, + c.Description, + len(labels), + c.URL) + if err != nil { + return err + } + + for _, label := range labels { + _, err := fmt.Fprintf(d.file, " %s (%q)\n", + label.Name, + label.Description) + if err != nil { + return err + } + } + } else { + _, err := fmt.Printf(" %s (%q, url: %v)\n", + c.Name, + c.Description, + c.URL) + if err != nil { + return err + } + } + + return nil +} diff --git a/tests/cmd/github-labels/display_tsv.go b/tests/cmd/github-labels/display_tsv.go new file mode 100644 index 0000000000..e848003696 --- /dev/null +++ b/tests/cmd/github-labels/display_tsv.go @@ -0,0 +1,66 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "encoding/csv" + "os" +) + +type displayTSV struct { + writer *csv.Writer +} + +func NewDisplayTSV(file *os.File) DisplayHandler { + tsv := &displayTSV{} + tsv.writer = csv.NewWriter(file) + + // Tab separator + tsv.writer.Comma = rune('\t') + + return tsv +} + +func (d *displayTSV) DisplayLabels(lf *LabelsFile) error { + record := labelHeaderRecord() + if err := d.writer.Write(record); err != nil { + return err + } + + for _, l := range lf.Labels { + record := labelToRecord(l, false) + + if err := d.writer.Write(record); err != nil { + return err + } + } + + d.writer.Flush() + + return d.writer.Error() +} + +func (d *displayTSV) DisplayCategories(lf *LabelsFile, showLabels bool) error { + record := categoryHeaderRecord(showLabels) + if err := d.writer.Write(record); err != nil { + return err + } + + for _, c := range lf.Categories { + record, err := categoryToRecord(lf, c, showLabels, false) + if err != nil { + return err + } + + if err := d.writer.Write(record); err != nil { + return err + } + } + + d.writer.Flush() + + return d.writer.Error() +} diff --git a/tests/cmd/github-labels/github-labels.sh b/tests/cmd/github-labels/github-labels.sh new file mode 100755 index 0000000000..e5bf4a7359 --- /dev/null +++ b/tests/cmd/github-labels/github-labels.sh @@ -0,0 +1,176 @@ +#!/bin/bash +# +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +# Description: Generate the combined GitHub labels database for the +# specified repository. + +set -e + +script_name=${0##*/} + +source "/etc/os-release" || "source /usr/lib/os-release" + +self_dir=$(dirname "$(readlink -f "$0")") +cidir="${self_dir}/../../.ci" +source "${cidir}/lib.sh" + +typeset -r labels_file="labels.yaml" +typeset -r labels_template="${labels_file}.in" + +typeset -r master_labels_file="${self_dir}/${labels_file}" +typeset -r master_labels_template="${self_dir}/${labels_template}" + +# The GitHub labels API requires a colour for each label so +# default to a white background. +typeset -r default_color="ffffff" + +need_yq() { + # install yq if not exist + ${cidir}/install_yq.sh + + command -v yq &>/dev/null || \ + die 'yq command not found. Ensure "$GOPATH/bin" is in your $PATH.' +} + +merge_yaml() +{ + local -r file1="$1" + local -r file2="$2" + local -r out="$3" + + [ -n "$file1" ] || die "need 1st file" + [ -n "$file2" ] || die "need 2nd file" + [ -n "$out" ] || die "need output file" + + need_yq + yq merge "$file1" --append "$file2" > "$out" +} + +check_yaml() +{ + local -r file="$1" + + [ -n "$file" ] || die "need file to check" + + need_yq + yq read "$file" >/dev/null + + [ -z "$(command -v yamllint)" ] && die "need yamllint installed" + + # Deal with different versions of the tool + local opts="" + local has_strict_opt=$(yamllint --help 2>&1|grep -- --strict) + + [ -n "$has_strict_opt" ] && opts+="--strict" + + yamllint $opts "$file" +} + +# Expand the variables in the labels database. +generate_yaml() +{ + local repo="$1" + local template="$2" + local out="$3" + + [ -n "$repo" ] || die "need repo" + [ -n "$template" ] || die "need template" + [ -n "$out" ] || die "need output file" + + local repo_slug=$(echo "${repo}"|sed 's!github.com/!!g') + + sed \ + -e "s|REPO_SLUG|${repo_slug}|g" \ + -e "s|DEFAULT_COLOUR|${default_color}|g" \ + "$template" > "$out" + + check_yaml "$out" +} + +cmd_generate() +{ + local repo="$1" + local out_file="$2" + + [ -n "$repo" ] || die "need repo" + [ -n "$out_file" ] || die "need output file" + + # Create the master database from the template + generate_yaml \ + "${repo}" \ + "${master_labels_template}" \ + "${master_labels_file}" + + local -r repo_labels_template="${GOPATH}/src/${repo}/${labels_template}" + local -r repo_labels_file="${GOPATH}/src/${repo}/${labels_file}" + + # Check for a repo-specific set of labels + if [ -e "${repo_labels_template}" ]; then + info "Found repo-specific labels database" + + # Generate repo-specific labels from template + generate_yaml \ + "${repo}" \ + "${repo_labels_template}" \ + "${repo_labels_file}" + + # Combine the two databases + tmp=$(mktemp) + + merge_yaml \ + "${master_labels_file}" \ + "${repo_labels_file}" \ + "${tmp}" + + mv "${tmp}" "${out_file}" + else + info "No repo-specific labels database" + cp "${master_labels_file}" "${out_file}" + fi + + + info "Generated labels database ${out_file}" + + # Perform checks + kata-github-labels check "${out_file}" +} + +usage() +{ + cat < + +Examples: + + # Generate combined labels database for runtime repo and write to + # specified file + \$ ${script_name} generate github.com/kata-containers/kata-containers /tmp/out.yaml + +EOF +} + +main() +{ + case "$1" in + generate) + shift + cmd_generate "$@" + ;; + + help|"") + usage + exit 0 + ;; + + *) + die "Invalid command: '$1'" + ;; + esac +} + +main "$@" diff --git a/tests/cmd/github-labels/labels.yaml b/tests/cmd/github-labels/labels.yaml new file mode 100644 index 0000000000..c8e69027b6 --- /dev/null +++ b/tests/cmd/github-labels/labels.yaml @@ -0,0 +1,555 @@ +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +description: | + This file contains a list of all the generic GitHub labels used by all Kata + Containers GitHub repositories. + + Each repository can optionally contain a top-level `labels.yaml` that + specifies a list of repository-specific labels (and possibly additional + categories). The labels in the repository-specific labels file plus the + labels defined in this file define the minimum list of labels for the + repository in question. + + Each label must specify: + + - Name (which must be lower-case without spaces) + - Description + - Category + - Colour (explicit colour, or `ffffff`) + + A label may also specify a "From" value. This is used for renaming labels; + if a label has an associated "From" value, an existing label whose name is + specified by the "From" value will be renamed to the label name. + + A category is a collective name used to describe one or more related labels. + Each category must specify: + + - Name (which must be lower-case without spaces) + - Description + + A category may also specify a related URL which points to a document + containing further information. + +categories: + - name: api + description: Change related to an Application Programming Interface. + + - name: architecture-committee + description: Needs input from the Architecture Committee. + url: https://github.com/kata-containers/community#architecture-committee + + - name: area + description: Code component / general part of product affected. + + - name: backport + description: | + Code that needs to be applied to other branches, generally older stable + ones. + + - name: behaviour + description: | + How the issue affect the operation of the system. A more precise version + of regression. + + - name: block + description: | + Stop a PR from being merged. + + - name: cleanup + description: Refactoring, restructuring or general tidy-up needed. + + - name: customer + description: Related to a customer. + + - name: design + description: Requires formal review on the approach to solving the problem. + + - name: detail + description: Need further information from the user or author. + + - name: documentation + description: Needs more documentation. + + - name: environment + description: Related to particular system environment. + + - name: help + description: | + Request for technical help / extra resource. Also used for assisted + workflow. + + - name: label-admin + description: Relates to the administration of labels. + + - name: limitation + description: | + Issue cannot be resolved (too hard/impossible, would be too slow, + insufficient resources, etc). + url: | + https://github.com/kata-containers/kata-containers/blob/main/docs/Documentation-Requirements.md + + - name: new-contributor + description: Small, self-contained tasks suitable for newcomers. + url: | + https://github.com/kata-containers/community/blob/main/CONTRIBUTING.md + + - name: priority + description: | + Relative urgency (time-critical). + + - name: question + description: Needs input from the team. + + - name: rebase + description: Code conflicts need to be resolved. + + - name: related + description: | + Related project. Base set can be generated from + https://github.com/kata-containers/kata-containers/blob/main/versions.yaml. + + - name: release + description: Related to production of new versions. + + - name: resolution + description: | + Issue is not (or no longer) valid for some reason. Label specifies + reason for closing. + + - name: security + description: Potential or actual vulnerability / CVE. + url: https://github.com/kata-containers/community/blob/main/VMT/VMT.md + + - name: severity + description: Relative importance (mission-critical). + + - name: sizing + description: Estimate of the complexity of the task (story points). + + - name: sub-type + description: More specific detail on the type category. + + - name: team + description: Team that needs to analyse the issue. + + - name: test + description: New tests needed. + + - name: type + description: High-level summary of the issue. + + - name: vendor + description: Related to handling imported code. + url: | + https://github.com/kata-containers/community/blob/main/CONTRIBUTING.md#re-vendor-prs + +repo: kata-containers/kata-containers + +labels: + - name: api-breakage + description: API was broken + category: api + color: ff0000 + + - name: api-change + description: API change + category: api + color: ffffff + + - name: architecture-specific + description: Affects subset of architectures + category: environment + color: ffffff + + - name: area/api + description: Application Programming Interface + category: area + color: ffffff + + - name: area/cli + description: Command Line Interface (flags/options and arguments) + category: area + color: ffffff + + - name: area/comms + description: Communications (gRPC, Yamux, etc) + category: area + color: ffffff + + - name: area/config + description: Configuration + category: area + color: ffffff + + - name: area/logging + description: Logging + category: area + color: ffffff + + - name: area/networking + description: Networking + category: area + color: ffffff + + - name: area/storage + description: Storage + category: area + color: ffffff + + - name: area/tracing + description: Tracing + category: area + color: ffffff + + - name: backport + description: Code needs to be applied to older (stable) releases + category: backport + color: ffffff + + - name: bug + description: Incorrect behaviour + category: type + color: ff0000 + + - name: cannot-reproduce + description: Issue cannot be recreated + category: resolution + color: ffffff + + - name: cleanup + description: General tidy-up + category: cleanup + color: ffffff + + - name: crash + description: Causes part of the system to crash + category: behaviour + color: ffffff + + - name: customer + description: Relates to a customer + category: customer + color: ffffff + + - name: data-loss + description: System loses information + category: behaviour + color: ffffff + + - name: deprecate + description: Highlight a feature that will soon be removed + category: cleanup + color: ffffff + + - name: do-not-merge + description: PR has problems or depends on another + category: block + color: ff0000 + + - name: duplicate + description: Same issue as one already reported + category: resolution + color: ffffff + + - name: enhancement + description: Improvement to an existing feature + category: type + color: ffffff + + - name: feature + description: New functionality + category: type + color: ffffff + + - name: good-first-issue + description: Small and simple task for new contributors + category: new-contributor + color: ffffff + + - name: hang + description: System appears to stop operating or freeze + category: behaviour + color: ffffff + + - name: high-priority + description: Very urgent issue (resolve quickly) + category: priority + color: ff7f00 + + - name: high-severity + description: Very important issue + category: severity + color: 00d7ff + + - name: highest-priority + description: Critically urgent issue (must be resolved as soon as possible) + category: priority + color: ff0000 + + - name: highest-severity + description: Extremely important issue + category: severity + color: 00ffff + + - name: invalid + description: Issue does not make sense + category: resolution + color: ffffff + + - name: limitation + description: Issue cannot be resolved + category: limitation + color: ffffff + + - name: medium-priority + description: Urgent issue (resolve before unprioritised issues) + category: priority + color: ffff00 + + - name: medium-severity + description: Important issue + category: severity + color: 0000ff + + - name: needs-decision + description: Requires input from the Architecture Committee + category: architecture-committee + color: ffffff + + - name: needs-design-doc + description: Needs a document explaining the design + category: design + color: ffffff + + - name: needs-design-review + description: Needs a formal design review of the approach + category: design + color: ffffff + + - name: needs-docs + description: Needs some new or updated documentation + category: documentation + color: ffffff + + - name: needs-help + description: Request for extra help (technical, resource, etc) + category: help + color: ffffff + + - name: needs-integration-tests + description: | + Needs new system/integration tests to validate behaviour in the tests + repository + category: test + color: ffffff + + - name: needs-more-info + description: Blocked until user or author provides further details + category: detail + color: ffffff + + - name: needs-new-label + description: New label required to categorise this issue + category: label-admin + color: ffffff + + - name: needs-rebase + description: PR contains conflicts which need resolving + category: rebase + color: ffffff + + - name: needs-revendor + description: Needs imported code to be re-vendored + category: vendor + color: ffffff + + - name: needs-review + description: Needs to be assessed by the team. + category: team + color: 00ff00 + + - name: needs-unit-tests + description: Needs new unit tests to validate behaviour in this repository + category: test + color: ffffff + + - name: os-specific + description: Affects subset of operating system / distro versions + category: environment + color: ffffff + + - name: performance + description: System runs too slowly + category: behaviour + color: ffffff + + - name: question + description: Requires an answer + category: question + color: ffffff + + - name: refactor + description: Remove duplication, improve organisation, etc + category: cleanup + color: ffffff + + - name: regression + description: Behaviour inadvertently reverted to older behaviour + category: sub-type + color: ffffff + + - name: related/containerd + description: Containerd + category: related + color: ffffff + + - name: related/cri + description: CRI + category: related + color: ffffff + + - name: related/crio + description: CRIO + category: related + color: ffffff + + - name: related/docker + description: Docker + category: related + color: ffffff + + - name: related/firecracker + description: Firecracker + category: related + color: ffffff + + - name: related/k8s + description: Kubernetes + category: related + color: ffffff + + - name: related/qemu + description: QEMU + category: related + color: ffffff + + - name: related/runc + description: Runc + category: related + color: ffffff + + - name: release-gating + description: Release must wait for this to be resolved before release + category: release + color: ffffff + + - name: resource-hog + description: System uses too many resources (such as memory) + category: behaviour + color: ffffff + + - name: resource-leak + description: System does not free resources (such as memory) + category: behaviour + color: ffffff + + - name: rfc + description: Requires input from the team + category: question + color: ffffff + + - name: security + description: Potential or actual security issue + category: security + color: ff0000 + + - name: size/huge + description: | + Largest and most complex task (probably needs breaking into small + pieces) + category: sizing + color: ffffff + + - name: size/large + description: Task of significant size + category: sizing + color: ffffff + + - name: size/medium + description: Average sized task + category: sizing + color: ffffff + + - name: size/small + description: Small and simple task + category: sizing + color: ffffff + + - name: size/tiny + description: Smallest and simplest task + category: sizing + color: ffffff + + - name: stale + description: Issue or PR was not updated in a timely fashion + category: resolution + color: ffffff + + - name: team/ci + description: Need Continuous Integration Team input + category: team + color: ffffff + + - name: team/developer + description: Need Developer Team input + category: team + color: ffffff + + - name: team/documentation + description: Need Documentation Team input + category: team + color: ffffff + + - name: team/kernel + description: Need Kernel Team input + category: team + color: ffffff + + - name: team/metrics + description: Need Metrics Team input + category: team + color: ffffff + + - name: team/packaging + description: Need Packaging Team input + category: team + color: ffffff + + - name: team/test + description: Need Test Team input + category: team + color: ffffff + + - name: unreliable + description: Part of the system is not stable + category: behaviour + color: ffffff + + - name: wip + description: Work in Progress (PR incomplete - needs more work or rework) + category: block + color: ff0000 + + - name: wont-fix + description: Issue will not be fixed (not a good use of limited resources) + category: resolution + color: ffffff + + - name: wrong-repo + description: Raised in incorrect repository + category: resolution + color: ffffff diff --git a/tests/cmd/github-labels/labels.yaml.in b/tests/cmd/github-labels/labels.yaml.in new file mode 100644 index 0000000000..65d5ea69f3 --- /dev/null +++ b/tests/cmd/github-labels/labels.yaml.in @@ -0,0 +1,555 @@ +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +--- +description: | + This file contains a list of all the generic GitHub labels used by all Kata + Containers GitHub repositories. + + Each repository can optionally contain a top-level `labels.yaml` that + specifies a list of repository-specific labels (and possibly additional + categories). The labels in the repository-specific labels file plus the + labels defined in this file define the minimum list of labels for the + repository in question. + + Each label must specify: + + - Name (which must be lower-case without spaces) + - Description + - Category + - Colour (explicit colour, or `DEFAULT_COLOUR`) + + A label may also specify a "From" value. This is used for renaming labels; + if a label has an associated "From" value, an existing label whose name is + specified by the "From" value will be renamed to the label name. + + A category is a collective name used to describe one or more related labels. + Each category must specify: + + - Name (which must be lower-case without spaces) + - Description + + A category may also specify a related URL which points to a document + containing further information. + +categories: + - name: api + description: Change related to an Application Programming Interface. + + - name: architecture-committee + description: Needs input from the Architecture Committee. + url: https://github.com/kata-containers/community#architecture-committee + + - name: area + description: Code component / general part of product affected. + + - name: backport + description: | + Code that needs to be applied to other branches, generally older stable + ones. + + - name: behaviour + description: | + How the issue affect the operation of the system. A more precise version + of regression. + + - name: block + description: | + Stop a PR from being merged. + + - name: cleanup + description: Refactoring, restructuring or general tidy-up needed. + + - name: customer + description: Related to a customer. + + - name: design + description: Requires formal review on the approach to solving the problem. + + - name: detail + description: Need further information from the user or author. + + - name: documentation + description: Needs more documentation. + + - name: environment + description: Related to particular system environment. + + - name: help + description: | + Request for technical help / extra resource. Also used for assisted + workflow. + + - name: label-admin + description: Relates to the administration of labels. + + - name: limitation + description: | + Issue cannot be resolved (too hard/impossible, would be too slow, + insufficient resources, etc). + url: | + https://github.com/kata-containers/kata-containers/blob/main/docs/Documentation-Requirements.md + + - name: new-contributor + description: Small, self-contained tasks suitable for newcomers. + url: | + https://github.com/kata-containers/community/blob/main/CONTRIBUTING.md + + - name: priority + description: | + Relative urgency (time-critical). + + - name: question + description: Needs input from the team. + + - name: rebase + description: Code conflicts need to be resolved. + + - name: related + description: | + Related project. Base set can be generated from + https://github.com/kata-containers/kata-containers/blob/main/versions.yaml. + + - name: release + description: Related to production of new versions. + + - name: resolution + description: | + Issue is not (or no longer) valid for some reason. Label specifies + reason for closing. + + - name: security + description: Potential or actual vulnerability / CVE. + url: https://github.com/kata-containers/community/blob/main/VMT/VMT.md + + - name: severity + description: Relative importance (mission-critical). + + - name: sizing + description: Estimate of the complexity of the task (story points). + + - name: sub-type + description: More specific detail on the type category. + + - name: team + description: Team that needs to analyse the issue. + + - name: test + description: New tests needed. + + - name: type + description: High-level summary of the issue. + + - name: vendor + description: Related to handling imported code. + url: | + https://github.com/kata-containers/community/blob/main/CONTRIBUTING.md#re-vendor-prs + +repo: REPO_SLUG + +labels: + - name: api-breakage + description: API was broken + category: api + color: ff0000 + + - name: api-change + description: API change + category: api + color: DEFAULT_COLOUR + + - name: architecture-specific + description: Affects subset of architectures + category: environment + color: DEFAULT_COLOUR + + - name: area/api + description: Application Programming Interface + category: area + color: DEFAULT_COLOUR + + - name: area/cli + description: Command Line Interface (flags/options and arguments) + category: area + color: DEFAULT_COLOUR + + - name: area/comms + description: Communications (gRPC, Yamux, etc) + category: area + color: DEFAULT_COLOUR + + - name: area/config + description: Configuration + category: area + color: DEFAULT_COLOUR + + - name: area/logging + description: Logging + category: area + color: DEFAULT_COLOUR + + - name: area/networking + description: Networking + category: area + color: DEFAULT_COLOUR + + - name: area/storage + description: Storage + category: area + color: DEFAULT_COLOUR + + - name: area/tracing + description: Tracing + category: area + color: DEFAULT_COLOUR + + - name: backport + description: Code needs to be applied to older (stable) releases + category: backport + color: DEFAULT_COLOUR + + - name: bug + description: Incorrect behaviour + category: type + color: ff0000 + + - name: cannot-reproduce + description: Issue cannot be recreated + category: resolution + color: DEFAULT_COLOUR + + - name: cleanup + description: General tidy-up + category: cleanup + color: DEFAULT_COLOUR + + - name: crash + description: Causes part of the system to crash + category: behaviour + color: DEFAULT_COLOUR + + - name: customer + description: Relates to a customer + category: customer + color: DEFAULT_COLOUR + + - name: data-loss + description: System loses information + category: behaviour + color: DEFAULT_COLOUR + + - name: deprecate + description: Highlight a feature that will soon be removed + category: cleanup + color: DEFAULT_COLOUR + + - name: do-not-merge + description: PR has problems or depends on another + category: block + color: ff0000 + + - name: duplicate + description: Same issue as one already reported + category: resolution + color: DEFAULT_COLOUR + + - name: enhancement + description: Improvement to an existing feature + category: type + color: DEFAULT_COLOUR + + - name: feature + description: New functionality + category: type + color: DEFAULT_COLOUR + + - name: good-first-issue + description: Small and simple task for new contributors + category: new-contributor + color: DEFAULT_COLOUR + + - name: hang + description: System appears to stop operating or freeze + category: behaviour + color: DEFAULT_COLOUR + + - name: high-priority + description: Very urgent issue (resolve quickly) + category: priority + color: ff7f00 + + - name: high-severity + description: Very important issue + category: severity + color: 00d7ff + + - name: highest-priority + description: Critically urgent issue (must be resolved as soon as possible) + category: priority + color: ff0000 + + - name: highest-severity + description: Extremely important issue + category: severity + color: 00ffff + + - name: invalid + description: Issue does not make sense + category: resolution + color: DEFAULT_COLOUR + + - name: limitation + description: Issue cannot be resolved + category: limitation + color: DEFAULT_COLOUR + + - name: medium-priority + description: Urgent issue (resolve before unprioritised issues) + category: priority + color: ffff00 + + - name: medium-severity + description: Important issue + category: severity + color: 0000ff + + - name: needs-decision + description: Requires input from the Architecture Committee + category: architecture-committee + color: DEFAULT_COLOUR + + - name: needs-design-doc + description: Needs a document explaining the design + category: design + color: DEFAULT_COLOUR + + - name: needs-design-review + description: Needs a formal design review of the approach + category: design + color: DEFAULT_COLOUR + + - name: needs-docs + description: Needs some new or updated documentation + category: documentation + color: DEFAULT_COLOUR + + - name: needs-help + description: Request for extra help (technical, resource, etc) + category: help + color: DEFAULT_COLOUR + + - name: needs-integration-tests + description: | + Needs new system/integration tests to validate behaviour in the tests + repository + category: test + color: DEFAULT_COLOUR + + - name: needs-more-info + description: Blocked until user or author provides further details + category: detail + color: DEFAULT_COLOUR + + - name: needs-new-label + description: New label required to categorise this issue + category: label-admin + color: DEFAULT_COLOUR + + - name: needs-rebase + description: PR contains conflicts which need resolving + category: rebase + color: DEFAULT_COLOUR + + - name: needs-revendor + description: Needs imported code to be re-vendored + category: vendor + color: DEFAULT_COLOUR + + - name: needs-review + description: Needs to be assessed by the team. + category: team + color: 00ff00 + + - name: needs-unit-tests + description: Needs new unit tests to validate behaviour in this repository + category: test + color: DEFAULT_COLOUR + + - name: os-specific + description: Affects subset of operating system / distro versions + category: environment + color: DEFAULT_COLOUR + + - name: performance + description: System runs too slowly + category: behaviour + color: DEFAULT_COLOUR + + - name: question + description: Requires an answer + category: question + color: DEFAULT_COLOUR + + - name: refactor + description: Remove duplication, improve organisation, etc + category: cleanup + color: DEFAULT_COLOUR + + - name: regression + description: Behaviour inadvertently reverted to older behaviour + category: sub-type + color: DEFAULT_COLOUR + + - name: related/containerd + description: Containerd + category: related + color: DEFAULT_COLOUR + + - name: related/cri + description: CRI + category: related + color: DEFAULT_COLOUR + + - name: related/crio + description: CRIO + category: related + color: DEFAULT_COLOUR + + - name: related/docker + description: Docker + category: related + color: DEFAULT_COLOUR + + - name: related/firecracker + description: Firecracker + category: related + color: DEFAULT_COLOUR + + - name: related/k8s + description: Kubernetes + category: related + color: DEFAULT_COLOUR + + - name: related/qemu + description: QEMU + category: related + color: DEFAULT_COLOUR + + - name: related/runc + description: Runc + category: related + color: DEFAULT_COLOUR + + - name: release-gating + description: Release must wait for this to be resolved before release + category: release + color: DEFAULT_COLOUR + + - name: resource-hog + description: System uses too many resources (such as memory) + category: behaviour + color: DEFAULT_COLOUR + + - name: resource-leak + description: System does not free resources (such as memory) + category: behaviour + color: DEFAULT_COLOUR + + - name: rfc + description: Requires input from the team + category: question + color: DEFAULT_COLOUR + + - name: security + description: Potential or actual security issue + category: security + color: ff0000 + + - name: size/huge + description: | + Largest and most complex task (probably needs breaking into small + pieces) + category: sizing + color: DEFAULT_COLOUR + + - name: size/large + description: Task of significant size + category: sizing + color: DEFAULT_COLOUR + + - name: size/medium + description: Average sized task + category: sizing + color: DEFAULT_COLOUR + + - name: size/small + description: Small and simple task + category: sizing + color: DEFAULT_COLOUR + + - name: size/tiny + description: Smallest and simplest task + category: sizing + color: DEFAULT_COLOUR + + - name: stale + description: Issue or PR was not updated in a timely fashion + category: resolution + color: DEFAULT_COLOUR + + - name: team/ci + description: Need Continuous Integration Team input + category: team + color: DEFAULT_COLOUR + + - name: team/developer + description: Need Developer Team input + category: team + color: DEFAULT_COLOUR + + - name: team/documentation + description: Need Documentation Team input + category: team + color: DEFAULT_COLOUR + + - name: team/kernel + description: Need Kernel Team input + category: team + color: DEFAULT_COLOUR + + - name: team/metrics + description: Need Metrics Team input + category: team + color: DEFAULT_COLOUR + + - name: team/packaging + description: Need Packaging Team input + category: team + color: DEFAULT_COLOUR + + - name: team/test + description: Need Test Team input + category: team + color: DEFAULT_COLOUR + + - name: unreliable + description: Part of the system is not stable + category: behaviour + color: DEFAULT_COLOUR + + - name: wip + description: Work in Progress (PR incomplete - needs more work or rework) + category: block + color: ff0000 + + - name: wont-fix + description: Issue will not be fixed (not a good use of limited resources) + category: resolution + color: DEFAULT_COLOUR + + - name: wrong-repo + description: Raised in incorrect repository + category: resolution + color: DEFAULT_COLOUR diff --git a/tests/cmd/github-labels/main.go b/tests/cmd/github-labels/main.go new file mode 100644 index 0000000000..31a1b9b1d7 --- /dev/null +++ b/tests/cmd/github-labels/main.go @@ -0,0 +1,157 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Description: Program to check and summarise the Kata GitHub +// labels YAML file. + +package main + +import ( + "errors" + "fmt" + "os" + + "github.com/urfave/cli" +) + +type DataToShow int + +const ( + showLabels DataToShow = iota + showCategories DataToShow = iota + + textFormat = "text" + defaultOutputFormat = textFormat +) + +var errNeedYAMLFile = errors.New("need YAML file") + +var ( + // set by the build + name = "" + version = "" + commit = "" + + debug = false +) + +var formatFlag = cli.StringFlag{ + Name: "format", + Usage: "display in specified format ('help' to show all)", + Value: defaultOutputFormat, +} + +func commonHandler(context *cli.Context, what DataToShow, withLabels bool) error { + handlers := NewDisplayHandlers() + + format := context.String("format") + if format == "help" { + availableFormats := handlers.Get() + + for _, format := range availableFormats { + fmt.Fprintf(outputFile, "%s\n", format) + } + + return nil + } + + handler := handlers.find(format) + if handler == nil { + return fmt.Errorf("no handler for format %q", format) + } + + if context.NArg() == 0 { + return errNeedYAMLFile + } + + file := context.Args().Get(0) + + return show(file, handler, what, withLabels) +} + +func main() { + app := cli.NewApp() + app.Description = "tool to manipulate Kata GitHub labels" + app.Usage = app.Description + app.Version = fmt.Sprintf("%s %s (commit %v)", name, version, commit) + + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug, d", + Usage: "enable debug output", + Destination: &debug, + }, + } + + app.Commands = []cli.Command{ + { + Name: "check", + Usage: "Perform tests on the labels database", + Description: "Exit code denotes success", + Action: func(context *cli.Context) error { + if context.NArg() == 0 { + return errNeedYAMLFile + } + + file := context.Args().Get(0) + + return checkYAML(file) + }, + }, + { + Name: "show", + Usage: "Display labels database details", + Subcommands: []cli.Command{ + { + Name: "categories", + Usage: "Display categories from labels database", + Flags: []cli.Flag{ + formatFlag, + cli.BoolFlag{ + Name: "with-labels", + Usage: "Add labels in each category to output", + }, + }, + Action: func(context *cli.Context) error { + withLabels := context.Bool("with-labels") + return commonHandler(context, showCategories, withLabels) + }, + }, + { + Name: "labels", + Usage: "Display labels from labels database", + Flags: []cli.Flag{ + formatFlag, + }, + Action: func(context *cli.Context) error { + withLabels := context.Bool("with-labels") + return commonHandler(context, showLabels, withLabels) + }, + }, + }, + }, + { + Name: "sort", + Usage: "Sort the specified YAML labels file and write to a new file", + Description: "Can be used to keep the master labels file sorted", + ArgsUsage: " ", + Action: func(context *cli.Context) error { + if context.NArg() != 2 { + return errors.New("need two YAML files: ") + } + + from := context.Args().Get(0) + to := context.Args().Get(1) + return sortYAML(from, to) + }, + }, + } + + err := app.Run(os.Args) + if err != nil { + fmt.Fprintf(os.Stderr, "ERROR: %v\n", err) + os.Exit(1) + } +} diff --git a/tests/cmd/github-labels/record.go b/tests/cmd/github-labels/record.go new file mode 100644 index 0000000000..6bdc12b946 --- /dev/null +++ b/tests/cmd/github-labels/record.go @@ -0,0 +1,102 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "strings" +) + +const ( + labelNamesSeparator = "," +) + +func labelToRecord(l Label, quote bool) (record []string) { + name := l.Name + category := l.CategoryName + colour := l.Colour + from := l.From + + if quote { + name = fmt.Sprintf("`%s`", l.Name) + category = fmt.Sprintf("`%s`", l.CategoryName) + colour = fmt.Sprintf("`%s`", l.Colour) + if from != "" { + from = fmt.Sprintf("`%s`", l.From) + } + } + + record = append(record, name) + record = append(record, l.Description) + record = append(record, category) + record = append(record, colour) + record = append(record, from) + + return record +} + +func labelHeaderRecord() []string { + return []string{ + "Name", + "Description", + "Category", + "Colour", + "From", + } +} + +func categoryHeaderRecord(showLabels bool) []string { + var fields []string + + fields = append(fields, "Name") + fields = append(fields, "Description") + fields = append(fields, "URL") + + if showLabels { + fields = append(fields, "Labels") + } + + return fields +} + +func categoryToRecord(lf *LabelsFile, c Category, showLabels, quote bool) ([]string, error) { + var record []string + + name := c.Name + + if quote { + name = fmt.Sprintf("`%s`", c.Name) + } + + record = append(record, name) + record = append(record, c.Description) + record = append(record, c.URL) + + if showLabels { + var labelNames []string + + labels, err := getLabelsByCategory(c.Name, lf) + if err != nil { + return nil, err + } + + for _, l := range labels { + labelName := l.Name + + if quote { + labelName = fmt.Sprintf("`%s`", l.Name) + } + + labelNames = append(labelNames, labelName) + } + + result := strings.Join(labelNames, labelNamesSeparator) + + record = append(record, result) + } + + return record, nil +} diff --git a/tests/cmd/github-labels/types.go b/tests/cmd/github-labels/types.go new file mode 100644 index 0000000000..9248f8c68a --- /dev/null +++ b/tests/cmd/github-labels/types.go @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +type Category struct { + Name string + Description string + URL string `yaml:",omitempty"` +} + +type Label struct { + Name string + Description string + CategoryName string `yaml:"category"` + Colour string `yaml:"color"` + From string `yaml:",omitempty"` +} + +type Categories []Category + +func (c Categories) Len() int { + return len(c) +} + +func (c Categories) Swap(i, j int) { + c[i], c[j] = c[j], c[i] +} + +func (c Categories) Less(i, j int) bool { + return c[i].Name < c[j].Name +} + +type Labels []Label + +func (l Labels) Len() int { + return len(l) +} + +func (l Labels) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +func (l Labels) Less(i, j int) bool { + return l[i].Name < l[j].Name +} + +type LabelsFile struct { + Description string + Categories Categories + Repo string + Labels Labels +} diff --git a/tests/cmd/github-labels/utils.go b/tests/cmd/github-labels/utils.go new file mode 100644 index 0000000000..4490d3a0df --- /dev/null +++ b/tests/cmd/github-labels/utils.go @@ -0,0 +1,24 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import "errors" + +func getLabelsByCategory(categoryName string, lf *LabelsFile) ([]Label, error) { + var labels []Label + + if categoryName == "" { + return nil, errors.New("need category name") + } + + for _, label := range lf.Labels { + if label.CategoryName == categoryName { + labels = append(labels, label) + } + } + + return labels, nil +} diff --git a/tests/cmd/github-labels/yaml.go b/tests/cmd/github-labels/yaml.go new file mode 100644 index 0000000000..d1dfb6aff6 --- /dev/null +++ b/tests/cmd/github-labels/yaml.go @@ -0,0 +1,72 @@ +// Copyright (c) 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +package main + +import ( + "fmt" + "os" + "sort" + + yaml "gopkg.in/yaml.v2" +) + +const fileMode os.FileMode = 0600 + +func readYAML(file string) (*LabelsFile, error) { + bytes, err := os.ReadFile(file) + if err != nil { + return nil, err + } + + lf := LabelsFile{} + + err = yaml.Unmarshal(bytes, &lf) + if err != nil { + return nil, err + } + + sort.Sort(lf.Labels) + sort.Sort(lf.Categories) + + clean(&lf) + + err = check(&lf) + if err != nil { + return nil, fmt.Errorf("file was not in expected format: %v", err) + } + + return &lf, nil +} + +func writeYAML(lf *LabelsFile, file string) error { + bytes, err := yaml.Marshal(lf) + if err != nil { + return err + } + + return os.WriteFile(file, bytes, fileMode) +} + +func checkYAML(file string) error { + // read and check + _, err := readYAML(file) + + if err == nil { + fmt.Printf("Checked file %v\n", file) + } + + return err +} + +func sortYAML(fromFile, toFile string) error { + // read and sort + lf, err := readYAML(fromFile) + if err != nil { + return err + } + + return writeYAML(lf, toFile) +}