From 53a084522b6928111ea79a4d17e906b7c0862ecd Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 2 Nov 2023 14:29:55 -0700 Subject: [PATCH 1/9] Added project structure and documentation --- toolbox/README.md | 41 +++++++++++++++++++++++++++++++++++++++++ toolbox/go.mod | 3 +++ toolbox/magefile.go | 4 ++++ 3 files changed, 48 insertions(+) create mode 100644 toolbox/README.md create mode 100644 toolbox/go.mod create mode 100644 toolbox/magefile.go diff --git a/toolbox/README.md b/toolbox/README.md new file mode 100644 index 0000000..a367f7c --- /dev/null +++ b/toolbox/README.md @@ -0,0 +1,41 @@ +# Toolbox Organization + +## Structure + +The AIO toolbox is a collection of several tools for customers of AIO. To keep the project understandable and extensible to future additions, the following conventions should be followed for repository structure. + +``` +├── lib +│ ├── library1 +│ ├── ... +│ ├── libraryN +├── tools +│ ├── tool1 +│ │ ├── cmd +│ │ │ ├── main.go +│ │ ├── pkg +│ │ │ ├── pkg1 +│ ├── ... +│ ├── toolN +├── docker +│ ├── tool1 +│ │ ├── Dockerfile +│ ├── ... +│ ├── toolN +├── go.mod +├── go.sum +├── magefile.go +└── README.md +``` + +__Libraries__ which are shared between multiple tools should be stored in the lib directory. This library can be incorporated into projects beyond AIO tools and such documentation will be available on the _go.dev_ documentation site. + +__Tools__ are AIO specific tools which may have their own internal packages stored within the pkg folder. Other top-level folders in each tool are allowed, though it is recommended to minimize the number of top-level folders. Each tool must also have its own cmd directory where the entrypoint is located. + +__Dockerfiles__ for relavent docker images are stored for each tool under the docker directory. They are siloed into their own directories for each tool. + +__Mage Commands__ are located within the magefile.go. Mage commands should be written in such a way that they apply to any given tool based on a parameter. Mage commands should not be targeted at a specific tool itself. If such a command is required, a magefile within the tool directory should be produced. + +## Linting & Formatting Requirements + +Linting and formatting rules are applied to all tools and libraries based on a linting configuration set up and applied via mage commands. \ No newline at end of file diff --git a/toolbox/go.mod b/toolbox/go.mod new file mode 100644 index 0000000..1a949b1 --- /dev/null +++ b/toolbox/go.mod @@ -0,0 +1,3 @@ +module github.com/Azure-Samples/aio-dev-toolbox/toolbox + +go 1.21.3 diff --git a/toolbox/magefile.go b/toolbox/magefile.go new file mode 100644 index 0000000..5b16e6c --- /dev/null +++ b/toolbox/magefile.go @@ -0,0 +1,4 @@ +//go:build mage +// +build mage + +package main \ No newline at end of file From c38e1627e776e7895229d8d3eac70778c4848adb Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 2 Nov 2023 14:39:53 -0700 Subject: [PATCH 2/9] Add example tool --- toolbox/README.md | 6 +++++- toolbox/tools/example/cmd/main.go | 10 ++++++++++ toolbox/tools/example/pkg/example/example.go | 11 +++++++++++ toolbox/tools/example/pkg/example/example_test.go | 8 ++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 toolbox/tools/example/cmd/main.go create mode 100644 toolbox/tools/example/pkg/example/example.go create mode 100644 toolbox/tools/example/pkg/example/example_test.go diff --git a/toolbox/README.md b/toolbox/README.md index a367f7c..54f83b3 100644 --- a/toolbox/README.md +++ b/toolbox/README.md @@ -38,4 +38,8 @@ __Mage Commands__ are located within the magefile.go. Mage commands should be wr ## Linting & Formatting Requirements -Linting and formatting rules are applied to all tools and libraries based on a linting configuration set up and applied via mage commands. \ No newline at end of file +Linting and formatting rules are applied to all tools and libraries based on a linting configuration set up and applied via mage commands. + +## Example Tool + +Please view the example tool in `tools/example` for an example of how to contribute a new tool to the AIO toolbox. \ No newline at end of file diff --git a/toolbox/tools/example/cmd/main.go b/toolbox/tools/example/cmd/main.go new file mode 100644 index 0000000..99d1b49 --- /dev/null +++ b/toolbox/tools/example/cmd/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/Azure-Samples/aio-dev-toolbox/toolbox/tools/example/pkg/example" +) + +func main() { + example := &example.Example{} + example.Print() +} diff --git a/toolbox/tools/example/pkg/example/example.go b/toolbox/tools/example/pkg/example/example.go new file mode 100644 index 0000000..dbc4fd3 --- /dev/null +++ b/toolbox/tools/example/pkg/example/example.go @@ -0,0 +1,11 @@ +package example + +import "fmt" + +// Example shows that all exported symbols must have a comment like this. +type Example struct {} + +// Print shows that the exported symbol comments applies to functions as well. +func (*Example) Print() { + fmt.Println("Example") +} \ No newline at end of file diff --git a/toolbox/tools/example/pkg/example/example_test.go b/toolbox/tools/example/pkg/example/example_test.go new file mode 100644 index 0000000..e62fd50 --- /dev/null +++ b/toolbox/tools/example/pkg/example/example_test.go @@ -0,0 +1,8 @@ +package example + +import "testing" + +// Every package must have a test file and must meet a minimum test coverage to be merged into the toolbox. +func TestMain(m *testing.M) { + m.Run() +} From b018f90e485661fb87ba0f8be16c9484bbf339d4 Mon Sep 17 00:00:00 2001 From: Ethan Date: Thu, 2 Nov 2023 14:58:24 -0700 Subject: [PATCH 3/9] Initialize magefile --- README.md | 10 +- toolbox/.gitignore | 3 + toolbox/go.mod | 15 +++ toolbox/go.sum | 36 ++++++++ toolbox/mageerrors.go | 45 +++++++++ toolbox/magefile.go | 206 +++++++++++++++++++++++++++++++++++++++++- 6 files changed, 309 insertions(+), 6 deletions(-) create mode 100644 toolbox/.gitignore create mode 100644 toolbox/go.sum create mode 100644 toolbox/mageerrors.go diff --git a/README.md b/README.md index 364f052..a1a307f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# Project Name +# Azure IoT Operations Dev Toolbox -(short, 1-3 sentenced, description of the project) +The AIO dev toolbox is the source of tools, samples, and other resources for customers of AIO. It features MQTT simulation tools, ## Features This project framework provides the following features: -* Feature 1 -* Feature 2 -* ... +* Krill MQTT Data Simulator +* HTTP Callout Server +* GRPC Callout Server ## Getting Started diff --git a/toolbox/.gitignore b/toolbox/.gitignore new file mode 100644 index 0000000..17bcdbd --- /dev/null +++ b/toolbox/.gitignore @@ -0,0 +1,3 @@ +bin +cover.tmp.out +coverage.out \ No newline at end of file diff --git a/toolbox/go.mod b/toolbox/go.mod index 1a949b1..31b40a9 100644 --- a/toolbox/go.mod +++ b/toolbox/go.mod @@ -1,3 +1,18 @@ module github.com/Azure-Samples/aio-dev-toolbox/toolbox go 1.21.3 + +require ( + github.com/magefile/mage v1.15.0 + github.com/princjef/mageutil v1.0.0 +) + +require ( + github.com/VividCortex/ewma v1.1.1 // indirect + github.com/cheggaaa/pb/v3 v3.0.4 // indirect + github.com/fatih/color v1.9.0 // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-runewidth v0.0.7 // indirect + golang.org/x/sys v0.0.0-20200116001909-b77594299b42 // indirect +) diff --git a/toolbox/go.sum b/toolbox/go.sum new file mode 100644 index 0000000..dca4d73 --- /dev/null +++ b/toolbox/go.sum @@ -0,0 +1,36 @@ +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/cheggaaa/pb v2.0.7+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= +github.com/cheggaaa/pb/v3 v3.0.4 h1:QZEPYOj2ix6d5oEg63fbHmpolrnNiwjUsk+h74Yt4bM= +github.com/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= +github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= +github.com/matryer/is v1.3.0 h1:9qiso3jaJrOe6qBRJRBt2Ldht05qDiFP9le0JOIhRSI= +github.com/matryer/is v1.3.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/princjef/mageutil v1.0.0 h1:1OfZcJUMsooPqieOz2ooLjI+uHUo618pdaJsbCXcFjQ= +github.com/princjef/mageutil v1.0.0/go.mod h1:mkShhaUomCYfAoVvTKRcbAs8YSVPdtezI5j6K+VXhrs= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710= +gopkg.in/cheggaaa/pb.v2 v2.0.7/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4= +gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0= +gopkg.in/mattn/go-colorable.v0 v0.1.0/go.mod h1:BVJlBXzARQxdi3nZo6f6bnl5yR20/tOL6p+V0KejgSY= +gopkg.in/mattn/go-isatty.v0 v0.0.4/go.mod h1:wt691ab7g0X4ilKZNmMII3egK0bTxl37fEn/Fwbd8gc= +gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ= diff --git a/toolbox/mageerrors.go b/toolbox/mageerrors.go new file mode 100644 index 0000000..8815adb --- /dev/null +++ b/toolbox/mageerrors.go @@ -0,0 +1,45 @@ +package main + +import "fmt" + +// PackageMissingTestFileError is an error describing a package which is missing a test file. +type PackageMissingTestFileError struct { + name string +} + +func (err *PackageMissingTestFileError) Error() string { + return fmt.Sprintf( + "package %q is missing a test file and is not listed in the packages excluded from requiring a test file", + err.name, + ) +} + +type Coverage struct { + path string + name string + percentage float64 + expected float64 +} + +func (coverage *Coverage) Error() string { + return fmt.Sprintf( + "block %q in file %q has an inadequate unit test coverage percentage of %0.2f%% where %0.2f%% was expected", + coverage.name, + coverage.path, + coverage.percentage, + coverage.expected, + ) +} + +type InadequateOverallCoverageError struct { + percentage float64 + expected float64 +} + +func (err *InadequateOverallCoverageError) Error() string { + return fmt.Sprintf( + "inadequate overall unit test coverage percentage of %0.2f%% where %0.2f%% was expected", + err.percentage, + err.expected, + ) +} diff --git a/toolbox/magefile.go b/toolbox/magefile.go index 5b16e6c..08de73d 100644 --- a/toolbox/magefile.go +++ b/toolbox/magefile.go @@ -1,4 +1,208 @@ //go:build mage // +build mage -package main \ No newline at end of file +package main + +import ( + "encoding/json" + "fmt" + "io" + "strings" + + "github.com/magefile/mage/sh" + "github.com/princjef/mageutil/bintool" +) + +var ( + linter = bintool.Must(bintool.New( + "golangci-lint{{.BinExt}}", + "1.51.1", + "https://github.com/golangci/golangci-lint/releases/download/v{{.Version}}/golangci-lint-{{.Version}}-{{.GOOS}}-{{.GOARCH}}{{.ArchiveExt}}", + )) + golines = bintool.Must(bintool.NewGo( + "github.com/segmentio/golines", + "v0.11.0", + )) + documenter = bintool.Must(bintool.New( + "gomarkdoc{{.BinExt}}", + "0.4.1", + "https://github.com/princjef/gomarkdoc/releases/download/v{{.Version}}/gomarkdoc_{{.Version}}_{{.GOOS}}_{{.GOARCH}}{{.ArchiveExt}}", + )) + releaser = bintool.Must(bintool.NewGo( + "github.com/goreleaser/goreleaser", + "v1.18.2", + )) +) + +const ( + // UnitTestTimeoutMs specifies the maximum amount of time a unit test will be given before it is considered failed. + UnitTestTimeoutMs = 3000 + + // ExpectedBlockCoverage describes the minimum expected test coverage of each code block. + ExpectedBlockCoverage = 0.00 + + // ExpectedOverallCoverage describes the minimum expected test coverage of the overall codebase. + ExpectedOverallCoverage = 85.00 +) + +// Clean clears the testing cache such that all tests are fully run again. +// Cleaning the test cache is recommended to avoid letting flaky tests into the toolbox. +func Clean() error { + return sh.RunV("go", "clean", "-testcache") +} + +// Cover runs tests and generates coverage profiles for all tests. +// The tests run in atomic mode and check for race conditions. +func Cover() error { + err := Clean() + if err != nil { + return err + } + + err = sh.RunV( + "go", + "test", + "-timeout", + fmt.Sprintf("%dms", UnitTestTimeoutMs), + "-cover", + "--coverprofile=cover.tmp.out", + "-covermode=atomic", + "-race", + "./...", + ) + if err != nil { + return err + } + + err = sh.RunV("bash", "-c", `cat cover.tmp.out | grep -v "pb.go" > coverage.out`) + if err != nil { + return err + } + + return sh.RunV("go", "tool", "cover", "-func=coverage.out") +} + + +// Package is a subset of the structure returned by the go list tool. +type Package struct { + TestGoFiles []string `json:"TestGoFiles"` + XTestGoFiles []string `json:"XTestGoFiles"` + ImportPath string `json:"ImportPath"` +} + +// ImportPathRoot is the root import for all packages in this project. +const ImportPathRoot = "dev.azure.com/msazure/One/_git/Digital-Operations-Experience/service/" + +// TestPackageExclusions is a set of packages which are excluded from the check for at least one test file. +// This should only include the main package and any packages which only define types or constants. +var TestPackageExclusions = map[string]any{} + +// EnsureTests ensures that every package besides those excluded via the "TestPackageExclusions" variable defined above must contain at least one test file. +// This ensures that coverage will be measured for all packages and forces the creation of test files for all new packages. +func EnsureTests() error { + res, err := sh.Output("go", "list", "-json", "./...") + if err != nil { + return err + } + + dec := json.NewDecoder(strings.NewReader(res)) + + var packages []Package + for { + var pack Package + err := dec.Decode(&pack) + if err == io.EOF { + break + } + if err != nil { + return err + } + packages = append(packages, pack) + } + + for _, pack := range packages { + if _, ok := TestPackageExclusions[strings.TrimPrefix(pack.ImportPath, ImportPathRoot)]; ok { + continue + } + if len(pack.XTestGoFiles) < 1 && len(pack.TestGoFiles) < 1 { + return &PackageMissingTestFileError{ + name: pack.ImportPath, + } + } + } + + return nil +} + +// Bench will run all benchmarking tests in the codebase, if any are present. +func Bench() error { + return sh.RunV("go", "test", "-bench=.", "-benchmem", "./...") +} + +// EvaluateCoverage takes a coverage file and evaluates the coverage of code block and overall app unit test coverage. +// If the coverage of any given block or the overall coverage of the application does not meet the above +// thresholds, an error will be returned. Otherwise a message describing coverage will be reported. +func EvaluateCoverage() error { + res, err := sh.Output( + "go", + "tool", + "cover", + "-func=coverage.out", + ) + if err != nil { + return err + } + + lines := strings.Split(res, "\n") + + totalCoverage := &Coverage{} + coverages := make([]*Coverage, len(lines)-1) + + for idx, line := range lines { + components := strings.Fields(line) + if len(components) != 3 { + return fmt.Errorf( + "invalid number of fields in coverage profile, please regenerate profile", + ) + } + percentage, err := strconv.ParseFloat( + strings.Trim(components[2], "%"), + 64, + ) + if err != nil { + return err + } + + if idx == len(lines)-1 { + totalCoverage.percentage = percentage + break + } + + coverages[idx] = &Coverage{ + path: components[0], + name: components[1], + percentage: percentage, + expected: ExpectedBlockCoverage, + } + } + + for _, coverage := range coverages { + if coverage.percentage < ExpectedBlockCoverage { + return coverage + } + } + + if totalCoverage.percentage < ExpectedOverallCoverage { + return &InadequateOverallCoverageError{ + percentage: totalCoverage.percentage, + expected: ExpectedOverallCoverage, + } + } + + fmt.Printf( + "measured overall unit test coverage of %0.2f%%\n", + totalCoverage.percentage, + ) + + return nil +} \ No newline at end of file From 2213fe98f95cc4b81fca626196b4817547e77d2b Mon Sep 17 00:00:00 2001 From: ethanperry1 <107274017+ethanperry1@users.noreply.github.com> Date: Fri, 3 Nov 2023 10:39:33 -0700 Subject: [PATCH 4/9] Create docker_build.yml --- .github/workflows/docker_build.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/workflows/docker_build.yml diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml new file mode 100644 index 0000000..3e47461 --- /dev/null +++ b/.github/workflows/docker_build.yml @@ -0,0 +1,4 @@ +name: docker_build + +on: + workflow_dispatch: From f60c76184c6ecce07b8b4819cfa051b71ab7696a Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 3 Nov 2023 10:55:10 -0700 Subject: [PATCH 5/9] Restructuring for project, modified github action --- .github/workflows/docker_build.yml | 14 ++ .gitignore | 4 + docker/example/Dockerfile | 9 ++ {toolbox => docs}/README.md | 0 toolbox/go.mod => go.mod | 0 toolbox/go.sum => go.sum | 0 toolbox/mageerrors.go => mageerrors.go | 0 toolbox/magefile.go => magefile.go | 136 +++++++++++++++++- toolbox/.gitignore | 3 - {toolbox/tools => tools}/example/cmd/main.go | 0 .../example/pkg/example/example.go | 0 .../example/pkg/example/example_test.go | 0 12 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 docker/example/Dockerfile rename {toolbox => docs}/README.md (100%) rename toolbox/go.mod => go.mod (100%) rename toolbox/go.sum => go.sum (100%) rename toolbox/mageerrors.go => mageerrors.go (100%) rename toolbox/magefile.go => magefile.go (70%) delete mode 100644 toolbox/.gitignore rename {toolbox/tools => tools}/example/cmd/main.go (100%) rename {toolbox/tools => tools}/example/pkg/example/example.go (100%) rename {toolbox/tools => tools}/example/pkg/example/example_test.go (100%) diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index 3e47461..80abf8e 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -2,3 +2,17 @@ name: docker_build on: workflow_dispatch: + push: + branches: + - release/* + +jobs: + docker_build: + name: 'Build docker containers' + runs-on: ubuntu-latest + environment: production + + defaults: + run: + shell: bash + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8a30d25..7bb8afa 100644 --- a/.gitignore +++ b/.gitignore @@ -396,3 +396,7 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml + +bin +cover.tmp.out +coverage.out \ No newline at end of file diff --git a/docker/example/Dockerfile b/docker/example/Dockerfile new file mode 100644 index 0000000..05e26a0 --- /dev/null +++ b/docker/example/Dockerfile @@ -0,0 +1,9 @@ +# syntax=docker/dockerfile:1 + +FROM mcr.microsoft.com/oss/go/microsoft/golang:1.21-cbl-mariner2.0 + +COPY go.mod /tool/go.mod +COPY go.sum /tool/go.sum +COPY lib /tool/lib +COPY tools/example /tool/example + diff --git a/toolbox/README.md b/docs/README.md similarity index 100% rename from toolbox/README.md rename to docs/README.md diff --git a/toolbox/go.mod b/go.mod similarity index 100% rename from toolbox/go.mod rename to go.mod diff --git a/toolbox/go.sum b/go.sum similarity index 100% rename from toolbox/go.sum rename to go.sum diff --git a/toolbox/mageerrors.go b/mageerrors.go similarity index 100% rename from toolbox/mageerrors.go rename to mageerrors.go diff --git a/toolbox/magefile.go b/magefile.go similarity index 70% rename from toolbox/magefile.go rename to magefile.go index 08de73d..833f12f 100644 --- a/toolbox/magefile.go +++ b/magefile.go @@ -34,6 +34,22 @@ var ( )) ) +const ( + KRILL project = "krill" +) + +var projects = []project{ + KRILL, +} + +var buildProjectPathMap map[project]string = map[project]string{ + KRILL: "./cmd/krill", +} + +var releasePathMap map[project]string = map[project]string{ + KRILL: "./releases/krill/.goreleaser.yaml", +} + const ( // UnitTestTimeoutMs specifies the maximum amount of time a unit test will be given before it is considered failed. UnitTestTimeoutMs = 3000 @@ -45,6 +61,69 @@ const ( ExpectedOverallCoverage = 85.00 ) +func ensureFormatter() error { + return golines.Ensure() +} + +func ensureLinter() error { + return linter.Ensure() +} + +func ensureDocumenter() error { + return documenter.Ensure() +} + +func ensureReleaser() error { + return releaser.Ensure() +} + +func EnsureAllTools() error { + if err := ensureFormatter(); err != nil { + return err + } + + if err := ensureLinter(); err != nil { + return err + } + + if err := ensureDocumenter(); err != nil { + return err + } + + if err := ensureReleaser(); err != nil { + return err + } + + return nil +} + +func Format() error { + if err := ensureFormatter(); err != nil { + return err + } + + return golines.Command("-m 80 --no-reformat-tags --base-formatter gofmt -w ."). + Run() +} + +func Lint() error { + if err := ensureLinter(); err != nil { + return err + } + + return linter.Command("run").Run() +} + +func Doc() error { + if err := ensureDocumenter(); err != nil { + return err + } + + return shellcmd.RunAll( + documenter.Command("./lib/..."), + ) +} + // Clean clears the testing cache such that all tests are fully run again. // Cleaning the test cache is recommended to avoid letting flaky tests into the toolbox. func Clean() error { @@ -205,4 +284,59 @@ func EvaluateCoverage() error { ) return nil -} \ No newline at end of file +} + +func Build(proj string) error { + + path, ok := buildProjectPathMap[project(proj)] + if !ok { + return fmt.Errorf("invalid project name, must be one of the following: %v", projects) + } + + err := EnsureAllTools() + if err != nil { + return err + } + + err = Cover() + if err != nil { + return err + } + + return sh.RunV("go", "build", "-o", proj, path) +} + +func Release(proj string, version string, message string) error { + + path, ok := releasePathMap[project(proj)] + if !ok { + return fmt.Errorf("invalid project name, must be one of the following: %v", projects) + } + + err := EnsureAllTools() + if err != nil { + return err + } + + err = Cover() + if err != nil { + return err + } + + err = sh.RunV("goreleaser", "check", path) + if err != nil { + return err + } + + err = sh.RunV("git", "tag", "-a", version, "-m", message) + if err != nil { + return err + } + + err = sh.RunV("git", "push", "origin", version) + if err != nil { + return err + } + + return sh.RunV("goreleaser", "release", "--clean", "-f", path) +} diff --git a/toolbox/.gitignore b/toolbox/.gitignore deleted file mode 100644 index 17bcdbd..0000000 --- a/toolbox/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -bin -cover.tmp.out -coverage.out \ No newline at end of file diff --git a/toolbox/tools/example/cmd/main.go b/tools/example/cmd/main.go similarity index 100% rename from toolbox/tools/example/cmd/main.go rename to tools/example/cmd/main.go diff --git a/toolbox/tools/example/pkg/example/example.go b/tools/example/pkg/example/example.go similarity index 100% rename from toolbox/tools/example/pkg/example/example.go rename to tools/example/pkg/example/example.go diff --git a/toolbox/tools/example/pkg/example/example_test.go b/tools/example/pkg/example/example_test.go similarity index 100% rename from toolbox/tools/example/pkg/example/example_test.go rename to tools/example/pkg/example/example_test.go From ddd623411f83f691ba49adaa84c46b13db07ab31 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 3 Nov 2023 10:56:04 -0700 Subject: [PATCH 6/9] Rename organization document --- docs/{README.md => ORGANIZATION.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/{README.md => ORGANIZATION.md} (100%) diff --git a/docs/README.md b/docs/ORGANIZATION.md similarity index 100% rename from docs/README.md rename to docs/ORGANIZATION.md From dcc51029b8b5ad3f54a7d55ec05edc0853c9d71c Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 3 Nov 2023 11:02:03 -0700 Subject: [PATCH 7/9] Add example library and samples --- docs/ORGANIZATION.md | 8 ++++++++ lib/example/example.go | 11 +++++++++++ lib/example/example_test.go | 8 ++++++++ samples/example /config.yaml | 2 ++ tools/example/pkg/example/example.go | 2 +- 5 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 lib/example/example.go create mode 100644 lib/example/example_test.go create mode 100644 samples/example /config.yaml diff --git a/docs/ORGANIZATION.md b/docs/ORGANIZATION.md index 54f83b3..58c067a 100644 --- a/docs/ORGANIZATION.md +++ b/docs/ORGANIZATION.md @@ -9,6 +9,12 @@ The AIO toolbox is a collection of several tools for customers of AIO. To keep t │ ├── library1 │ ├── ... │ ├── libraryN +├── samples +│ ├── tool1 +│ │ ├── config.yml +│ │ ├── config.json +│ ├── ... +│ ├── toolN ├── tools │ ├── tool1 │ │ ├── cmd @@ -30,6 +36,8 @@ The AIO toolbox is a collection of several tools for customers of AIO. To keep t __Libraries__ which are shared between multiple tools should be stored in the lib directory. This library can be incorporated into projects beyond AIO tools and such documentation will be available on the _go.dev_ documentation site. +__Samples__ are configuration files which have been created for specific sample usages of a tool. Multiple configuration files can live in each sample folder for different samples which utilize the same tool. + __Tools__ are AIO specific tools which may have their own internal packages stored within the pkg folder. Other top-level folders in each tool are allowed, though it is recommended to minimize the number of top-level folders. Each tool must also have its own cmd directory where the entrypoint is located. __Dockerfiles__ for relavent docker images are stored for each tool under the docker directory. They are siloed into their own directories for each tool. diff --git a/lib/example/example.go b/lib/example/example.go new file mode 100644 index 0000000..6c31d3e --- /dev/null +++ b/lib/example/example.go @@ -0,0 +1,11 @@ +package example + +import "fmt" + +// Example shows that all exported symbols must have a comment like this. +type Example struct {} + +// Print shows that the exported symbol comments applies to functions as well. +func (*Example) Print() { + fmt.Println("Example library") +} \ No newline at end of file diff --git a/lib/example/example_test.go b/lib/example/example_test.go new file mode 100644 index 0000000..109a6a8 --- /dev/null +++ b/lib/example/example_test.go @@ -0,0 +1,8 @@ +package example + +import "testing" + +// Every library must have a test file and must meet a minimum test coverage to be merged into the toolbox. +func TestMain(m *testing.M) { + m.Run() +} diff --git a/samples/example /config.yaml b/samples/example /config.yaml new file mode 100644 index 0000000..afb8cf7 --- /dev/null +++ b/samples/example /config.yaml @@ -0,0 +1,2 @@ +# This is an example configuration which would be used with the example project. +# Multiple configuration files can live within the samples/example directory. \ No newline at end of file diff --git a/tools/example/pkg/example/example.go b/tools/example/pkg/example/example.go index dbc4fd3..b81f9a3 100644 --- a/tools/example/pkg/example/example.go +++ b/tools/example/pkg/example/example.go @@ -7,5 +7,5 @@ type Example struct {} // Print shows that the exported symbol comments applies to functions as well. func (*Example) Print() { - fmt.Println("Example") + fmt.Println("Example package") } \ No newline at end of file From 34dc013397ca3a2bb53da630f0d0a9d740924c8f Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 3 Nov 2023 11:04:14 -0700 Subject: [PATCH 8/9] Add note to organization document --- docs/ORGANIZATION.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/ORGANIZATION.md b/docs/ORGANIZATION.md index 58c067a..f0d8d63 100644 --- a/docs/ORGANIZATION.md +++ b/docs/ORGANIZATION.md @@ -50,4 +50,6 @@ Linting and formatting rules are applied to all tools and libraries based on a l ## Example Tool -Please view the example tool in `tools/example` for an example of how to contribute a new tool to the AIO toolbox. \ No newline at end of file +Please view the example tool in `tools/example` for an example of how to contribute a new tool to the AIO toolbox. + +_Note_: tools can be written in any language including minimal tools such as bash scripts. The tools are written in golang in this document for example purposes. \ No newline at end of file From 089cd6010128b1fae7d1be75ef87904a81c59483 Mon Sep 17 00:00:00 2001 From: Ethan Date: Fri, 3 Nov 2023 11:06:40 -0700 Subject: [PATCH 9/9] Fix main readme --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a1a307f..1132256 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,14 @@ # Azure IoT Operations Dev Toolbox -The AIO dev toolbox is the source of tools, samples, and other resources for customers of AIO. It features MQTT simulation tools, +The AIO dev toolbox is the source of tools, samples, and other resources for customers of AIO. + +## Contributing + +Please view the developer guides in the docs directory to get started with contributions. Get started with the [Organization docs](./docs/ORGANIZATION.md). ## Features -This project framework provides the following features: +This project provides the following tools: * Krill MQTT Data Simulator * HTTP Callout Server