Add imports checks to linter (#3056)

supersedes https://github.com/woodpecker-ci/woodpecker/pull/874

---------

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
qwerty287 2023-12-29 21:19:42 +01:00 committed by GitHub
parent af513b5a30
commit a37af3eeac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 190 additions and 65 deletions

View file

@ -16,6 +16,92 @@ linters-settings:
- ^log.Fatal().*$
errorlint:
errorf-multi: true
depguard:
rules:
pipeline:
list-mode: lax
files:
- '**/pipeline/**/*.go'
- '**/pipeline/*.go'
- '!**/cli/pipeline/*.go'
- '!**/cli/pipeline/**/*.go'
- '!**/server/pipeline/*.go'
- '!**/server/pipeline/**/*.go'
deny:
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/server'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/web'
shared:
list-mode: lax
files:
- '**/shared/**/*.go'
- '**/shared/*.go'
- '!**/pipeline/shared/*.go'
- '!**/pipeline/shared/**/*.go'
deny:
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/pipeline'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/server'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/web'
woodpecker-go:
list-mode: lax
files:
- '**/woodpecker-go/woodpecker/**/*.go'
- '**/woodpecker-go/woodpecker/*.go'
deny:
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/pipeline'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/server'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/shared'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/web'
agent:
list-mode: lax
files:
- '**/agent/**/*.go'
- '**/agent/*.go'
- '**/cmd/agent/**/*.go'
- '**/cmd/agent/*.go'
deny:
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/server'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/server'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/web'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker'
cli:
list-mode: lax
files:
- '**/cli/**/*.go'
- '**/cli/*.go'
- '**/cmd/cli/**/*.go'
- '**/cmd/cli/*.go'
deny:
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/server'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/server'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/web'
server:
list-mode: lax
files:
- '**/server/**/*.go'
- '**/server/*.go'
- '**/cmd/server/**/*.go'
- '**/cmd/server/*.go'
- '**/web/**/*.go'
- '**/web/*.go'
deny:
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/agent'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/cmd/cli'
- pkg: 'go.woodpecker-ci.org/woodpecker/v2/woodpecker-go/woodpecker'
linters:
disable-all: true
@ -37,9 +123,10 @@ linters:
- errorlint
- forbidigo
- zerologlint
- depguard
run:
timeout: 5m
timeout: 15m
issues:
exclude-rules:

View file

@ -141,7 +141,7 @@ ui-dependencies: ## Install UI dependencies
.PHONY: lint
lint: install-tools ## Lint code
@echo "Running golangci-lint"
golangci-lint run --timeout 15m
golangci-lint run
lint-ui: ## Lint UI code
(cd web/; pnpm install)

View file

@ -94,7 +94,7 @@ func runExec(c *cli.Context, file, repoPath string) error {
axes, err := matrix.ParseString(string(dat))
if err != nil {
return fmt.Errorf("Parse matrix fail")
return fmt.Errorf("parse matrix fail")
}
if len(axes) == 0 {

View file

@ -0,0 +1,32 @@
// Copyright 2023 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metadata
import (
"fmt"
"strings"
"github.com/drone/envsubst"
)
func EnvVarSubst(yaml string, environ map[string]string) (string, error) {
return envsubst.Eval(yaml, func(name string) string {
env := environ[name]
if strings.Contains(env, "\n") {
env = fmt.Sprintf("%q", env)
}
return env
})
}

View file

@ -0,0 +1,47 @@
// Copyright 2023 Woodpecker Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metadata
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestEnvVarSubst(t *testing.T) {
testCases := []struct {
name string
yaml string
environ map[string]string
want string
}{{
name: "simple substitution",
yaml: `steps:
step1:
image: ${HELLO_IMAGE}`,
environ: map[string]string{"HELLO_IMAGE": "hello-world"},
want: `steps:
step1:
image: hello-world`,
}}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
result, err := EnvVarSubst(testCase.yaml, testCase.environ)
assert.NoError(t, err)
assert.EqualValues(t, testCase.want, result)
})
}
}

View file

