241 lines
6.4 KiB
Go
241 lines
6.4 KiB
Go
// Copyright 2022 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 plugin
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.kle.li/woodpecker/plugin-lib/common"
|
|
"git.kle.li/woodpecker/plugin-sonar/pkg/sonar"
|
|
"github.com/pelletier/go-toml"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type (
|
|
Plugin struct {
|
|
Config *sonar.Config
|
|
BuildData *common.BuildData
|
|
Logger *zap.SugaredLogger
|
|
}
|
|
|
|
args struct {
|
|
list []string
|
|
logger *zap.SugaredLogger
|
|
}
|
|
)
|
|
|
|
func (p *Plugin) AnalyzeBranch() bool {
|
|
return p.Config.BranchAnalysis == "true" || (p.Config.BranchAnalysis == "auto" && !p.SourceBranchIsDefault())
|
|
}
|
|
|
|
func (p *Plugin) SourceBranchIsDefault() bool {
|
|
return p.BuildData.SourceBranch() == p.BuildData.Metadata.Repo.Branch || len(p.BuildData.SourceBranch()) == 0
|
|
}
|
|
|
|
func (p *Plugin) getAuth() *sonar.Authentication {
|
|
return &sonar.Authentication{
|
|
Token: p.Config.Token,
|
|
}
|
|
}
|
|
|
|
func (p *Plugin) Exec() {
|
|
|
|
p.prepareVersion()
|
|
p.runScanner()
|
|
|
|
report := p.staticScanToReport()
|
|
|
|
p.Logger.Infow("job url", "url", report.CeTaskURL)
|
|
|
|
task := sonar.WaitForSonarJob(report, p.getAuth(), p.Config)
|
|
|
|
p.Logger.Infow("Job finished", "report", report)
|
|
|
|
status := sonar.GetStatus(task, report, p.getAuth(), p.Config)
|
|
p.Logger.Infow("status received", "status", status, "dashboardURL", p.Config.GetProjectDashUrl())
|
|
|
|
p.Logger.Infow("Woodpecker SonarQube Plugin",
|
|
"qualityEnabled", p.Config.QualityEnabled)
|
|
|
|
if p.Config.QualityEnabled {
|
|
p.Logger.Info("QualityGate ENABLED")
|
|
|
|
if status != p.Config.Quality {
|
|
p.Logger.Fatal("QualityGate status FAILED",
|
|
"targetQuality", p.Config.Quality,
|
|
"qualityResult", status)
|
|
} else {
|
|
p.Logger.Infow("QualityGate PASSED", "status", status)
|
|
}
|
|
}
|
|
|
|
if !p.Config.QualityEnabled {
|
|
p.Logger.Info("QualityGate DISABLED only displaying information")
|
|
if status != p.Config.Quality {
|
|
p.Logger.Infow("QualityGate status FAILED", "status", status)
|
|
} else {
|
|
p.Logger.Infow("QualityGate status PASSED", "status", status)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func (p *Plugin) runScanner() {
|
|
scannerArgs := p.generateScannerArgs()
|
|
|
|
p.Logger.Debugw("Prepping env SONAR_USER_HOME")
|
|
os.Setenv("SONAR_USER_HOME", ".sonar")
|
|
|
|
p.Logger.Infow("executing sonar-scanner", "options", strings.Join(scannerArgs, " "))
|
|
|
|
cmd := exec.Command("sonar-scanner", scannerArgs...)
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
|
|
err := cmd.Run()
|
|
|
|
if err != nil {
|
|
p.Logger.Fatalw("executing sonarscanner failed", "error", err)
|
|
}
|
|
}
|
|
|
|
func (p *Plugin) generateScannerArgs() []string {
|
|
args := &args{
|
|
list: []string{
|
|
"-Dsonar.host.url=" + p.Config.HostURL,
|
|
"-Dsonar.login=" + p.Config.Token,
|
|
},
|
|
logger: p.Logger,
|
|
}
|
|
|
|
if !p.Config.UsingProperties {
|
|
argsParameter := []string{
|
|
"-Dsonar.projectKey=" + p.Config.Key,
|
|
"-Dsonar.sources=" + p.Config.Sources,
|
|
"-Dsonar.ws.timeout=" + strconv.Itoa(p.Config.HttpTimeout),
|
|
"-Dsonar.log.level=" + p.Config.SonarLogLevel,
|
|
"-Dsonar.showProfiling=" + p.Config.ShowProfiling,
|
|
"-Dsonar.scm.provider=" + p.BuildData.AdditionalData.SCM,
|
|
}
|
|
args.append(argsParameter...)
|
|
|
|
args.appendIfNotEmpty("-Dsonar.projectName=", p.Config.Name)
|
|
args.appendIfNotEmpty("-Dsonar.projectVersion=", p.Config.Version)
|
|
args.appendIfNotEmpty("-Dsonar.java.binaries=", p.Config.Binaries)
|
|
args.appendIfNotEmpty("-Dsonar.exclusions=", p.Config.Exclusions)
|
|
args.appendIfNotEmpty("-Dsonar.inclusions=", p.Config.Inclusions)
|
|
}
|
|
|
|
p.Logger.Debugw("argsbuilder",
|
|
"analyze", p.AnalyzeBranch(),
|
|
"isPr", p.BuildData.IsPR(),
|
|
"isTag", p.BuildData.IsTag(),
|
|
"source", p.BuildData.SourceBranch(),
|
|
"target", p.BuildData.TargetBranch(),
|
|
"prKey", p.BuildData.PullRequest())
|
|
|
|
if p.AnalyzeBranch() && !p.BuildData.IsPR() {
|
|
args.appendMustIfNotEmpty("-Dsonar.branch.name=", p.BuildData.TargetBranch())
|
|
}
|
|
|
|
if p.AnalyzeBranch() && p.BuildData.IsPR() {
|
|
args.appendMustIfNotEmpty("-Dsonar.pullrequest.branch=", p.BuildData.SourceBranch())
|
|
args.appendMustIfNotEmpty("-Dsonar.pullrequest.base=", p.BuildData.TargetBranch())
|
|
args.appendMustIfNotEmpty("-Dsonar.pullrequest.key=", p.BuildData.PullRequest())
|
|
}
|
|
|
|
args.appendIfNotEmpty("-Dsonar.qualitygate.wait=", strconv.FormatBool(p.Config.QualityEnabled))
|
|
args.appendIfNotEmpty("-Dsonar.qualitygate.timeout=", strconv.Itoa(p.Config.QualityTimeout))
|
|
|
|
validArg := regexp.MustCompile(".*=.+")
|
|
for _, argument := range args.list {
|
|
if !validArg.Match([]byte(argument)) {
|
|
p.Logger.Fatalf("verifying generateScannerArgs failed: %s does not have a value assigned", argument)
|
|
}
|
|
}
|
|
|
|
return args.list
|
|
}
|
|
|
|
func (p *Plugin) staticScanToReport() *sonar.Report {
|
|
|
|
var reportLocation = ".scannerwork/report-task.txt"
|
|
|
|
fileData, err := os.ReadFile(reportLocation)
|
|
if err != nil {
|
|
p.Logger.Fatalw("failed to access report-task file",
|
|
"filepath", reportLocation,
|
|
"error", err)
|
|
}
|
|
|
|
replacer := regexp.MustCompile("(?m)^(.*?)=(.*)($)")
|
|
data := replacer.ReplaceAllString(string(fileData), "$1=\"$2\"")
|
|
|
|
report := sonar.Report{}
|
|
|
|
if err = toml.Unmarshal([]byte(data), &report); err != nil {
|
|
p.Logger.Fatalw("failed to unmarshal task-report toml to Report", "error", err)
|
|
}
|
|
|
|
return &report
|
|
}
|
|
|
|
func (p *Plugin) prepareVersion() {
|
|
if len(p.Config.Version) > 0 {
|
|
return
|
|
}
|
|
|
|
if p.BuildData.IsTag() {
|
|
p.Config.Version = p.BuildData.Tag()
|
|
p.Logger.Infow("Version unset updating it",
|
|
"action", "tag",
|
|
"version", p.Config.Version)
|
|
return
|
|
}
|
|
|
|
if p.BuildData.IsPR() {
|
|
p.Config.Version = p.BuildData.SourceBranch()
|
|
p.Logger.Infow("Version unset updating it",
|
|
"action", "pull request",
|
|
"version", p.Config.Version)
|
|
return
|
|
}
|
|
|
|
p.Config.Version = p.BuildData.TargetBranch()
|
|
}
|
|
|
|
func (a *args) appendMustIfNotEmpty(prefix string, value string) {
|
|
if len(value) > 0 {
|
|
a.append(prefix + value)
|
|
} else {
|
|
a.logger.Fatalw("error appending argument, value empty",
|
|
"prefix", prefix)
|
|
}
|
|
}
|
|
|
|
func (a *args) appendIfNotEmpty(prefix string, value string) {
|
|
if len(value) > 0 {
|
|
a.append(prefix + value)
|
|
}
|
|
}
|
|
|
|
func (a *args) append(elems ...string) {
|
|
a.list = append(a.list, elems...)
|
|
}
|