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:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
27
Dockerfile
27
Dockerfile
@@ -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
|
||||
|
||||
|
||||
18
Makefile
18
Makefile
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
18
internal/project/golang/gen_name.go
Normal file
18
internal/project/golang/gen_name.go
Normal 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))...), "-")
|
||||
}
|
||||
@@ -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,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
))
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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")).
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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 ./...`,
|
||||
).
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
74
internal/project/golang/linters.go
Normal file
74
internal/project/golang/linters.go
Normal 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() {
|
||||
}
|
||||
18
internal/project/golang/linters_test.go
Normal file
18
internal/project/golang/linters_test.go
Normal 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))
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -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]())...),
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user