1
0
mirror of https://github.com/siderolabs/kres.git synced 2026-02-05 09:45:35 +01:00

feat: support nested Go projects

Generate boilerplate for:

- unit-tests.
- linters.
- codecoverage.
- build executables from nested projects.

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
This commit is contained in:
Artem Chernyshev
2023-05-18 22:46:39 +03:00
parent 5240ae144c
commit 33bdccc45c
25 changed files with 491 additions and 257 deletions

View File

@@ -1,7 +1,7 @@
---
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2023-03-01T19:46:03Z by kres 636983d-dirty.
# Generated on 2023-05-18T19:10:42Z by kres 5240ae1-dirty.
kind: pipeline
type: kubernetes

View File

@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2023-02-02T11:37:12Z by kres 5686454-dirty.
# Generated on 2023-05-18T16:29:23Z by kres 5240ae1-dirty.
# options for analysis running
run:
@@ -36,7 +36,7 @@ linters-settings:
lines: 60
statements: 40
gci:
local-prefixes: github.com/siderolabs/kres
local-prefixes: github.com/siderolabs/kres/
gocognit:
min-complexity: 30
ireturn:
@@ -65,7 +65,7 @@ linters-settings:
gofmt:
simplify: true
goimports:
local-prefixes: github.com/siderolabs/kres
local-prefixes: github.com/siderolabs/kres/
golint:
min-confidence: 0.8
gomnd:

View File

@@ -2,7 +2,7 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2023-05-17T06:04:50Z by kres 0f0b8c2.
# Generated on 2023-05-19T11:21:06Z by kres 05a5352-dirty.
ARG TOOLCHAIN
@@ -32,26 +32,27 @@ ENV GO111MODULE on
ARG CGO_ENABLED
ENV CGO_ENABLED ${CGO_ENABLED}
ENV GOPATH /go
ARG DEEPCOPY_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/siderolabs/deep-copy@${DEEPCOPY_VERSION} \
&& mv /go/bin/deep-copy /bin/deep-copy
ARG GOLANGCILINT_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCILINT_VERSION} \
&& mv /go/bin/golangci-lint /bin/golangci-lint
ARG GOFUMPT_VERSION
RUN go install mvdan.cc/gofumpt@${GOFUMPT_VERSION} \
&& mv /go/bin/gofumpt /bin/gofumpt
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/vuln/cmd/govulncheck@latest \
&& mv /go/bin/govulncheck /bin/govulncheck
ARG GOIMPORTS_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/tools/cmd/goimports@${GOIMPORTS_VERSION} \
&& mv /go/bin/goimports /bin/goimports
ARG DEEPCOPY_VERSION
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/siderolabs/deep-copy@${DEEPCOPY_VERSION} \
&& mv /go/bin/deep-copy /bin/deep-copy
ARG GOFUMPT_VERSION
RUN go install mvdan.cc/gofumpt@${GOFUMPT_VERSION} \
&& mv /go/bin/gofumpt /bin/gofumpt
# tools and sources
FROM tools AS base
WORKDIR /src
COPY ./go.mod .
COPY ./go.sum .
COPY go.mod go.mod
COPY go.sum go.sum
RUN cd .
RUN --mount=type=cache,target=/go/pkg go mod download
RUN --mount=type=cache,target=/go/pkg go mod verify
COPY ./cmd ./cmd
@@ -75,25 +76,29 @@ RUN FILES="$(gofumpt -l .)" && test -z "${FILES}" || (echo -e "Source code is no
# runs goimports
FROM base AS lint-goimports
RUN FILES="$(goimports -l -local github.com/siderolabs/kres .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'goimports -w -local github.com/siderolabs/kres .':\n${FILES}"; exit 1)
RUN FILES="$(goimports -l -local github.com/siderolabs/kres/ .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'goimports -w -local github.com/siderolabs/kres/ .':\n${FILES}"; exit 1)
# runs golangci-lint
FROM base AS lint-golangci-lint
WORKDIR /src
COPY .golangci.yml .
ENV GOGC 50
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/root/.cache/golangci-lint --mount=type=cache,target=/go/pkg golangci-lint run --config .golangci.yml
# runs govulncheck
FROM base AS lint-govulncheck
WORKDIR /src
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg govulncheck ./...
# runs unit-tests with race detector
FROM base AS unit-tests-race
WORKDIR /src
ARG TESTPKGS
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg --mount=type=cache,target=/tmp CGO_ENABLED=1 go test -v -race -count 1 ${TESTPKGS}
# runs unit-tests
FROM base AS unit-tests-run
WORKDIR /src
ARG TESTPKGS
RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg --mount=type=cache,target=/tmp go test -v -covermode=atomic -coverprofile=coverage.txt -coverpkg=${TESTPKGS} -count 1 ${TESTPKGS}
@@ -101,7 +106,7 @@ FROM scratch AS kres-linux-amd64
COPY --from=kres-linux-amd64-build /kres-linux-amd64 /kres-linux-amd64
FROM scratch AS unit-tests
COPY --from=unit-tests-run /src/coverage.txt /coverage.txt
COPY --from=unit-tests-run /src/coverage.txt /coverage-unit-tests.txt
FROM kres-linux-${TARGETARCH} AS kres

View File