@ -20,7 +20,6 @@ import (
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
)
@ -543,7 +542,7 @@ func TestConstraints(t *testing.T) {
for _, test := range testdata {
t.Run(test.desc, func(t *testing.T) {
conf, err := frontend.EnvVarSubst(test.conf, test.with.Environ())
conf, err := metadata.EnvVarSubst(test.conf, test.with.Environ())
assert.NoError(t, err)
c := parseConstraints(t, conf)
got, err := c.Match(test.with, false, test.env)

View file

@ -18,9 +18,9 @@ import (
"crypto/sha256"
"fmt"
"go.woodpecker-ci.org/woodpecker/v2/pipeline"
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
"go.woodpecker-ci.org/woodpecker/v2/server/pipeline/stepbuilder"
"go.woodpecker-ci.org/woodpecker/v2/server/store"
)
@ -32,7 +32,7 @@ func findOrPersistPipelineConfig(store store.Store, currentPipeline *model.Pipel
RepoID: currentPipeline.RepoID,
Data: forgeYamlConfig.Data,
Hash: sha,
Name: pipeline.SanitizePath(forgeYamlConfig.Name),
Name: stepbuilder.SanitizePath(forgeYamlConfig.Name),
}
err = store.ConfigCreate(conf)
if err != nil {

View file

@ -21,16 +21,16 @@ import (
"github.com/rs/zerolog/log"
"go.woodpecker-ci.org/woodpecker/v2/pipeline"
pipeline_errors "go.woodpecker-ci.org/woodpecker/v2/pipeline/errors"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/compiler"
"go.woodpecker-ci.org/woodpecker/v2/server"
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
"go.woodpecker-ci.org/woodpecker/v2/server/pipeline/stepbuilder"
"go.woodpecker-ci.org/woodpecker/v2/server/store"
)
func parsePipeline(store store.Store, currentPipeline *model.Pipeline, user *model.User, repo *model.Repo, yamls []*forge_types.FileMeta, envs map[string]string) ([]*pipeline.Item, error) {
func parsePipeline(store store.Store, currentPipeline *model.Pipeline, user *model.User, repo *model.Repo, yamls []*forge_types.FileMeta, envs map[string]string) ([]*stepbuilder.Item, error) {
netrc, err := server.Config.Services.Forge.Netrc(user, repo)
if err != nil {
log.Error().Err(err).Msg("Failed to generate netrc file")
@ -66,7 +66,7 @@ func parsePipeline(store store.Store, currentPipeline *model.Pipeline, user *mod
envs[k] = v
}
b := pipeline.StepBuilder{
b := stepbuilder.StepBuilder{
Repo: repo,
Curr: currentPipeline,
Last: last,
@ -89,7 +89,7 @@ func parsePipeline(store store.Store, currentPipeline *model.Pipeline, user *mod
func createPipelineItems(c context.Context, store store.Store,
currentPipeline *model.Pipeline, user *model.User, repo *model.Repo,
yamls []*forge_types.FileMeta, envs map[string]string,
) (*model.Pipeline, []*pipeline.Item, error) {
) (*model.Pipeline, []*stepbuilder.Item, error) {
pipelineItems, err := parsePipeline(store, currentPipeline, user, repo, yamls, envs)
if pipeline_errors.HasBlockingErrors(err) {
currentPipeline, uerr := UpdateToStatusError(store, *currentPipeline, err)
@ -113,7 +113,7 @@ func createPipelineItems(c context.Context, store store.Store,
// setPipelineStepsOnPipeline is the link between pipeline representation in "pipeline package" and server
// to be specific this func currently is used to convert the pipeline.Item list (crafted by StepBuilder.Build()) into
// a pipeline that can be stored in the database by the server
func setPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*pipeline.Item) *model.Pipeline {
func setPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*stepbuilder.Item) *model.Pipeline {
var pidSequence int
for _, item := range pipelineItems {
if pidSequence < item.Workflow.PID {

View file

@ -3,9 +3,9 @@ package pipeline
import (
"testing"
sharedPipeline "go.woodpecker-ci.org/woodpecker/v2/pipeline"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
sharedPipeline "go.woodpecker-ci.org/woodpecker/v2/server/pipeline/stepbuilder"
)
func TestSetPipelineStepsOnPipeline(t *testing.T) {

View file

@ -19,13 +19,13 @@ import (
"encoding/json"
"fmt"
"go.woodpecker-ci.org/woodpecker/v2/pipeline"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/rpc"
"go.woodpecker-ci.org/woodpecker/v2/server"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
"go.woodpecker-ci.org/woodpecker/v2/server/pipeline/stepbuilder"
)
func queuePipeline(repo *model.Repo, pipelineItems []*pipeline.Item) error {
func queuePipeline(repo *model.Repo, pipelineItems []*stepbuilder.Item) error {
var tasks []*model.Task
for _, item := range pipelineItems {
if item.Workflow.State == model.StatusSkipped {
@ -53,7 +53,7 @@ func queuePipeline(repo *model.Repo, pipelineItems []*pipeline.Item) error {
return server.Config.Services.Queue.PushAtOnce(context.Background(), tasks)
}
func taskIds(dependsOn []string, pipelineItems []*pipeline.Item) (taskIds []string) {
func taskIds(dependsOn []string, pipelineItems []*stepbuilder.Item) (taskIds []string) {
for _, dep := range dependsOn {
for _, pipelineItem := range pipelineItems {
if pipelineItem.Workflow.Name == dep {

View file

@ -19,13 +19,13 @@ import (
"github.com/rs/zerolog/log"
"go.woodpecker-ci.org/woodpecker/v2/pipeline"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
"go.woodpecker-ci.org/woodpecker/v2/server/pipeline/stepbuilder"
"go.woodpecker-ci.org/woodpecker/v2/server/store"
)
// start a pipeline, make sure it was stored persistent in the store before
func start(ctx context.Context, store store.Store, activePipeline *model.Pipeline, user *model.User, repo *model.Repo, pipelineItems []*pipeline.Item) (*model.Pipeline, error) {
func start(ctx context.Context, store store.Store, activePipeline *model.Pipeline, user *model.User, repo *model.Repo, pipelineItems []*stepbuilder.Item) (*model.Pipeline, error) {
// call to cancel previous pipelines if needed
if err := cancelPreviousPipelines(ctx, store, activePipeline, repo, user); err != nil {
// should be not breaking

View file

@ -12,30 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package frontend
package stepbuilder
import (
"fmt"
"net/url"
"strings"
"github.com/drone/envsubst"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
"go.woodpecker-ci.org/woodpecker/v2/version"
)
func EnvVarSubst(yaml string, environ map[string]string) (string, error) {
return envsubst.Eval(yaml, func(name string) string {
env := environ[name]
if strings.Contains(env, "\n") {
env = fmt.Sprintf("%q", env)
}
return env
})
}
// MetadataFromStruct return the metadata from a pipeline will run with.
func MetadataFromStruct(forge metadata.ServerForge, repo *model.Repo, pipeline, last *model.Pipeline, workflow *model.Workflow, sysURL string) metadata.Metadata {
host := sysURL

View file

@ -12,45 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package frontend_test
package stepbuilder
import (
"testing"
"github.com/stretchr/testify/assert"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
"go.woodpecker-ci.org/woodpecker/v2/server/forge/mocks"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
)
func TestEnvVarSubst(t *testing.T) {
testCases := []struct {
name string
yaml string
environ map[string]string
want string
}{{
name: "simple substitution",
yaml: `steps:
step1:
image: ${HELLO_IMAGE}`,
environ: map[string]string{"HELLO_IMAGE": "hello-world"},
want: `steps:
step1:
image: hello-world`,
}}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
result, err := frontend.EnvVarSubst(testCase.yaml, testCase.environ)
assert.NoError(t, err)
assert.EqualValues(t, testCase.want, result)
})
}
}
func TestMetadataFromStruct(t *testing.T) {
forge := mocks.NewForge(t)
forge.On("Name").Return("gitea")
@ -125,7 +98,7 @@ func TestMetadataFromStruct(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
result := frontend.MetadataFromStruct(testCase.forge, testCase.repo, testCase.pipeline, testCase.last, testCase.workflow, testCase.sysURL)
result := MetadataFromStruct(testCase.forge, testCase.repo, testCase.pipeline, testCase.last, testCase.workflow, testCase.sysURL)
assert.EqualValues(t, testCase.expectedMetadata, result)
assert.EqualValues(t, testCase.expectedEnviron, result.Environ())
})

View file

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package pipeline
package stepbuilder
import (
"fmt"
@ -30,7 +30,6 @@ import (
yaml_types "go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/types"
forge_types "go.woodpecker-ci.org/woodpecker/v2/server/forge/types"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml"
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/compiler"
@ -117,7 +116,7 @@ func (b *StepBuilder) Build() (items []*Item, errorsAndWarnings error) {
}
func (b *StepBuilder) genItemForWorkflow(workflow *model.Workflow, axis matrix.Axis, data string) (item *Item, errorsAndWarnings error) {
workflowMetadata := frontend.MetadataFromStruct(b.Forge, b.Repo, b.Curr, b.Last, workflow, b.Host)
workflowMetadata := MetadataFromStruct(b.Forge, b.Repo, b.Curr, b.Last, workflow, b.Host)
environ := b.environmentVariables(workflowMetadata, axis)
// add global environment variables for substituting
@ -130,7 +129,7 @@ func (b *StepBuilder) genItemForWorkflow(workflow *model.Workflow, axis matrix.A
}
// substitute vars
substituted, err := frontend.EnvVarSubst(data, environ)
substituted, err := metadata.EnvVarSubst(data, environ)
if err != nil {
return nil, multierr.Append(errorsAndWarnings, err)
}

View file

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package pipeline
package stepbuilder
import (
"fmt"