plugin-sonar/main.go

253 lines
6.7 KiB
Go

// Copyright 2021 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 main
import (
"fmt"
"log"
"os"
"time"
plugLib "git.kle.li/gapodo/woodpecker-plugin-lib/urfave"
"git.kle.li/gapodo/woodpecker-plugin-sonar/pkg/plugin"
"git.kle.li/gapodo/woodpecker-plugin-sonar/pkg/sonar"
"github.com/urfave/cli/v2"
"go.uber.org/zap"
)
var appVersion = "v0.0.1"
func main() {
app := &cli.App{
Name: "Woodpecker-SonarQube-Plugin",
Usage: "Trigger SonarQube scan from a Woodpecker-CI pipeline",
Version: appVersion,
Compiled: time.Now(),
Authors: []*cli.Author{
{
Name: "Michael Amann [@gapodo:kle.li]",
Email: "gapodo@geekvoid.net",
},
},
Copyright: "(c) 2022 Michael Amann",
Before: func(ctx *cli.Context) error {
fmt.Fprintf(ctx.App.Writer, "Woodpecker-CI SonarQube plugin\n")
return nil
},
After: func(ctx *cli.Context) error {
fmt.Fprintf(ctx.App.Writer, "Exiting Woodpecker-CI SonarQube plugin\n")
return nil
},
Action: run,
Flags: Flags(),
}
app.Run(os.Args)
}
func Flags() []cli.Flag {
flags := []cli.Flag{
&cli.StringFlag{
Name: "logLevel",
Usage: "plugin log level (debug, info, warn, error, dpanic, panic, and fatal)",
Value: "info",
EnvVars: []string{"PLUGIN_LOG_LEVEL"},
},
&cli.StringFlag{
Name: "key",
Usage: "SonarQube project key",
EnvVars: []string{"PLUGIN_SONAR_KEY"},
},
&cli.StringFlag{
Name: "name",
Usage: "SonarQube project name",
EnvVars: []string{"PLUGIN_SONAR_NAME"},
},
&cli.StringFlag{
Name: "host-url",
Usage: "SonarQube host url",
EnvVars: []string{"PLUGIN_SONAR_URL"},
},
&cli.StringFlag{
Name: "token",
Usage: "SonarQube analysis token",
EnvVars: []string{"PLUGIN_SONAR_TOKEN"},
},
&cli.StringFlag{
Name: "ver",
Usage: "Project version",
EnvVars: []string{"PLUGIN_SONAR_VERSION"},
},
&cli.IntFlag{
Name: "http-timeout",
Usage: "Web request timeout",
Value: 300,
EnvVars: []string{"PLUGIN_TIMEOUT"},
},
&cli.StringFlag{
Name: "sources",
Usage: "analysis sources",
Value: ".",
EnvVars: []string{"PLUGIN_SOURCES"},
},
&cli.StringFlag{
Name: "inclusions",
Usage: "code inclusions",
EnvVars: []string{"PLUGIN_INCLUSIONS"},
},
&cli.StringFlag{
Name: "exclusions",
Usage: "code exclusions",
EnvVars: []string{"PLUGIN_EXCLUSIONS"},
},
&cli.StringFlag{
Name: "sonar-log-Level",
Usage: "sonar-scanner log level (as per sonar-scanner)",
Value: "INFO",
EnvVars: []string{"PLUGIN_SONAR_LOG_LEVEL"},
},
&cli.BoolFlag{
Name: "showProfiling",
Usage: "showProfiling during analysis",
Value: false,
EnvVars: []string{"PLUGIN_SHOWPROFILING"},
},
&cli.StringFlag{
Name: "branchAnalysis",
Usage: "execute branchAnalysis (true, auto, false)",
EnvVars: []string{"PLUGIN_BRANCHANALYSIS"},
Value: "false",
},
&cli.StringFlag{
Name: "usingProperties",
Usage: "using sonar-project.properties",
EnvVars: []string{"PLUGIN_USINGPROPERTIES"},
Value: "false",
},
&cli.StringFlag{
Name: "binaries",
Usage: "Java Binaries",
EnvVars: []string{"PLUGIN_BINARIES"},
},
&cli.StringFlag{
Name: "quality-target",
Usage: "Quality Gate target",
EnvVars: []string{"PLUGIN_QUALITYGATE"},
Value: "OK",
},
&cli.BoolFlag{
Name: "quality-gate-enabled",
Usage: "true or false - stop pipeline if sonar quality gate conditions are not met",
Value: true,
EnvVars: []string{"PLUGIN_SONAR_QUALITY_ENABLED"},
},
&cli.IntFlag{
Name: "quality-gate-timeout",
Usage: "number in seconds for timeout",
Value: 300,
EnvVars: []string{"PLUGIN_SONAR_QUALITY_GATE_TIMEOUT"},
},
&cli.StringFlag{
Name: "artifact-file",
Usage: "Artifact file location that will be generated by the plugin. This file will include information of docker images that are uploaded by the plugin.",
Value: "artifact.json",
EnvVars: []string{"PLUGIN_ARTIFACT_FILE"},
},
}
flags = append(flags, plugLib.BuildFlags()...)
return flags
}
func run(ctx *cli.Context) error {
slog := setupSugaredLogger(ctx.String("logLevel"))
slog.Infow("SonarQube plugin for Woodpecker", "version", appVersion)
slog.Infow("Parsing values from env")
plugin := pluginFromContext(ctx, slog)
slog.Infow("Starting scanner")
plugin.Exec()
return nil
}
func pluginFromContext(ctx *cli.Context, logger *zap.SugaredLogger) plugin.Plugin {
config := sonar.Config{
Key: ctx.String("key"),
Name: ctx.String("name"),
HostURL: ctx.String("host-url"),
Token: ctx.String("token"),
Version: ctx.String("ver"),
HttpTimeout: ctx.Int("http-timeout"),
Sources: ctx.String("sources"),
Inclusions: ctx.String("inclusions"),
Exclusions: ctx.String("exclusions"),
SonarLogLevel: ctx.String("sonar-log-Level"),
ShowProfiling: ctx.String("showProfiling"),
BranchAnalysis: ctx.String("branchAnalysis"),
UsingProperties: ctx.Bool("usingProperties"),
Binaries: ctx.String("binaries"),
Quality: ctx.String("quality-target"),
QualityEnabled: ctx.Bool("quality-gate-enabled"),
QualityTimeout: ctx.Int("quality-gate-timeout"),
ArtifactFile: ctx.String("artifact-file"),
Logger: logger,
}
bdata := plugLib.BuildDataFromContext(ctx, nil)
plug := plugin.Plugin{
Config: &config,
BuildData: &bdata,
Logger: logger,
}
return plug
}
func setupSugaredLogger(level string) *zap.SugaredLogger {
fmt.Printf("loglevel %s", level)
alvl, err := zap.ParseAtomicLevel(level)
if err != nil {
log.Fatal(fmt.Errorf("provided plugin log level failed to parse \"%s\": %w", level, err))
}
conf := zap.NewProductionConfig()
conf.EncoderConfig.TimeKey = "" // don't print a timestamp
conf.EncoderConfig.CallerKey = "" // don't show caller
conf.Level = alvl
if conf.Level.Level() == zap.DebugLevel {
conf.EncoderConfig.CallerKey = "caller"
conf.EncoderConfig.FunctionKey = "func"
}
logger := zap.Must(conf.Build())
defer logger.Sync()
sugar := logger.Sugar()
sugar.Infow("Logger setup", "levelConf", level)
sugar.Debugw("debug level enabled")
return sugar
}