@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2023-05-11T04:45:18Z by kres 00c9f23.
# Generated on 2023-05-19T11:21:06Z by kres 05a5352-dirty.
# common variables
@@ -13,15 +13,15 @@ WITH_RACE ?= false
REGISTRY ?= ghcr.io
USERNAME ?= siderolabs
REGISTRY_AND_USERNAME ?= $(REGISTRY)/$(USERNAME)
GOLANGCILINT_VERSION ?= v1.52.2
GOFUMPT_VERSION ?= v0.5.0
GO_VERSION ?= 1.20
GOIMPORTS_VERSION ?= v0.9.1
PROTOBUF_GO_VERSION ?= 1.28.1
GRPC_GO_VERSION ?= 1.3.0
GRPC_GATEWAY_VERSION ?= 2.15.2
VTPROTOBUF_VERSION ?= 0.4.0
DEEPCOPY_VERSION ?= v0.5.5
GOLANGCILINT_VERSION ?= v1.52.2
GOFUMPT_VERSION ?= v0.5.0
GO_VERSION ?= 1.20
GOIMPORTS_VERSION ?= v0.9.1
GO_BUILDFLAGS ?=
GO_LDFLAGS ?=
CGO_ENABLED ?= 0
@@ -49,14 +49,14 @@ COMMON_ARGS += --build-arg=TOOLCHAIN="$(TOOLCHAIN)"
COMMON_ARGS += --build-arg=CGO_ENABLED="$(CGO_ENABLED)"
COMMON_ARGS += --build-arg=GO_BUILDFLAGS="$(GO_BUILDFLAGS)"
COMMON_ARGS += --build-arg=GO_LDFLAGS="$(GO_LDFLAGS)"
COMMON_ARGS += --build-arg=GOLANGCILINT_VERSION="$(GOLANGCILINT_VERSION)"
COMMON_ARGS += --build-arg=GOFUMPT_VERSION="$(GOFUMPT_VERSION)"
COMMON_ARGS += --build-arg=GOIMPORTS_VERSION="$(GOIMPORTS_VERSION)"
COMMON_ARGS += --build-arg=PROTOBUF_GO_VERSION="$(PROTOBUF_GO_VERSION)"
COMMON_ARGS += --build-arg=GRPC_GO_VERSION="$(GRPC_GO_VERSION)"
COMMON_ARGS += --build-arg=GRPC_GATEWAY_VERSION="$(GRPC_GATEWAY_VERSION)"
COMMON_ARGS += --build-arg=VTPROTOBUF_VERSION="$(VTPROTOBUF_VERSION)"
COMMON_ARGS += --build-arg=DEEPCOPY_VERSION="$(DEEPCOPY_VERSION)"
COMMON_ARGS += --build-arg=GOLANGCILINT_VERSION="$(GOLANGCILINT_VERSION)"
COMMON_ARGS += --build-arg=GOIMPORTS_VERSION="$(GOIMPORTS_VERSION)"
COMMON_ARGS += --build-arg=GOFUMPT_VERSION="$(GOFUMPT_VERSION)"
COMMON_ARGS += --build-arg=TESTPKGS="$(TESTPKGS)"
TOOLCHAIN ?= docker.io/golang:1.20-alpine
@@ -150,7 +150,7 @@ unit-tests-race: ## Performs unit tests with race detection enabled.
.PHONY: coverage
coverage: ## Upload coverage data to codecov.io.
bash -c "bash <(curl -s https://codecov.io/bash) -f $(ARTIFACTS)/coverage.txt -X fix"
bash -c "bash <(curl -s https://codecov.io/bash) -f $(ARTIFACTS)/coverage-unit-tests.txt -X fix"
.PHONY: $(ARTIFACTS)/kres-linux-amd64
$(ARTIFACTS)/kres-linux-amd64:

View File

