diff --git a/.golangci.yaml b/.golangci.yaml index 41705e83e6e5..8e6af74fa29b 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -10,7 +10,7 @@ linters: disable-all: true enable: - unused - - exportloopref + - copyloopvar - gosimple - govet - ineffassign diff --git a/cmd/external-plugins/automated-approver/plugin.go b/cmd/external-plugins/automated-approver/plugin.go index cc45f057652a..4f4496a4ff53 100644 --- a/cmd/external-plugins/automated-approver/plugin.go +++ b/cmd/external-plugins/automated-approver/plugin.go @@ -183,7 +183,6 @@ func (ac *ApproveCondition) checkChangedFiles(logger *zap.SugaredLogger, changes defer logger.Sync() logger.Debugf("Checking if PR changed only allowed files: %v", ac.ChangedFiles) for _, change := range changes { - change := change logger.Debugf("Checking file: %s", change.Filename) matched := slices.ContainsFunc(ac.ChangedFiles, func(allowedFile string) bool { filesMatcher := regexp.MustCompile(allowedFile) diff --git a/cmd/image-builder/config.go b/cmd/image-builder/config.go index 0b9e71c489be..d056f9631e55 100644 --- a/cmd/image-builder/config.go +++ b/cmd/image-builder/config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "regexp" "slices" "strconv" @@ -21,6 +22,7 @@ const ( Prow CISystem = "Prow" GithubActions CISystem = "GithubActions" AzureDevOps CISystem = "AzureDevOps" + Jenkins CISystem = "Jenkins" ) type Config struct { @@ -147,7 +149,8 @@ func (gitState GitStateConfig) IsPullRequest() bool { return gitState.isPullRequest } -func LoadGitStateConfig(ciSystem CISystem) (GitStateConfig, error) { +// TODO (dekiel): Add logger parameter to all functions reading a git state. +func LoadGitStateConfig(logger Logger, ciSystem CISystem) (GitStateConfig, error) { switch ciSystem { // Load from env specific for Azure DevOps and Prow Jobs case AzureDevOps, Prow: @@ -155,6 +158,8 @@ func LoadGitStateConfig(ciSystem CISystem) (GitStateConfig, error) { // Load from env specific for Github Actions case GithubActions: return loadGithubActionsGitState() + case Jenkins: + return loadJenkinsGitState(logger) default: // Unknown CI System, return error and empty git state return GitStateConfig{}, fmt.Errorf("unknown ci system, got %s", ciSystem) @@ -326,6 +331,75 @@ func loadGithubActionsGitState() (GitStateConfig, error) { } } +// loadJenkinsGitState loads git state from environment variables specific for Jenkins. +func loadJenkinsGitState(logger Logger) (GitStateConfig, error) { + // Load from env specific for Jenkins Jobs + prID, isPullRequest := os.LookupEnv("CHANGE_ID") + gitURL, present := os.LookupEnv("GIT_URL") + if !present { + return GitStateConfig{}, fmt.Errorf("GIT_URL environment variable is not set, please set it to valid git URL") + } + + owner, repo, err := extractOwnerAndRepoFromGitURL(logger, gitURL) + if err != nil { + return GitStateConfig{}, fmt.Errorf("failed to extract owner and repository from git URL %s: %w", gitURL, err) + } + + baseCommitSHA, present := os.LookupEnv("GIT_COMMIT") + if !present { + return GitStateConfig{}, fmt.Errorf("GIT_COMMIT environment variable is not set, please set it to valid commit SHA") + } + + gitState := GitStateConfig{ + RepositoryName: repo, + RepositoryOwner: owner, + JobType: "postsubmit", + BaseCommitSHA: baseCommitSHA, + } + + if isPullRequest { + pullNumber, err := strconv.Atoi(prID) + if err != nil { + return GitStateConfig{}, fmt.Errorf("failed to convert prID string variable to integer: %w", err) + } + + baseRef, present := os.LookupEnv("CHANGE_BRANCH") + if !present { + return GitStateConfig{}, fmt.Errorf("CHANGE_BRANCH environment variable is not set, please set it to valid base branch name") + } + pullRequestHeadSHA, present := os.LookupEnv("CHANGE_HEAD_SHA") + if !present { + return GitStateConfig{}, fmt.Errorf("CHANGE_HEAD_SHA environment variable is not set, please set it to valid commit SHA") + } + gitState.JobType = "presubmit" + gitState.PullRequestNumber = pullNumber + gitState.BaseCommitRef = baseRef + gitState.PullHeadCommitSHA = pullRequestHeadSHA + gitState.isPullRequest = true + } + + return gitState, nil +} + +func extractOwnerAndRepoFromGitURL(logger Logger, gitURL string) (string, string, error) { + re := regexp.MustCompile(`.*/(?P.*)/(?P.*).git`) + matches := re.FindStringSubmatch(gitURL) + + logger.Debugw("Extracted matches from git URL", "matches", matches, "gitURL", gitURL) + + if len(matches) != 3 { + return "", "", fmt.Errorf("failed to extract owner and repository from git URL") + } + + owner := matches[re.SubexpIndex("owner")] + repo := matches[re.SubexpIndex("repo")] + + logger.Debugw("Extracted owner from git URL", "owner", owner) + logger.Debugw("Extracted repository from git URL", "repo", repo) + + return owner, repo, nil +} + // DetermineUsedCISystem return CISystem bind to system in which image builder is running or error if unknown // It is used to avoid getting env variables in multiple parts of image builder func DetermineUsedCISystem() (CISystem, error) { @@ -358,5 +432,11 @@ func determineUsedCISystem(envGetter func(key string) string, envLookup func(key return AzureDevOps, nil } + // JENKINS_HOME environment variable is set in Jenkins + _, isJenkins := envLookup("JENKINS_HOME") + if isJenkins { + return Jenkins, nil + } + return "", fmt.Errorf("cannot determine ci system: unknown system") } diff --git a/cmd/image-builder/config_test.go b/cmd/image-builder/config_test.go index 97ae9d27aa2f..22bc26d3c993 100644 --- a/cmd/image-builder/config_test.go +++ b/cmd/image-builder/config_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/kyma-project/test-infra/pkg/tags" + "go.uber.org/zap" ) func Test_ParseConfig(t *testing.T) { @@ -379,6 +380,48 @@ func TestLoadGitStateConfig(t *testing.T) { PullHeadCommitSHA: "e47034172c36d3e5fb407b5ba57adf0f7868599d", }, }, + { + name: "load data from push event for jenkins", + options: options{ + ciSystem: Jenkins, + }, + env: map[string]string{ + "CHANGE_BRANCH": "refs/heads/main", + "JENKINS_HOME": "/some/absolute/path", + "GIT_URL": "github.com/kyma-project/test-infra.git", + "GIT_COMMIT": "1234", + }, + gitState: GitStateConfig{ + RepositoryName: "test-infra", + RepositoryOwner: "kyma-project", + JobType: "postsubmit", + BaseCommitSHA: "1234", + }, + }, + { + name: "load data from pull request event for jenkins", + options: options{ + ciSystem: Jenkins, + }, + env: map[string]string{ + "CHANGE_BRANCH": "refs/heads/main", + "JENKINS_HOME": "/some/absolute/path", + "CHANGE_ID": "14", + "GIT_URL": "github.com/kyma-project/test-infra.git", + "GIT_COMMIT": "1234", + "CHANGE_HEAD_SHA": "4321", // Must be explicitly set when calling docker run + }, + gitState: GitStateConfig{ + RepositoryName: "test-infra", + RepositoryOwner: "kyma-project", + JobType: "presubmit", + BaseCommitSHA: "1234", + BaseCommitRef: "refs/heads/main", + PullRequestNumber: 14, + PullHeadCommitSHA: "4321", + isPullRequest: true, + }, + }, } for _, c := range tc { @@ -388,8 +431,15 @@ func TestLoadGitStateConfig(t *testing.T) { t.Setenv(key, value) } + // Setup logger + zapLogger, err := zap.NewDevelopment() + if err != nil { + t.Errorf("Failed to initialize logger: %s", err) + } + logger := zapLogger.Sugar() + // Load git state - state, err := LoadGitStateConfig(c.options.ciSystem) + state, err := LoadGitStateConfig(logger, c.options.ciSystem) if err != nil && !c.expectError { t.Errorf("unexpected error occured %s", err) } @@ -436,6 +486,13 @@ func Test_determineCISystem(t *testing.T) { }, ciSystem: GithubActions, }, + { + name: "detect running in jenkins", + env: mockEnv{ + "JENKINS_HOME": "/some/absolute/path", + }, + ciSystem: Jenkins, + }, { name: "unknown ci system", env: mockEnv{ @@ -463,4 +520,4 @@ func Test_determineCISystem(t *testing.T) { } }) } -} +} \ No newline at end of file diff --git a/cmd/image-builder/main.go b/cmd/image-builder/main.go index 351eacea1119..0c645c4848b0 100644 --- a/cmd/image-builder/main.go +++ b/cmd/image-builder/main.go @@ -869,14 +869,16 @@ func main() { // If running inside some CI system, determine which system is used if o.isCI { - ciSystem, err := DetermineUsedCISystem() + o.ciSystem, err = DetermineUsedCISystem() if err != nil { - log.Fatalf("Failed to determine current ci system: %s", err) + o.logger.Errorw("Failed to determine current ci system", "error", err) + os.Exit(1) } - o.ciSystem = ciSystem - o.gitState, err = LoadGitStateConfig(ciSystem) + + o.gitState, err = LoadGitStateConfig(o.logger, o.ciSystem) if err != nil { - log.Fatalf("Failed to load current git state: %s", err) + o.logger.Errorw("Failed to load current git state", "error", err) + os.Exit(1) } o.logger.Debugw("Git state loaded", "gitState", o.gitState) @@ -922,12 +924,13 @@ func main() { if o.buildInADO { err = buildInADO(o) if err != nil { - fmt.Printf("Image build failed with error: %s\n", err) + o.logger.Errorw("Image build failed", "error", err, "JobType", o.gitState.JobType) os.Exit(1) } os.Exit(0) } + o.logger.Warnw("Local build is deprecated and will be removed soon, the tool will not support local building anymore. Please migrate to the ADO build backend.") err = buildLocally(o) if err != nil { fmt.Println(err)