internal/core/corescan/context_changes.go (143 lines of code) (raw):

/* * Copyright 2021-2024 JetBrains s.r.o. * * 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 * * https://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 corescan import ( "fmt" "path/filepath" "strings" "github.com/JetBrains/qodana-cli/internal/core/startup" "github.com/JetBrains/qodana-cli/internal/platform/qdenv" "github.com/JetBrains/qodana-cli/internal/platform/strutil" ) // All changes of Context must be defined clearly by usecase and business logic // not just "builder" type functions, but concrete functions related to some business logic // Why? We want to restrict changes at all, for details see doc on Context // // package-internal functions can be "builder" type, like withEnv func (c Context) WithVcsEnvForFullHistoryAnalysisIteration(remoteUrl string, branch string, revision string) Context { return c. withEnv(qdenv.QodanaRemoteUrl, remoteUrl, true). withEnv(qdenv.QodanaBranch, branch, true). withEnv(qdenv.QodanaRevision, revision, true) } func (c Context) WithEnvExtractedFromOsEnv(key string, value string) Context { return c.withEnv(key, value, false) } func (c Context) BackoffToDefaultAnalysisBecauseOfMissingCommit() Context { c.commit = "" c.diffStart = "" c.diffEnd = "" c.fullHistory = false c.forceLocalChangesScript = false c.script = "" return c } func (c Context) FirstStageOfScopedScript(scopeFile string) Context { c.script = strutil.QuoteForWindows("scoped:" + scopeFile) startDir := filepath.Join(c.ResultsDir(), "start") c = c.prepareContext( true, "-Dqodana.skip.result=true", // don't print results "-Dqodana.skip.coverage.computation=true", // don't compute coverage on first pass ) c.baseline = "" c.resultsDir = startDir startup.MakeDirAll(c.LogDir()) // need to prepare new result and log dir return c } func (c Context) SecondStageOfScopedScript(scopeFile string, startSarif string) Context { c.script = strutil.QuoteForWindows("scoped:" + scopeFile) endDir := filepath.Join(c.ResultsDir(), "end") c = c.prepareContext( false, "-Dqodana.skip.preamble=true", // don't print the QD logo again "-Didea.headless.enable.statistics=false", // disable statistics for second run fmt.Sprintf("-Dqodana.scoped.baseline.path=%s", startSarif), // disable statistics for second run "-Dqodana.skip.coverage.issues.reporting=true", // don't report coverage issues on the second pass, but allow numbers to be computed ) c.resultsDir = endDir startup.MakeDirAll(c.LogDir()) // need to prepare new result and log dir return c } func (c Context) FirstStageOfReverseScopedScript(scopeFile string) Context { c.script = strutil.QuoteForWindows("reverse-scoped:NEW," + scopeFile) startDir := filepath.Join(c.ResultsDir(), "start") properties := []string{"-Dqodana.skip.result.strategy=ANY"} // finish only in case of none issues found if !c.BaselineIncludeAbsent() { reducedScope := filepath.Join(startDir, "reduced-scope.json") properties = append(properties, "-Dqodana.reduced.scope.path="+reducedScope) c.reducedScopePath = reducedScope } c = c.prepareContext(true, properties...) c.resultsDir = startDir startup.MakeDirAll(c.LogDir()) // need to prepare new result and log dir return c } func (c Context) SecondStageOfReverseScopedScript(scopeFile string, startSarif string) Context { c.script = strutil.QuoteForWindows("reverse-scoped:OLD," + scopeFile) endDir := filepath.Join(c.ResultsDir(), "end") resultStrategy := "FIXABLE" // continue if any fixable issues found if !c.applyFixes && !c.cleanup { resultStrategy = "NEVER" // finish right afterwards } properties := []string{ "-Dqodana.skip.preamble=true", // don't print the QD logo again "-Didea.headless.enable.statistics=false", // disable statistics for second run fmt.Sprintf("-Dqodana.skip.result.strategy=%s", resultStrategy), fmt.Sprintf("-Dqodana.scoped.baseline.path=%s", startSarif), } c = c.prepareContext(true, properties...) c.resultsDir = endDir startup.MakeDirAll(c.LogDir()) // need to prepare new result and log dir return c } func (c Context) ThirdStageOfReverseScopedScript(scopeFile string, startSarif string) Context { c.script = strutil.QuoteForWindows("reverse-scoped:FIXES," + scopeFile) endDir := filepath.Join(c.ResultsDir(), "fixes") properties := []string{ "-Dqodana.skip.preamble=true", // don't print the QD logo again "-Didea.headless.enable.statistics=false", // disable statistics for second run "-Dqodana.skip.result.strategy=NEVER", // finish right afterwards fmt.Sprintf("-Dqodana.scoped.baseline.path=%s", startSarif), } c = c.prepareContext(false, properties...) c.resultsDir = endDir startup.MakeDirAll(c.LogDir()) // need to prepare new result and log dir return c } func (c Context) ForcedLocalChanges() Context { c.script = "local-changes" return c } // WithEffectiveConfigurationDirOnRevision // in diff-start, diff-end scenario, for analysis of revision, we reset only // effectiveConfigurationDir (which is used by IJ), fields used by CLI besides bootstrap stay the same func (c Context) WithEffectiveConfigurationDirOnRevision(effectiveConfigurationDir string) Context { c.effectiveConfigurationDir = effectiveConfigurationDir return c } func (c Context) withAddedProperties(propertiesToAdd ...string) Context { props := c.Property() props = append(props, propertiesToAdd...) c._property = props return c } func (c Context) withEnv(key string, value string, override bool) Context { currentEnvs := c.Env() envs := make([]string, 0) for _, e := range currentEnvs { isEnvAlreadySet := strings.HasPrefix(e, key) && value != "" if isEnvAlreadySet && !override { return c } if !isEnvAlreadySet { envs = append(envs, e) } } if value != "" { envs = append(envs, fmt.Sprintf("%s=%s", key, value)) } c._env = envs return c } func (c Context) prepareContext(skipFixes bool, propertiesToAdd ...string) Context { c = c.withAddedProperties(propertiesToAdd...) c.showReport = false c.saveReport = false if skipFixes { c.applyFixes = false c.cleanup = false c.fixesStrategy = "none" // this option is deprecated, but the only way to overwrite the possible yaml value } return c }