@@ -9,6 +9,7 @@ import (
"strings"
"github.com/drone/drone-yaml/yaml"
"github.com/siderolabs/gen/slices"
)
// Step is a pipeline Step.
@@ -64,7 +65,15 @@ func (step *Step) EnvironmentFromSecret(name, secretName string) *Step {
// DependsOn appends to a list of step dependencies.
func (step *Step) DependsOn(depends ...string) *Step {
step.container.DependsOn = append(step.container.DependsOn, depends...)
for _, dep := range depends {
if slices.Contains(step.container.DependsOn, func(d string) bool {
return d == dep
}) {
continue
}
step.container.DependsOn = append(step.container.DependsOn, dep)
}
return step
}

View File

@@ -9,6 +9,9 @@ import (
_ "embed"
"fmt"
"io"
"path/filepath"
"github.com/siderolabs/gen/slices"
"github.com/siderolabs/kres/internal/output"
)
@@ -24,15 +27,18 @@ var configTemplate string
type Output struct {
output.FileAdapter
files []file
enabled bool
}
type file struct {
path string
canonicalPath string
enabled bool
}
// NewOutput creates new Makefile output.
func NewOutput() *Output {
output := &Output{
canonicalPath: "github.com/example.com/example.proj",
}
output := &Output{}
output.FileAdapter.FileWriter = output
@@ -49,9 +55,12 @@ func (o *Output) Enable() {
o.enabled = true
}
// CanonicalPath sets canonical import path.
func (o *Output) CanonicalPath(path string) {
o.canonicalPath = path
// NewFile sets canonical import path and project path.
func (o *Output) NewFile(canonicalPath, path string) {
o.files = append(o.files, file{
path: filepath.Join(path, filename),
canonicalPath: canonicalPath,
})
}
// Filenames implements output.FileWriter interface.
@@ -60,25 +69,28 @@ func (o *Output) Filenames() []string {
return nil
}
return []string{filename}
return slices.Map(o.files, func(f file) string { return f.path })
}
// GenerateFile implements output.FileWriter interface.
func (o *Output) GenerateFile(filename string, w io.Writer) error {
switch filename {
case filename:
return o.config(w)
default:
panic("unexpected filename: " + filename)
index := slices.IndexFunc(o.files, func(f file) bool {
return f.path == filename
})
if index >= 0 {
return o.config(o.files[index], w)
}
panic("unexpected filename: " + filename)
}
func (o *Output) config(w io.Writer) error {
func (o *Output) config(f file, w io.Writer) error {
if _, err := w.Write([]byte(output.Preamble("# "))); err != nil {
return err
}
if _, err := fmt.Fprintf(w, configTemplate, o.canonicalPath); err != nil {
if _, err := fmt.Fprintf(w, configTemplate, f.canonicalPath); err != nil {
return err
}

View File

@@ -7,6 +7,8 @@ package makefile
import (
"fmt"
"io"
"github.com/siderolabs/gen/slices"
)
// Descriptions (used as keys) of some predefined variable groups.
@@ -25,6 +27,12 @@ type VariableGroup struct {
// Variable appends variable to the group.
func (group *VariableGroup) Variable(variable *Variable) *VariableGroup {
if slices.Contains(group.variables, func(item *Variable) bool {
return item.name == variable.name
}) {
return group
}
group.variables = append(group.variables, variable)
return group

View File

@@ -9,6 +9,8 @@ import (
"io"
"sort"
"github.com/siderolabs/gen/slices"
"github.com/siderolabs/kres/internal/output"
)
@@ -63,6 +65,13 @@ func (o *Output) Target(name string) *Target {
return target
}
// HasTarget checks that target exists.
func (o *Output) HasTarget(name string) bool {
return slices.Contains(o.targets, func(t *Target) bool {
return t.name == name
})
}
// IfTrueCondition creates new Makefile condition.
func (o *Output) IfTrueCondition(variable string) *Condition {
condition := &Condition{

View File

@@ -8,40 +8,91 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"golang.org/x/mod/modfile"
"github.com/siderolabs/kres/internal/dag"
"github.com/siderolabs/kres/internal/project/common"
"github.com/siderolabs/kres/internal/project/golang"
"github.com/siderolabs/kres/internal/project/meta"
"github.com/siderolabs/kres/internal/project/service"
"github.com/siderolabs/kres/internal/project/wrap"
)
// DetectGolang checks if project at rootPath is Go-based project.
//
//nolint:gocognit,gocyclo,cyclop
func (builder *builder) DetectGolang() (bool, error) {
gomodPath := filepath.Join(builder.rootPath, "go.mod")
lookupDirs := []string{}
gomod, err := os.Open(gomodPath)
if err != nil {
if os.IsNotExist(err) {
return false, nil
err := filepath.Walk(builder.rootPath, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
return nil
}
if filepath.Base(path) == "go.mod" {
lookupDirs = append(lookupDirs, filepath.Dir(path))
}
return nil
})
if err != nil {
return false, err
}
defer gomod.Close() //nolint:errcheck
for _, dir := range lookupDirs {
_, err := os.Stat(filepath.Join(dir, ".kresignore"))
if err == nil {
continue
}
contents, err := io.ReadAll(gomod)
if err != nil {
return true, err
if !os.IsNotExist(err) {
return false, err
}
if err := builder.processDirectory(dir); err != nil {
return false, err
}
builder.meta.GoRootDirectories = append(builder.meta.GoRootDirectories, dir)
}
builder.meta.CanonicalPath = modfile.ModulePath(contents)
if len(builder.meta.GoSourceFiles) == 0 && len(builder.meta.GoDirectories) == 0 {
return false, fmt.Errorf("no Go source files found")
}
return true, nil
}
//nolint:gocognit,gocyclo,cyclop
func (builder *builder) processDirectory(path string) error {
var (
canonicalPath string
dir = filepath.Join(builder.rootPath, path)
)
{
gomodPath := filepath.Join(dir, "go.mod")
gomod, err := os.Open(gomodPath)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
defer gomod.Close() //nolint:errcheck
contents, err := io.ReadAll(gomod)
if err != nil {
return err
}
canonicalPath = modfile.ModulePath(contents)
}
builder.meta.CanonicalPaths = append(builder.meta.CanonicalPaths, canonicalPath)
for _, srcDir := range []string{
"api", // API definitions (generated protobufs, Kubebuilder's resources)
@@ -51,22 +102,22 @@ func (builder *builder) DetectGolang() (bool, error) {
"pkg", // generic, general use packages that can be used independently
"src", // deprecated
} {
exists, err := directoryExists(builder.rootPath, srcDir)
exists, err := directoryExists(dir, srcDir)
if err != nil {
return true, err
return err
}
if exists {
builder.meta.Directories = append(builder.meta.Directories, srcDir)
builder.meta.GoDirectories = append(builder.meta.GoDirectories, srcDir)
builder.meta.Directories = append(builder.meta.Directories, filepath.Join(dir, srcDir))
builder.meta.GoDirectories = append(builder.meta.GoDirectories, filepath.Join(dir, srcDir))
}
}
if len(builder.meta.GoDirectories) == 0 {
// no standard directories found, assume any directory with `.go` files is a source directory
topLevel, err := os.ReadDir(builder.rootPath)
topLevel, err := os.ReadDir(dir)
if err != nil {
return true, err
return err
}
for _, item := range topLevel {
@@ -74,68 +125,74 @@ func (builder *builder) DetectGolang() (bool, error) {
continue
}
result, err := listFilesWithSuffix(filepath.Join(builder.rootPath, item.Name()), ".go")
result, err := listFilesWithSuffix(filepath.Join(dir, item.Name()), ".go")
if err != nil {
return true, err
return err
}
if len(result) > 0 {
builder.meta.Directories = append(builder.meta.Directories, item.Name())
builder.meta.GoDirectories = append(builder.meta.GoDirectories, item.Name())
builder.meta.Directories = append(builder.meta.Directories, filepath.Join(dir, item.Name()))
builder.meta.GoDirectories = append(builder.meta.GoDirectories, filepath.Join(dir, item.Name()))
}
}
}
{
list, err := listFilesWithSuffix(builder.rootPath, ".go")
list, err := listFilesWithSuffix(dir, ".go")
if err != nil {
return true, err
return err
}
for _, item := range list {
builder.meta.SourceFiles = append(builder.meta.SourceFiles, item)
builder.meta.GoSourceFiles = append(builder.meta.GoSourceFiles, item)
builder.meta.SourceFiles = append(builder.meta.SourceFiles, filepath.Join(dir, item))
builder.meta.GoSourceFiles = append(builder.meta.GoSourceFiles, filepath.Join(dir, item))
}
}
if len(builder.meta.GoSourceFiles) == 0 && len(builder.meta.GoDirectories) == 0 {
return false, fmt.Errorf("no Go source files found")
}
builder.meta.SourceFiles = append(builder.meta.SourceFiles,
filepath.Join(dir, "go.mod"),
filepath.Join(dir, "go.sum"),
)
builder.meta.SourceFiles = append(builder.meta.SourceFiles, "go.mod", "go.sum")
rootPath := filepath.Join(builder.rootPath, dir)
for _, candidate := range []string{"pkg/version", "internal/version"} {
exists, err := directoryExists(builder.rootPath, candidate)
if err != nil {
return true, err
}
if exists {
builder.meta.VersionPackage = path.Join(builder.meta.CanonicalPath, candidate)
}
}
{
cmdExists, err := directoryExists(builder.rootPath, "cmd")
if err != nil {
return true, err
}
if cmdExists {
dirs, err := os.ReadDir(filepath.Join(builder.rootPath, "cmd"))
if builder.meta.VersionPackage == "" {
for _, candidate := range []string{"pkg/version", "internal/version"} {
exists, err := directoryExists(dir, candidate)
if err != nil {
return true, err
return err
}
for _, dir := range dirs {
if dir.IsDir() {
builder.meta.Commands = append(builder.meta.Commands, dir.Name())
}
if exists {
builder.meta.VersionPackage = filepath.Join(canonicalPath, filepath.Join(dir, candidate))
}
}
}
return true, nil
cmdExists, err := directoryExists(rootPath, "cmd")
if err != nil {
return err
}
if cmdExists {
path := filepath.Join(dir, "cmd")
dirs, err := os.ReadDir(path)
if err != nil {
return err
}
for _, dir := range dirs {
if dir.IsDir() {
builder.meta.Commands = append(builder.meta.Commands, meta.Command{
Path: filepath.Join(path, dir.Name()),
Name: dir.Name(),
})
}
}
}
return nil
}
// BuildGolang builds project structure for Go project.
@@ -144,49 +201,69 @@ func (builder *builder) BuildGolang() error {
toolchain := golang.NewToolchain(builder.meta)
toolchain.AddInput(builder.commonInputs...)
// linters
golangciLint := golang.NewGolangciLint(builder.meta)
gofumpt := golang.NewGofumpt(builder.meta)
govulncheck := golang.NewGoVulnCheck(builder.meta)
goimports := golang.NewGoimports(builder.meta)
// linters are input to the toolchain as they inject into toolchain build
toolchain.AddInput(golangciLint, gofumpt, govulncheck, goimports)
// add protobufs and go generate
generate := golang.NewGenerate(builder.meta)
// add deepcopy
deepcopy := golang.NewDeepCopy(builder.meta)
toolchain.AddInput(generate, deepcopy)
// add common linter tools
linters := golang.NewLinters(builder.meta)
builder.lintInputs = append(builder.lintInputs, toolchain, golangciLint, gofumpt, govulncheck, goimports)
toolchain.AddInput(generate, deepcopy, linters)
// unit-tests
unitTests := golang.NewUnitTests(builder.meta)
unitTests.AddInput(toolchain)
builder.lintInputs = append(builder.lintInputs, toolchain, linters)
coverage := service.NewCodeCov(builder.meta)
coverage.InputPath = "coverage.txt"
coverage.AddInput(unitTests)
builder.targets = append(builder.targets, unitTests, coverage)
allUnitTests := []dag.Node{}
// linters
for index, canonicalPath := range builder.meta.CanonicalPaths {
projectPath := builder.meta.GoRootDirectories[index]
canonicalPath += "/"
golangciLint := golang.NewGolangciLint(builder.meta, projectPath, canonicalPath)
gofumpt := golang.NewGofumpt(builder.meta, projectPath)
govulncheck := golang.NewGoVulnCheck(builder.meta, projectPath)
goimports := golang.NewGoimports(builder.meta, projectPath, canonicalPath)
// linters are input to the toolchain as they inject into toolchain build
toolchain.AddInput(golangciLint, gofumpt, govulncheck, goimports)
builder.lintInputs = append(builder.lintInputs, toolchain, golangciLint, gofumpt, govulncheck, goimports)
// unit-tests
unitTests := golang.NewUnitTests(builder.meta, projectPath)
unitTests.AddInput(toolchain)
coverage.AddInput(unitTests)
builder.targets = append(builder.targets, unitTests)
allUnitTests = append(allUnitTests, unitTests)
coverage.InputPaths = append(coverage.InputPaths, fmt.Sprintf("coverage-%s.txt", unitTests.Name()))
}
builder.targets = append(builder.targets, coverage)
// process commands
for _, cmd := range builder.meta.Commands {
cfg := CommandConfig{NamedConfig: NamedConfig{name: cmd}}
cfg := CommandConfig{NamedConfig: NamedConfig{name: cmd.Name}}
if err := builder.meta.Config.Load(&cfg); err != nil {
return err
}
build := golang.NewBuild(builder.meta, cmd, filepath.Join("cmd", cmd))
build := golang.NewBuild(builder.meta, cmd.Name, cmd.Path)
build.AddInput(toolchain)
builder.targets = append(builder.targets, build)
if !cfg.DisableImage {
image := common.NewImage(builder.meta, cmd)
image.AddInput(build, builder.lintTarget, wrap.Drone(unitTests))
image := common.NewImage(builder.meta, cmd.Name)
for _, unitTests := range allUnitTests {
image.AddInput(build, builder.lintTarget, wrap.Drone(unitTests))
}
builder.targets = append(builder.targets, image)
}

View File

@@ -0,0 +1,18 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package golang
import (
"os"
"strings"
)
func genName(name, projectPath string) string {
if projectPath == "." || projectPath == "" {
return name
}
return strings.Join(append([]string{name}, strings.Split(projectPath, string(os.PathSeparator))...), "-")
}

View File

@@ -265,7 +265,7 @@ func (generate *Generate) CompileDockerfile(output *dockerfile.Output) error {
"goimports",
"-w",
"-local",
generate.meta.CanonicalPath,
strings.Join(generate.meta.CanonicalPaths, ","),
generate.BaseSpecPath,
),
)
@@ -300,7 +300,7 @@ func (generate *Generate) CompileDockerfile(output *dockerfile.Output) error {
"goimports",
"-w",
"-local",
generate.meta.CanonicalPath,
strings.Join(generate.meta.CanonicalPaths, ","),
spec.Source,
))
}

View File

@@ -21,63 +21,60 @@ type Gofumpt struct {
meta *meta.Options
GoVersion string `yaml:"goVersion"`
Version string `yaml:"version"`
GoVersion string `yaml:"goVersion"`
Version string `yaml:"version"`
projectPath string
}
// NewGofumpt builds Gofumpt node.
func NewGofumpt(meta *meta.Options) *Gofumpt {
meta.BuildArgs = append(meta.BuildArgs, "GOFUMPT_VERSION")
func NewGofumpt(meta *meta.Options, projectPath string) *Gofumpt {
meta.BuildArgs.Add("GOFUMPT_VERSION")
return &Gofumpt{
BaseNode: dag.NewBaseNode("lint-gofumpt"),
BaseNode: dag.NewBaseNode(genName("lint-gofumpt", projectPath)),
meta: meta,
GoVersion: config.GoVersion,
Version: config.GoFmtVersion,
GoVersion: config.GoVersion,
Version: config.GoFmtVersion,
projectPath: projectPath,
}
}
// CompileMakefile implements makefile.Compiler.
func (lint *Gofumpt) CompileMakefile(output *makefile.Output) error {
output.Target("lint-gofumpt").Description("Runs gofumpt linter.").
output.Target(lint.Name()).Description("Runs gofumpt linter.").
Script("@$(MAKE) target-$@")
output.VariableGroup(makefile.VariableGroupCommon).
Variable(makefile.OverridableVariable("GOFUMPT_VERSION", lint.Version)).
Variable(makefile.OverridableVariable("GO_VERSION", lint.GoVersion))
output.Target("fmt").Description("Formats the source code").
Phony().
Script(
`@docker run --rm -it -v $(PWD):/src -w /src golang:$(GO_VERSION) \
if !output.HasTarget("fmt") {
output.Target("fmt").Description("Formats the source code").
Phony().
Script(
`@docker run --rm -it -v $(PWD):/src -w /src golang:$(GO_VERSION) \
bash -c "export GO111MODULE=on; export GOPROXY=https://proxy.golang.org; \
go install mvdan.cc/gofumpt@$(GOFUMPT_VERSION) && \
gofumpt -w ."`,
)
return nil
}
// ToolchainBuild implements common.ToolchainBuilder hook.
func (lint *Gofumpt) ToolchainBuild(stage *dockerfile.Stage) error {
stage.
Step(step.Arg("GOFUMPT_VERSION")).
Step(step.Script(fmt.Sprintf(
`go install mvdan.cc/gofumpt@${GOFUMPT_VERSION} \
&& mv /go/bin/gofumpt %s/gofumpt`, lint.meta.BinPath)))
)
}
return nil
}
// CompileDockerfile implements dockerfile.Compiler.
func (lint *Gofumpt) CompileDockerfile(output *dockerfile.Output) error {
output.Stage("lint-gofumpt").
output.Stage(lint.Name()).
Description("runs gofumpt").
From("base").
Step(step.Script(
`FILES="$(gofumpt -l .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'gofumpt -w .':\n${FILES}"; exit 1)`,
fmt.Sprintf(
`FILES="$(gofumpt -l %s)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'gofumpt -w %s':\n${FILES}"; exit 1)`,
lint.projectPath,
lint.projectPath,
),
))
return nil

View File

@@ -11,12 +11,10 @@ import (
"github.com/siderolabs/kres/internal/output/dockerfile"
"github.com/siderolabs/kres/internal/output/makefile"
"github.com/siderolabs/kres/internal/project/common"
"github.com/siderolabs/kres/internal/project/golang"
)
func TestGofumptInterfaces(t *testing.T) {
assert.Implements(t, (*dockerfile.Compiler)(nil), new(golang.Gofumpt))
assert.Implements(t, (*makefile.Compiler)(nil), new(golang.Gofumpt))
assert.Implements(t, (*common.ToolchainBuilder)(nil), new(golang.Gofumpt))
}

View File

@@ -6,7 +6,6 @@ package golang
import (
"fmt"
"path/filepath"
"github.com/siderolabs/kres/internal/config"
"github.com/siderolabs/kres/internal/dag"
@@ -22,25 +21,27 @@ type Goimports struct {
meta *meta.Options
Version string `yaml:"version"`
Version string `yaml:"version"`
canonicalPath string
projectPath string
}
// NewGoimports builds Goimports node.
func NewGoimports(meta *meta.Options) *Goimports {
meta.BuildArgs = append(meta.BuildArgs, "GOIMPORTS_VERSION")
func NewGoimports(meta *meta.Options, projectPath, canonicalPath string) *Goimports {
return &Goimports{
BaseNode: dag.NewBaseNode("lint-goimports"),
BaseNode: dag.NewBaseNode(genName("lint-goimports", projectPath)),
meta: meta,
Version: config.GoImportsVersion,
Version: config.GoImportsVersion,
canonicalPath: canonicalPath,
projectPath: projectPath,
}
}
// CompileMakefile implements makefile.Compiler.
func (lint *Goimports) CompileMakefile(output *makefile.Output) error {
output.Target("lint-goimports").Description("Runs goimports linter.").
output.Target(lint.Name()).Description("Runs goimports linter.").
Script("@$(MAKE) target-$@")
output.VariableGroup(makefile.VariableGroupCommon).
@@ -49,30 +50,18 @@ func (lint *Goimports) CompileMakefile(output *makefile.Output) error {
return nil
}
// ToolchainBuild implements common.ToolchainBuilder hook.
func (lint *Goimports) ToolchainBuild(stage *dockerfile.Stage) error {
stage.
Step(step.Arg("GOIMPORTS_VERSION")).
Step(step.Script(fmt.Sprintf(
`go install golang.org/x/tools/cmd/goimports@${GOIMPORTS_VERSION} \
&& mv /go/bin/goimports %s/goimports`, lint.meta.BinPath)).
MountCache(filepath.Join(lint.meta.CachePath, "go-build")).
MountCache(filepath.Join(lint.meta.GoPath, "pkg")),
)
return nil
}
// CompileDockerfile implements dockerfile.Compiler.
func (lint *Goimports) CompileDockerfile(output *dockerfile.Output) error {
output.Stage("lint-goimports").
output.Stage(lint.Name()).
Description("runs goimports").
From("base").
Step(step.Script(
fmt.Sprintf(
`FILES="$(goimports -l -local %s .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'goimports -w -local %s .':\n${FILES}"; exit 1)`,
lint.meta.CanonicalPath,
lint.meta.CanonicalPath,
`FILES="$(goimports -l -local %s %s)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'goimports -w -local %s %s':\n${FILES}"; exit 1)`,
lint.canonicalPath,
lint.projectPath,
lint.canonicalPath,
lint.projectPath,
),
))

View File

@@ -11,12 +11,10 @@ import (
"github.com/siderolabs/kres/internal/output/dockerfile"
"github.com/siderolabs/kres/internal/output/makefile"
"github.com/siderolabs/kres/internal/project/common"
"github.com/siderolabs/kres/internal/project/golang"
)
func TestGoimportsInterfaces(t *testing.T) {
assert.Implements(t, (*dockerfile.Compiler)(nil), new(golang.Goimports))
assert.Implements(t, (*makefile.Compiler)(nil), new(golang.Goimports))
assert.Implements(t, (*common.ToolchainBuilder)(nil), new(golang.Goimports))
}

View File

@@ -5,7 +5,6 @@
package golang
import (
"fmt"
"path/filepath"
"github.com/siderolabs/kres/internal/config"
@@ -23,34 +22,37 @@ type GolangciLint struct {
meta *meta.Options
Version string
Version string
canonicalPath string
projectPath string
}
// NewGolangciLint builds golangci-lint node.
func NewGolangciLint(meta *meta.Options) *GolangciLint {
meta.SourceFiles = append(meta.SourceFiles, ".golangci.yml")
meta.BuildArgs = append(meta.BuildArgs, "GOLANGCILINT_VERSION")
func NewGolangciLint(meta *meta.Options, projectPath, canonicalPath string) *GolangciLint {
meta.SourceFiles = append(meta.SourceFiles, filepath.Join(projectPath, ".golangci.yml"))
return &GolangciLint{
BaseNode: dag.NewBaseNode("lint-golangci-lint"),
BaseNode: dag.NewBaseNode(genName("lint-golangci-lint", projectPath)),
meta: meta,
Version: config.GolangCIlintVersion,
Version: config.GolangCIlintVersion,
canonicalPath: canonicalPath,
projectPath: projectPath,
}
}
// CompileGolangci implements golangci.Compiler.
func (lint *GolangciLint) CompileGolangci(output *golangci.Output) error {
output.Enable()
output.CanonicalPath(lint.meta.CanonicalPath)
output.NewFile(lint.canonicalPath, lint.projectPath)
return nil
}
// CompileMakefile implements makefile.Compiler.
func (lint *GolangciLint) CompileMakefile(output *makefile.Output) error {
output.Target("lint-golangci-lint").Description("Runs golangci-lint linter.").
output.Target(lint.Name()).Description("Runs golangci-lint linter.").
Script("@$(MAKE) target-$@")
output.VariableGroup(makefile.VariableGroupCommon).
@@ -59,28 +61,13 @@ func (lint *GolangciLint) CompileMakefile(output *makefile.Output) error {
return nil
}
// ToolchainBuild implements common.ToolchainBuilder hook.
func (lint *GolangciLint) ToolchainBuild(stage *dockerfile.Stage) error {
stage.
Step(step.Arg("GOLANGCILINT_VERSION")).
Step(step.Script(
fmt.Sprintf(
"go install github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCILINT_VERSION} \\\n"+
"\t&& mv /go/bin/golangci-lint %s/golangci-lint", lint.meta.BinPath),
).
MountCache(filepath.Join(lint.meta.CachePath, "go-build")).
MountCache(filepath.Join(lint.meta.GoPath, "pkg")),
)
return nil
}
// CompileDockerfile implements dockerfile.Compiler.
func (lint *GolangciLint) CompileDockerfile(output *dockerfile.Output) error {
output.Stage("lint-golangci-lint").
output.Stage(lint.Name()).
Description("runs golangci-lint").
From("base").
Step(step.Copy(".golangci.yml", ".")).
Step(step.WorkDir(filepath.Join("/src", lint.projectPath))).
Step(step.Copy(filepath.Join(lint.projectPath, ".golangci.yml"), ".")).
Step(step.Env("GOGC", "50")).
Step(step.Run("golangci-lint", "run", "--config", ".golangci.yml").
MountCache(filepath.Join(lint.meta.CachePath, "go-build")).

View File

@@ -11,12 +11,10 @@ import (
"github.com/siderolabs/kres/internal/output/dockerfile"
"github.com/siderolabs/kres/internal/output/makefile"
"github.com/siderolabs/kres/internal/project/common"
"github.com/siderolabs/kres/internal/project/golang"
)
func TestGolangciLintInterfaces(t *testing.T) {
assert.Implements(t, (*dockerfile.Compiler)(nil), new(golang.GolangciLint))
assert.Implements(t, (*makefile.Compiler)(nil), new(golang.GolangciLint))
assert.Implements(t, (*common.ToolchainBuilder)(nil), new(golang.GolangciLint))
}

View File

@@ -5,7 +5,6 @@
package golang
import (
"fmt"
"path/filepath"
"github.com/siderolabs/kres/internal/dag"
@@ -16,49 +15,37 @@ import (
)
// GoVulnCheck provides GoVulnCheck linter.
//
//nolint:govet
type GoVulnCheck struct {
dag.BaseNode
meta *meta.Options
meta *meta.Options
projectPath string
}
// NewGoVulnCheck builds GoVulnCheck node.
func NewGoVulnCheck(meta *meta.Options) *GoVulnCheck {
func NewGoVulnCheck(meta *meta.Options, projectPath string) *GoVulnCheck {
return &GoVulnCheck{
BaseNode: dag.NewBaseNode("lint-govulncheck"),
BaseNode: dag.NewBaseNode(genName("lint-govulncheck", projectPath)),
meta: meta,
meta: meta,
projectPath: projectPath,
}
}
// CompileMakefile implements makefile.Compiler.
func (lint *GoVulnCheck) CompileMakefile(output *makefile.Output) error {
output.Target("lint-govulncheck").Description("Runs govulncheck linter.").
output.Target(lint.Name()).Description("Runs govulncheck linter.").
Script("@$(MAKE) target-$@")
return nil
}
// ToolchainBuild implements common.ToolchainBuilder hook.
func (lint *GoVulnCheck) ToolchainBuild(stage *dockerfile.Stage) error {
stage.
Step(step.Script(fmt.Sprintf(
`go install golang.org/x/vuln/cmd/govulncheck@latest \
&& mv /go/bin/govulncheck %s/govulncheck`, lint.meta.BinPath)).
MountCache(filepath.Join(lint.meta.CachePath, "go-build")).
MountCache(filepath.Join(lint.meta.GoPath, "pkg")),
)
return nil
}
// CompileDockerfile implements dockerfile.Compiler.
func (lint *GoVulnCheck) CompileDockerfile(output *dockerfile.Output) error {
output.Stage("lint-govulncheck").
output.Stage(lint.Name()).
Description("runs govulncheck").
From("base").
Step(step.WorkDir(filepath.Join("/src", lint.projectPath))).
Step(step.Script(
`govulncheck ./...`,
).

View File

@@ -11,12 +11,10 @@ import (
"github.com/siderolabs/kres/internal/output/dockerfile"
"github.com/siderolabs/kres/internal/output/makefile"
"github.com/siderolabs/kres/internal/project/common"
"github.com/siderolabs/kres/internal/project/golang"
)
func TestGoVulnCheckInterfaces(t *testing.T) {
assert.Implements(t, (*dockerfile.Compiler)(nil), new(golang.GoVulnCheck))
assert.Implements(t, (*makefile.Compiler)(nil), new(golang.GoVulnCheck))
assert.Implements(t, (*common.ToolchainBuilder)(nil), new(golang.GoVulnCheck))
}

View File

@@ -0,0 +1,74 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package golang
import (
"fmt"
"path/filepath"
"github.com/siderolabs/kres/internal/dag"
"github.com/siderolabs/kres/internal/output/dockerfile"
"github.com/siderolabs/kres/internal/output/dockerfile/step"
"github.com/siderolabs/kres/internal/project/meta"
)
// Linters is the common node for all linters.
type Linters struct {
meta *meta.Options
dag.BaseNode
}
// NewLinters builds GoVulnCheck node.
func NewLinters(meta *meta.Options) *Linters {
meta.BuildArgs.Add(
"GOLANGCILINT_VERSION",
"GOIMPORTS_VERSION",
"GOFUMPT_VERSION",
)
return &Linters{
BaseNode: dag.NewBaseNode("go-linters"),
meta: meta,
}
}
// ToolchainBuild implements common.ToolchainBuilder hook.
func (linters *Linters) ToolchainBuild(stage *dockerfile.Stage) error {
stage.
Step(step.Arg("GOLANGCILINT_VERSION")).
Step(step.Script(
fmt.Sprintf(
"go install github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCILINT_VERSION} \\\n"+
"\t&& mv /go/bin/golangci-lint %s/golangci-lint", linters.meta.BinPath),
).
MountCache(filepath.Join(linters.meta.CachePath, "go-build")).
MountCache(filepath.Join(linters.meta.GoPath, "pkg")),
).
Step(step.Script(fmt.Sprintf(
`go install golang.org/x/vuln/cmd/govulncheck@latest \
&& mv /go/bin/govulncheck %s/govulncheck`, linters.meta.BinPath)).
MountCache(filepath.Join(linters.meta.CachePath, "go-build")).
MountCache(filepath.Join(linters.meta.GoPath, "pkg")),
).
Step(step.Arg("GOIMPORTS_VERSION")).
Step(step.Script(fmt.Sprintf(
`go install golang.org/x/tools/cmd/goimports@${GOIMPORTS_VERSION} \
&& mv /go/bin/goimports %s/goimports`, linters.meta.BinPath)).
MountCache(filepath.Join(linters.meta.CachePath, "go-build")).
MountCache(filepath.Join(linters.meta.GoPath, "pkg")),
).
Step(step.Arg("GOFUMPT_VERSION")).
Step(step.Script(fmt.Sprintf(
`go install mvdan.cc/gofumpt@${GOFUMPT_VERSION} \
&& mv /go/bin/gofumpt %s/gofumpt`, linters.meta.BinPath)))
return nil
}
// SkipAsMakefileDependency implements makefile.SkipAsMakefileDependency.
func (linters *Linters) SkipAsMakefileDependency() {
}

View File

@@ -0,0 +1,18 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package golang_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/siderolabs/kres/internal/project/common"
"github.com/siderolabs/kres/internal/project/golang"
)
func TestLintersInterfaces(t *testing.T) {
assert.Implements(t, (*common.ToolchainBuilder)(nil), new(golang.Linters))
}

View File

@@ -208,11 +208,21 @@ func (toolchain *Toolchain) CompileDockerfile(output *dockerfile.Output) error {
base := output.Stage("base").
Description("tools and sources").
From("tools").
Step(step.WorkDir("/src")).
Step(step.Copy("./go.mod", ".")).
Step(step.Copy("./go.sum", ".")).
Step(step.Run("go", "mod", "download").MountCache(filepath.Join(toolchain.meta.GoPath, "pkg"))).
Step(step.Run("go", "mod", "verify").MountCache(filepath.Join(toolchain.meta.GoPath, "pkg")))
Step(step.WorkDir("/src"))
for _, rootDir := range toolchain.meta.GoRootDirectories {
gomodPath := filepath.Join(rootDir, "go.mod")
gosumPath := filepath.Join(rootDir, "go.sum")
base.Step(step.Copy(gomodPath, gomodPath)).
Step(step.Copy(gosumPath, gosumPath))
}
for _, rootDir := range toolchain.meta.GoRootDirectories {
base.Step(step.Run("cd", rootDir)).
Step(step.Run("go", "mod", "download").MountCache(filepath.Join(toolchain.meta.GoPath, "pkg"))).
Step(step.Run("go", "mod", "verify").MountCache(filepath.Join(toolchain.meta.GoPath, "pkg")))
}
for _, directory := range toolchain.meta.GoDirectories {
base.Step(step.Copy("./"+directory, "./"+directory))

View File

@@ -22,18 +22,20 @@ type UnitTests struct { //nolint:govet
RequiresInsecure bool `yaml:"requiresInsecure"`
// ExtraArgs are extra arguments for `go test`.
ExtraArgs string `yaml:"extraArgs"`
ExtraArgs string `yaml:"extraArgs"`
packagePath string
meta *meta.Options
}
// NewUnitTests initializes UnitTests.
func NewUnitTests(meta *meta.Options) *UnitTests {
meta.BuildArgs = append(meta.BuildArgs, "TESTPKGS")
func NewUnitTests(meta *meta.Options, packagePath string) *UnitTests {
meta.BuildArgs.Add("TESTPKGS")
return &UnitTests{
BaseNode: dag.NewBaseNode("unit-tests"),
meta: meta,
BaseNode: dag.NewBaseNode(genName("unit-tests", packagePath)),
meta: meta,
packagePath: packagePath,
}
}
@@ -52,9 +54,13 @@ func (tests *UnitTests) CompileDockerfile(output *dockerfile.Output) error {
extraArgs += " "
}
output.Stage("unit-tests-run").
workdir := step.WorkDir(filepath.Join("/src", tests.packagePath))
testRun := fmt.Sprintf("%s-run", tests.Name())
output.Stage(testRun).
Description("runs unit-tests").
From("base").
Step(workdir).
Step(step.Arg("TESTPKGS")).
Step(wrapAsInsecure(
step.Script(
@@ -66,13 +72,14 @@ func (tests *UnitTests) CompileDockerfile(output *dockerfile.Output) error {
MountCache(filepath.Join(tests.meta.GoPath, "pkg")).
MountCache("/tmp")))
output.Stage("unit-tests").
output.Stage(tests.Name()).
From("scratch").
Step(step.Copy("/src/coverage.txt", "/coverage.txt").From("unit-tests-run"))
Step(step.Copy(filepath.Join("/src", tests.packagePath, "coverage.txt"), fmt.Sprintf("/coverage-%s.txt", tests.Name())).From(testRun))
output.Stage("unit-tests-race").
output.Stage(fmt.Sprintf("%s-race", tests.Name())).
Description("runs unit-tests with race detector").
From("base").
Step(workdir).
Step(step.Arg("TESTPKGS")).
Step(wrapAsInsecure(
step.Script(
@@ -100,12 +107,12 @@ func (tests *UnitTests) CompileMakefile(output *makefile.Output) error {
scriptExtraArgs += ` TARGET_ARGS="--allow security.insecure"`
}
output.Target("unit-tests").
output.Target(tests.Name()).
Description("Performs unit tests").
Script(fmt.Sprintf("@$(MAKE) local-$@ DEST=$(ARTIFACTS)%s", scriptExtraArgs)).
Phony()
output.Target("unit-tests-race").
output.Target(fmt.Sprintf("%s-race", tests.Name())).
Description("Performs unit tests with race detection enabled.").
Script(fmt.Sprintf("@$(MAKE) target-$@%s", scriptExtraArgs)).
Phony()
@@ -115,11 +122,11 @@ func (tests *UnitTests) CompileMakefile(output *makefile.Output) error {
// CompileDrone implements drone.Compiler.
func (tests *UnitTests) CompileDrone(output *drone.Output) error {
output.Step(drone.MakeStep("unit-tests").
output.Step(drone.MakeStep(tests.Name()).
DependsOn(dag.GatherMatchingInputNames(tests, dag.Implements[drone.Compiler]())...),
)
output.Step(drone.MakeStep("unit-tests-race").
output.Step(drone.MakeStep(fmt.Sprintf("%s-race", tests.Name())).
DependsOn(dag.GatherMatchingInputNames(tests, dag.Implements[drone.Compiler]())...),
)

View File

@@ -5,7 +5,11 @@
// Package meta provides project options from source code.
package meta
import "github.com/siderolabs/kres/internal/config"
import (
"github.com/siderolabs/gen/slices"
"github.com/siderolabs/kres/internal/config"
)
// Options for the project.
type Options struct { //nolint:govet
@@ -19,8 +23,8 @@ type Options struct { //nolint:govet
// Git settings.
MainBranch string
// CanonicalPath, import path for Go projects.
CanonicalPath string
// CanonicalPaths, import path for Go projects.
CanonicalPaths []string
// VersionPackage is a canonical path to version package (if any).
VersionPackage string
@@ -53,10 +57,13 @@ type Options struct { //nolint:govet
MarkdownSourceFiles []string
// Commands are top-level binaries to be built.
Commands []string
Commands []Command
// GoRootDirectories contans the list of all go.mod root directories.
GoRootDirectories []string
// BuildArgs passed down to Dockerfiles.
BuildArgs []string
BuildArgs BuildArgs
// Path to /bin.
BinPath string
@@ -76,3 +83,28 @@ type Options struct { //nolint:govet
// ArtifactsPath binary output path.
ArtifactsPath string
}
// Command defines Golang executable build configuration.
type Command struct {
// Path defines command source path.
Path string
// Name defines command name.
Name string
}
// BuildArgs defines input argument list.
type BuildArgs []string
// Add adds the args to list if it doesn't exist already.
func (args *BuildArgs) Add(arg ...string) {
for _, value := range arg {
if slices.Contains(*args, func(a string) bool {
return a == value
}) {
continue
}
*args = append(*args, value)
}
}

View File

@@ -20,9 +20,9 @@ type CodeCov struct {
meta *meta.Options
InputPath string `yaml:"inputPath"`
TargetThreshold int `yaml:"targetThreshold"`
Enabled bool `yaml:"enabled"`
InputPaths []string `yaml:"inputPaths"`
TargetThreshold int `yaml:"targetThreshold"`
Enabled bool `yaml:"enabled"`
}
// NewCodeCov initializes CodeCov.
@@ -57,9 +57,12 @@ func (coverage *CodeCov) CompileMakefile(output *makefile.Output) error {
return nil
}
output.Target("coverage").Description("Upload coverage data to codecov.io.").
Script(fmt.Sprintf(`bash -c "bash <(curl -s https://codecov.io/bash) -f $(ARTIFACTS)/%s -X fix"`, coverage.InputPath)).
Phony()
target := output.Target("coverage").Description("Upload coverage data to codecov.io.")
for _, inputPath := range coverage.InputPaths {
target.Script(fmt.Sprintf(`bash -c "bash <(curl -s https://codecov.io/bash) -f $(ARTIFACTS)/%s -X fix"`, inputPath)).
Phony()
}
return nil
}