mirror of
https://github.com/harness-community/drone-nexus-publish.git
synced 2026-06-04 18:23:55 +08:00
nexus plugin changes
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.env
|
||||
release/
|
||||
drone-nexus-publish
|
||||
Generated
+60
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="ALL" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="eaf74dd4-cc6e-4452-911f-10e1bd1a367c" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="GOROOT" url="file://$USER_HOME$/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.22.7.linux-amd64" />
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="MarkdownSettingsMigration">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 0
|
||||
}</component>
|
||||
<component name="ProjectId" id="2oZ333xeDu2iiAyxiIoHbhGAcfW" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.go.formatter.settings.were.checked": "true",
|
||||
"RunOnceActivity.go.migrated.go.modules.settings": "true",
|
||||
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
|
||||
"git-widget-placeholder": "drone-nexus-publish-senthil-02",
|
||||
"go.import.settings.migrated": "true",
|
||||
"go.sdk.automatically.set": "true",
|
||||
"last_opened_file_path": "/opt/hns/harness-plugins/nexus/drone-nexus-publish",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-gosdk-2946fb9b3188-155fe4b6e3a0-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-233.15619.13" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="VgoProject">
|
||||
<settings-migrated>true</settings-migrated>
|
||||
</component>
|
||||
</project>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
# Blue Oak Model License
|
||||
|
||||
Version 1.0.0
|
||||
|
||||
## Purpose
|
||||
|
||||
This license gives everyone as much permission to work with
|
||||
this software as possible, while protecting contributors
|
||||
from liability.
|
||||
|
||||
## Acceptance
|
||||
|
||||
In order to receive this license, you must agree to its
|
||||
rules. The rules of this license are both obligations
|
||||
under that agreement and conditions to your license.
|
||||
You must not do anything with this software that triggers
|
||||
a rule that you cannot or will not follow.
|
||||
|
||||
## Copyright
|
||||
|
||||
Each contributor licenses you to do everything with this
|
||||
software that would otherwise infringe that contributor's
|
||||
copyright in it.
|
||||
|
||||
## Notices
|
||||
|
||||
You must ensure that everyone who gets a copy of
|
||||
any part of this software from you, with or without
|
||||
changes, also gets the text of this license or a link to
|
||||
<https://blueoakcouncil.org/license/1.0.0>.
|
||||
|
||||
## Excuse
|
||||
|
||||
If anyone notifies you in writing that you have not
|
||||
complied with [Notices](#notices), you can keep your
|
||||
license by taking all practical steps to comply within 30
|
||||
days after the notice. If you do not do so, your license
|
||||
ends immediately.
|
||||
|
||||
## Patent
|
||||
|
||||
Each contributor licenses you to do everything with this
|
||||
software that would otherwise infringe any patent claims
|
||||
they can license or become able to license.
|
||||
|
||||
## Reliability
|
||||
|
||||
No contributor can revoke this license.
|
||||
|
||||
## No Liability
|
||||
|
||||
***As far as the law allows, this software comes as is,
|
||||
without any warranty or condition, and no contributor
|
||||
will be liable to anyone for any damages related to this
|
||||
software or this license, under any kind of legal claim.***
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM alpine:3.6 as alpine
|
||||
RUN apk add -U --no-cache ca-certificates
|
||||
|
||||
FROM alpine:3.6
|
||||
ENV GODEBUG netdns=go
|
||||
|
||||
COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
ADD release/linux/amd64/drone-nexus-publish /bin/
|
||||
ENTRYPOINT ["/bin/drone-nexus-publish"]
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM alpine:3.6 as alpine
|
||||
RUN apk add -U --no-cache ca-certificates
|
||||
|
||||
FROM alpine:3.6
|
||||
ENV GODEBUG netdns=go
|
||||
|
||||
COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
ADD release/linux/arm/plugin /bin/
|
||||
ENTRYPOINT ["/bin/plugin"]
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM alpine:3.6 as alpine
|
||||
RUN apk add -U --no-cache ca-certificates
|
||||
|
||||
FROM alpine:3.6
|
||||
ENV GODEBUG netdns=go
|
||||
|
||||
COPY --from=alpine /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
ADD release/linux/arm64/plugin /bin/
|
||||
ENTRYPOINT ["/bin/plugin"]
|
||||
@@ -0,0 +1,31 @@
|
||||
image: lhns/nexus-publish:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||
{{#if build.tags}}
|
||||
tags:
|
||||
{{#each build.tags}}
|
||||
- {{this}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
manifests:
|
||||
-
|
||||
image: lhns/nexus-publish:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||
platform:
|
||||
architecture: amd64
|
||||
os: linux
|
||||
-
|
||||
image: lhns/nexus-publish:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||
platform:
|
||||
variant: v8
|
||||
architecture: arm64
|
||||
os: linux
|
||||
-
|
||||
image: lhns/nexus-publish:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
variant: v7
|
||||
architecture: arm
|
||||
os: linux
|
||||
-
|
||||
image: lhns/nexus-publish:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||
platform:
|
||||
variant: v6
|
||||
architecture: arm
|
||||
os: linux
|
||||
@@ -0,0 +1 @@
|
||||
UPLOAD_STATUS=Success
|
||||
@@ -0,0 +1,12 @@
|
||||
module github.com/harness-community/drone-nexus-publish
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/datadrivers/go-nexus-client v1.13.0
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
@@ -0,0 +1,39 @@
|
||||
github.com/datadrivers/go-nexus-client v1.13.0 h1:8/EMpdfUImdmMLCUf/KGB2883Ryw6pAfAMtjyLQKlGE=
|
||||
github.com/datadrivers/go-nexus-client v1.13.0/go.mod h1:sPjBOxF7idUoiJoa730L3JyKZodjT0LDAvVF8u4kOLU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
|
||||
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020 the Drone Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Blue Oak Model License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/harness-community/drone-nexus-publish/plugin"
|
||||
plg "github.com/harness-community/drone-nexus-publish/plugin"
|
||||
"github.com/kelseyhightower/envconfig"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logrus.SetFormatter(new(formatter))
|
||||
|
||||
var args plg.Args
|
||||
if err := envconfig.Process("", &args); err != nil {
|
||||
logrus.Fatalln(err)
|
||||
}
|
||||
|
||||
switch args.Level {
|
||||
case "debug":
|
||||
logrus.SetFormatter(textFormatter)
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
case "trace":
|
||||
logrus.SetFormatter(textFormatter)
|
||||
logrus.SetLevel(logrus.TraceLevel)
|
||||
}
|
||||
|
||||
if _, err := plugin.Exec(context.Background(), args); err != nil {
|
||||
logrus.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
// default formatter that writes logs without including timestamp
|
||||
// or level information.
|
||||
type formatter struct{}
|
||||
|
||||
func (*formatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
return []byte(entry.Message), nil
|
||||
}
|
||||
|
||||
// text formatter that writes logs with level information
|
||||
var textFormatter = &logrus.TextFormatter{
|
||||
DisableTimestamp: true,
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package plugin
|
||||
|
||||
type Plugin interface {
|
||||
Init(args *Args) error
|
||||
SetBuildRoot(buildRootPath string) error
|
||||
DeInit() error
|
||||
ValidateAndProcessArgs(args Args) error
|
||||
DoPostArgsValidationSetup(args Args) error
|
||||
Run() error
|
||||
WriteOutputVariables() error
|
||||
PersistResults() error
|
||||
IsQuiet() bool
|
||||
InspectProcessArgs(argNamesList []string) (map[string]interface{}, error)
|
||||
}
|
||||
|
||||
type Args struct {
|
||||
Pipeline
|
||||
EnvPluginInputArgs
|
||||
Level string `envconfig:"PLUGIN_LOG_LEVEL"`
|
||||
}
|
||||
|
||||
type EnvPluginInputArgs struct {
|
||||
NexusVersion string `envconfig:"PLUGIN_NEXUS_VERSION"`
|
||||
Protocol string `envconfig:"PLUGIN_PROTOCOL"`
|
||||
GroupId string `envconfig:"PLUGIN_GROUP_ID"`
|
||||
Repository string `envconfig:"PLUGIN_REPOSITORY"`
|
||||
Artifact string `envconfig:"PLUGIN_ARTIFACTS"`
|
||||
Username string `envconfig:"PLUGIN_USERNAME"`
|
||||
Password string `envconfig:"PLUGIN_PASSWORD"`
|
||||
|
||||
// For backward compatibility
|
||||
ServerUrl string `envconfig:"PLUGIN_SERVER_URL"`
|
||||
Filename string `envconfig:"PLUGIN_FILENAME"`
|
||||
Format string `envconfig:"PLUGIN_FORMAT"`
|
||||
Attributes string `envconfig:"PLUGIN_ATTRIBUTES"`
|
||||
}
|
||||
|
||||
type Artifact struct {
|
||||
File string `yaml:"file"`
|
||||
Classifier string `yaml:"classifier"`
|
||||
ArtifactId string `yaml:"artifactId"`
|
||||
Type string `yaml:"type"`
|
||||
Version string `yaml:"version"`
|
||||
GroupId string `yaml:"groupId"`
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type HttpClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
type NexusPlugin struct {
|
||||
InputArgs *Args
|
||||
IsMultiFileUpload bool
|
||||
PluginProcessingInfo
|
||||
NexusPluginResponse
|
||||
HttpClient HttpClient
|
||||
}
|
||||
|
||||
type PluginProcessingInfo struct {
|
||||
UserName string
|
||||
Password string
|
||||
ServerUrl string
|
||||
Version string
|
||||
Format string
|
||||
Repository string
|
||||
GroupId string
|
||||
Artifacts []Artifact
|
||||
}
|
||||
|
||||
type NexusPluginResponse struct {
|
||||
Failed []FailedArtifact `json:"failed"`
|
||||
}
|
||||
|
||||
type FailedArtifact struct {
|
||||
File string `json:"file"`
|
||||
ArtifactId string `json:"artifactId"`
|
||||
Err string `json:"err"`
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) Run() error {
|
||||
LogPrintln(n, "Starting Nexus Plugin Run")
|
||||
|
||||
if n.HttpClient == nil {
|
||||
n.HttpClient = &http.Client{}
|
||||
}
|
||||
|
||||
for _, artifact := range n.Artifacts {
|
||||
filePath := artifact.File
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
n.addFailedArtifact(artifact, fmt.Sprintf("could not open file: %v", err))
|
||||
continue
|
||||
}
|
||||
|
||||
if n.Version == "nexus2" {
|
||||
artifactURL := n.prepareNexus2ArtifactURL(artifact)
|
||||
if err := n.uploadFileNexus2(artifactURL, file, filePath); err != nil {
|
||||
n.addFailedArtifact(artifact, fmt.Sprintf("upload failed: %v", err))
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error closing file: ", err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else if n.Version == "nexus3" {
|
||||
if err := n.uploadFileNexus3(artifact, filePath); err != nil {
|
||||
n.addFailedArtifact(artifact, fmt.Sprintf("upload failed: %v", err))
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error closing file: ", err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error closing file: ", err.Error())
|
||||
}
|
||||
|
||||
fmt.Println("Successfully uploaded artifact:", filePath)
|
||||
}
|
||||
|
||||
if len(n.Failed) > 0 {
|
||||
return GetNewError("NexusPlugin Error in Run: some artifacts failed to upload")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) WriteOutputVariables() error {
|
||||
|
||||
type EnvKvPair struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
var kvPairs []EnvKvPair
|
||||
|
||||
if len(n.Failed) == 0 {
|
||||
LogPrintln(n, "All artifacts uploaded successfully")
|
||||
kvPairs = append(kvPairs, EnvKvPair{Key: "UPLOAD_STATUS", Value: "Success"})
|
||||
} else {
|
||||
kvPairs = append(kvPairs, EnvKvPair{Key: "UPLOAD_STATUS", Value: n.Failed})
|
||||
}
|
||||
|
||||
var retErr error = nil
|
||||
|
||||
for _, kvPair := range kvPairs {
|
||||
err := WriteEnvVariableAsString(kvPair.Key, kvPair.Value)
|
||||
if err != nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
|
||||
return retErr
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) Init(args *Args) error {
|
||||
n.InputArgs = args
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) SetBuildRoot(buildRootPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) DeInit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) ValidateAndProcessArgs(args Args) error {
|
||||
LogPrintln(n, "NexusPlugin BuildAndValidateArgs")
|
||||
|
||||
err := n.DetermineIsMultiFileUpload(args)
|
||||
if err != nil {
|
||||
LogPrintln(n, "NexusPlugin Error in ValidateAndProcessArgs: "+err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if n.IsMultiFileUpload {
|
||||
err = n.IsMultiFileUploadArgsOk(args)
|
||||
if err != nil {
|
||||
LogPrintln(n, "NexusPlugin Error in ValidateAndProcessArgs: "+err.Error())
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = n.IsSingleFileUploadArgsOk(args)
|
||||
if err != nil {
|
||||
LogPrintln(n, "NexusPlugin Error in ValidateAndProcessArgs: "+err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) DetermineIsMultiFileUpload(args Args) error {
|
||||
LogPrintln(n, "NexusPlugin DetermineIsMultiFileUpload")
|
||||
|
||||
switch {
|
||||
case args.Attributes != "" && args.Artifact == "":
|
||||
n.IsMultiFileUpload = false
|
||||
case args.Artifact != "" && args.Attributes == "":
|
||||
n.IsMultiFileUpload = true
|
||||
case args.Attributes == "" && args.Artifact == "":
|
||||
return GetNewError("Error in DetermineCompatibilityMode: both 'Attributes' and 'Artifact' cannot be empty")
|
||||
default:
|
||||
return GetNewError("Error in DetermineCompatibilityMode: both 'Attributes' and 'Artifact' provided, which is ambiguous")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) IsMultiFileUploadArgsOk(args Args) error {
|
||||
LogPrintln(n, "NexusPlugin IsMultiFileUploadArgsOk")
|
||||
|
||||
requiredArgs := map[string]string{
|
||||
"username": args.Username,
|
||||
"password": args.Password,
|
||||
"protocol": args.Protocol,
|
||||
"nexusUrl": args.ServerUrl,
|
||||
"nexusVersion": args.NexusVersion,
|
||||
"repository": args.Repository,
|
||||
"groupId": args.GroupId,
|
||||
"format": args.Format,
|
||||
}
|
||||
|
||||
for field, value := range requiredArgs {
|
||||
if value == "" {
|
||||
return GetNewError("Error in IsMultiFileUploadArgsOk: " + field + " cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
n.UserName = args.Username
|
||||
n.Password = args.Password
|
||||
n.Repository = args.Repository
|
||||
n.ServerUrl = args.Protocol + "://" + args.ServerUrl
|
||||
n.GroupId = args.GroupId
|
||||
n.Version = args.NexusVersion
|
||||
n.Format = args.Format
|
||||
|
||||
// Unmarshalling YAML artifact data
|
||||
var artifacts []Artifact
|
||||
if err := yaml.Unmarshal([]byte(args.Artifact), &artifacts); err != nil {
|
||||
return GetNewError("Error in IsMultiFileUploadArgsOk: Error decoding YAML: " + err.Error())
|
||||
}
|
||||
|
||||
var filteredArtifacts []Artifact
|
||||
for _, artifact := range artifacts {
|
||||
missingFields := []string{}
|
||||
if artifact.ArtifactId == "" {
|
||||
missingFields = append(missingFields, "ArtifactId")
|
||||
}
|
||||
if artifact.File == "" {
|
||||
missingFields = append(missingFields, "File")
|
||||
}
|
||||
if artifact.Type == "" {
|
||||
missingFields = append(missingFields, "Type")
|
||||
}
|
||||
if artifact.Version == "" {
|
||||
missingFields = append(missingFields, "Version")
|
||||
}
|
||||
if artifact.GroupId == "" {
|
||||
artifact.GroupId = args.GroupId
|
||||
}
|
||||
if len(missingFields) > 0 {
|
||||
n.addFailedArtifact(artifact, fmt.Sprintf("Missing fields: %s", strings.Join(missingFields, ", ")))
|
||||
} else {
|
||||
// Add to filtered list if all fields are valid
|
||||
filteredArtifacts = append(filteredArtifacts, artifact)
|
||||
}
|
||||
}
|
||||
|
||||
n.Artifacts = filteredArtifacts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) IsSingleFileUploadArgsOk(args Args) error {
|
||||
LogPrintln(n, "NexusPlugin IsSingleFileUploadArgsOk")
|
||||
|
||||
requiredArgs := map[string]string{
|
||||
"Username": args.Username,
|
||||
"Password": args.Password,
|
||||
"ServerUrl": args.ServerUrl,
|
||||
"Filename": args.Filename,
|
||||
"Format": args.Format,
|
||||
"Repository": args.Repository,
|
||||
}
|
||||
|
||||
for field, value := range requiredArgs {
|
||||
if value == "" {
|
||||
return GetNewError("Error in IsSingleFileUploadArgsOk: " + field + " cannot be empty")
|
||||
}
|
||||
}
|
||||
|
||||
requiredFields := []string{"CgroupId", "Cversion", "Aextension", "Aclassifier"}
|
||||
values := make(map[string]string)
|
||||
|
||||
pattern := regexp.MustCompile(`-(CgroupId|CartifactId|Cversion|Aextension|Aclassifier)=(\S+)`)
|
||||
matches := pattern.FindAllStringSubmatch(args.Attributes, -1)
|
||||
|
||||
for _, match := range matches {
|
||||
if len(match) == 3 {
|
||||
values[match[1]] = match[2]
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all required fields are present
|
||||
for _, field := range requiredFields {
|
||||
if values[field] == "" {
|
||||
return GetNewError("Error in IsSingleFileUploadArgsOk: " + field + " cannot be empty")
|
||||
}
|
||||
}
|
||||
n.UserName = args.Username
|
||||
n.Password = args.Password
|
||||
n.Repository = args.Repository
|
||||
n.ServerUrl = args.ServerUrl
|
||||
n.Format = args.Format
|
||||
n.GroupId = values["CgroupId"]
|
||||
n.Version = "nexus3"
|
||||
n.Artifacts = []Artifact{
|
||||
{
|
||||
File: args.Filename,
|
||||
Classifier: values["Aclassifier"],
|
||||
ArtifactId: values["CartifactId"],
|
||||
Type: values["Aextension"],
|
||||
Version: values["Cversion"],
|
||||
GroupId: values["CgroupId"],
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) DoPostArgsValidationSetup(args Args) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) PersistResults() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) IsQuiet() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) InspectProcessArgs(argNamesList []string) (map[string]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func GetNewNexusPlugin() NexusPlugin {
|
||||
return NexusPlugin{}
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) prepareNexus2ArtifactURL(artifact Artifact) string {
|
||||
switch n.Format {
|
||||
case "maven2":
|
||||
return fmt.Sprintf("%s/repository/%s/%s/%s/%s/%s-%s.%s",
|
||||
n.ServerUrl, n.Repository, artifact.GroupId, artifact.ArtifactId, artifact.Version,
|
||||
artifact.ArtifactId, artifact.Version, artifact.Type)
|
||||
|
||||
case "yum":
|
||||
return fmt.Sprintf("%s/repository/%s/%s/%s",
|
||||
n.ServerUrl, n.Repository, artifact.ArtifactId, artifact.Version)
|
||||
|
||||
case "raw":
|
||||
return fmt.Sprintf("%s/repository/%s/%s/%s.%s",
|
||||
n.ServerUrl, n.Repository, artifact.GroupId, artifact.ArtifactId, artifact.Type)
|
||||
|
||||
default:
|
||||
LogPrintln(n, "Unsupported format for direct upload:", n.Format)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) uploadFileNexus2(url string, content io.Reader, filePath string) error {
|
||||
req, err := http.NewRequest("PUT", url, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.SetBasicAuth(n.UserName, n.Password)
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
|
||||
resp, err := n.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
fmt.Println("File upload failed status ", resp.StatusCode)
|
||||
return fmt.Errorf("Upload failed with status %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) uploadFileNexus3(artifact Artifact, filePath string) error {
|
||||
body := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(body)
|
||||
|
||||
var url string
|
||||
var assetFieldName string
|
||||
|
||||
switch n.Format {
|
||||
case "maven2":
|
||||
_ = writer.WriteField("maven2.groupId", artifact.GroupId)
|
||||
_ = writer.WriteField("maven2.artifactId", artifact.ArtifactId)
|
||||
_ = writer.WriteField("maven2.version", artifact.Version)
|
||||
assetFieldName = "maven2.asset1"
|
||||
_ = writer.WriteField("maven2.asset1.extension", artifact.Type)
|
||||
|
||||
case "raw":
|
||||
_ = writer.WriteField("raw.directory", artifact.GroupId)
|
||||
assetFieldName = "raw.asset1"
|
||||
_ = writer.WriteField("raw.asset1.filename", fmt.Sprintf("%s.%s", artifact.ArtifactId, artifact.Type))
|
||||
|
||||
default:
|
||||
assetFieldName = fmt.Sprintf("%s.asset", n.Format)
|
||||
}
|
||||
|
||||
fileWriter, err := writer.CreateFormFile(assetFieldName, artifact.File)
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error CreateFormFile: ", err.Error())
|
||||
return err
|
||||
}
|
||||
file, err := os.Open(artifact.File)
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error os.Open(artifact.File): ", err.Error())
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(fileWriter, file)
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error io.Copy(fileWriter, file): ", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
err = writer.Close()
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error writer.Close(): ", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
url = fmt.Sprintf("%s/service/rest/v1/components?repository=%s", n.ServerUrl, n.Repository)
|
||||
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error http.NewRequest: ", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
req.SetBasicAuth(n.UserName, n.Password)
|
||||
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
|
||||
resp, err := n.HttpClient.Do(req)
|
||||
if err != nil {
|
||||
LogPrintln(n, "Error n.HttpClient.Do(req): ", err.Error())
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
LogPrintln(n, "Error upload failed with status: ", resp.StatusCode)
|
||||
return fmt.Errorf("Upload failed with status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NexusPlugin) addFailedArtifact(artifact Artifact, errMsg string) {
|
||||
n.Failed = append(n.Failed, FailedArtifact{
|
||||
File: artifact.File,
|
||||
ArtifactId: artifact.ArtifactId,
|
||||
Err: errMsg,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockHttpClient struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockHttpClient) Do(req *http.Request) (*http.Response, error) {
|
||||
args := m.Called(req)
|
||||
return args.Get(0).(*http.Response), args.Error(1)
|
||||
}
|
||||
|
||||
// Utility function to create a temporary file for testing
|
||||
func createTempFile(content string) (string, error) {
|
||||
tmpFile, err := ioutil.TempFile("", "testfile_*.zip")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := tmpFile.Write([]byte(content)); err != nil {
|
||||
tmpFile.Close()
|
||||
return "", err
|
||||
}
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpFile.Name(), nil
|
||||
}
|
||||
|
||||
func TestNexusPlugin_Run_UploadFailed(t *testing.T) {
|
||||
mockClient := new(MockHttpClient)
|
||||
mockResp := &http.Response{
|
||||
StatusCode: 500,
|
||||
Body: ioutil.NopCloser(strings.NewReader("Internal Server Error")),
|
||||
}
|
||||
mockClient.On("Do", mock.AnythingOfType("*http.Request")).Return(mockResp, nil)
|
||||
|
||||
tmpFile, err := createTempFile("testfile.zip")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(tmpFile)
|
||||
|
||||
plugin := NexusPlugin{
|
||||
PluginProcessingInfo: PluginProcessingInfo{
|
||||
UserName: "testUser",
|
||||
Password: "testPass",
|
||||
ServerUrl: "https://nexus.example.com",
|
||||
Repository: "repo",
|
||||
GroupId: "group",
|
||||
Version: "1.0.0",
|
||||
Artifacts: []Artifact{
|
||||
{
|
||||
File: tmpFile,
|
||||
ArtifactId: "artifact123",
|
||||
Type: "zip",
|
||||
},
|
||||
},
|
||||
},
|
||||
HttpClient: mockClient,
|
||||
}
|
||||
|
||||
err = plugin.Run()
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Len(t, plugin.Failed, 1)
|
||||
assert.Equal(t, tmpFile, plugin.Failed[0].File)
|
||||
assert.Equal(t, "artifact123", plugin.Failed[0].ArtifactId)
|
||||
assert.Contains(t, plugin.Failed[0].Err, "upload failed")
|
||||
mockClient.AssertExpectations(t)
|
||||
}
|
||||
|
||||
// The following tests validate argument processing without needing an actual file
|
||||
|
||||
func TestNexusPlugin_ValidateAndProcessArgs_MultiFileUpload_Success(t *testing.T) {
|
||||
args := Args{
|
||||
EnvPluginInputArgs: EnvPluginInputArgs{
|
||||
Username: "testUser",
|
||||
Password: "testPass",
|
||||
Protocol: "https",
|
||||
ServerUrl: "nexus.example.com",
|
||||
NexusVersion: "3",
|
||||
Repository: "repo",
|
||||
GroupId: "group",
|
||||
Format: "maven2",
|
||||
Artifact: "[{ \"artifactId\": \"artifact123\", \"file\": \"testfile.zip\", \"type\": \"zip\", \"version\": \"1\" }]",
|
||||
},
|
||||
}
|
||||
|
||||
plugin := NexusPlugin{}
|
||||
err := plugin.ValidateAndProcessArgs(args)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, plugin.Artifacts, 1)
|
||||
assert.Equal(t, "testfile.zip", plugin.Artifacts[0].File)
|
||||
assert.Equal(t, "artifact123", plugin.Artifacts[0].ArtifactId)
|
||||
}
|
||||
|
||||
func TestNexusPlugin_ValidateAndProcessArgs_SingleFileUpload_Success(t *testing.T) {
|
||||
args := Args{
|
||||
EnvPluginInputArgs: EnvPluginInputArgs{
|
||||
Username: "testUser",
|
||||
Password: "testPass",
|
||||
ServerUrl: "https://nexus.example.com",
|
||||
Filename: "testfile.zip",
|
||||
Format: "zip",
|
||||
Repository: "repo",
|
||||
Attributes: "-CgroupId=group -CartifactId=artifact123 -Cversion=1.0.0 -Aextension=zip -Aclassifier=classifier",
|
||||
},
|
||||
}
|
||||
|
||||
plugin := NexusPlugin{}
|
||||
err := plugin.ValidateAndProcessArgs(args)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Len(t, plugin.Artifacts, 1)
|
||||
assert.Equal(t, "testfile.zip", plugin.Artifacts[0].File)
|
||||
assert.Equal(t, "artifact123", plugin.Artifacts[0].ArtifactId)
|
||||
}
|
||||
|
||||
func TestNexusPlugin_ValidateAndProcessArgs_MissingArguments(t *testing.T) {
|
||||
args := Args{
|
||||
EnvPluginInputArgs: EnvPluginInputArgs{
|
||||
Username: "testUser",
|
||||
Password: "testPass",
|
||||
ServerUrl: "https://nexus.example.com",
|
||||
Filename: "testfile.zip",
|
||||
Format: "zip",
|
||||
Repository: "repo",
|
||||
},
|
||||
}
|
||||
|
||||
plugin := NexusPlugin{}
|
||||
err := plugin.ValidateAndProcessArgs(args)
|
||||
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "Error in DetermineCompatibilityMode: both 'Attributes' and 'Artifact' cannot be empty", err.Error())
|
||||
}
|
||||
|
||||
func TestNexusPlugin_Run_MultiFileUpload_Success(t *testing.T) {
|
||||
mockClient := new(MockHttpClient)
|
||||
mockResp := &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: ioutil.NopCloser(strings.NewReader("Success")),
|
||||
}
|
||||
mockClient.On("Do", mock.AnythingOfType("*http.Request")).Return(mockResp, nil).Maybe()
|
||||
|
||||
tmpFile1, err := createTempFile("file1.zip")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(tmpFile1)
|
||||
|
||||
tmpFile2, err := createTempFile("file2.zip")
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(tmpFile2)
|
||||
|
||||
plugin := NexusPlugin{
|
||||
PluginProcessingInfo: PluginProcessingInfo{
|
||||
UserName: "testUser",
|
||||
Password: "testPass",
|
||||
ServerUrl: "https://nexus.example.com",
|
||||
Repository: "repo",
|
||||
GroupId: "group",
|
||||
Version: "1.0.0",
|
||||
Format: "maven2",
|
||||
Artifacts: []Artifact{
|
||||
{File: tmpFile1, ArtifactId: "artifact1", Type: "zip", Version: "1"},
|
||||
{File: tmpFile2, ArtifactId: "artifact2", Type: "zip", Version: "1"},
|
||||
},
|
||||
},
|
||||
HttpClient: mockClient,
|
||||
}
|
||||
|
||||
err = plugin.Run()
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Empty(t, plugin.Failed)
|
||||
mockClient.AssertExpectations(t)
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// Copyright 2020 the Drone Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Blue Oak Model License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package plugin
|
||||
|
||||
// Pipeline provides Pipeline metadata from the environment.
|
||||
type Pipeline struct {
|
||||
// Build provides build metadata.
|
||||
Build struct {
|
||||
Branch string `envconfig:"DRONE_BUILD_BRANCH"`
|
||||
Number int `envconfig:"DRONE_BUILD_NUMBER"`
|
||||
Parent int `envconfig:"DRONE_BUILD_PARENT"`
|
||||
Event string `envconfig:"DRONE_BUILD_EVENT"`
|
||||
Action string `envconfig:"DRONE_BUILD_ACTION"`
|
||||
Status string `envconfig:"DRONE_BUILD_STATUS"`
|
||||
Created int64 `envconfig:"DRONE_BUILD_CREATED"`
|
||||
Started int64 `envconfig:"DRONE_BUILD_STARTED"`
|
||||
Finished int64 `envconfig:"DRONE_BUILD_FINISHED"`
|
||||
Link string `envconfig:"DRONE_BUILD_LINK"`
|
||||
}
|
||||
|
||||
// Calver provides the calver details parsed from the
|
||||
// git tag. If the git tag is empty or is not a valid
|
||||
// calver, the values will be empty.
|
||||
Calver struct {
|
||||
Version string `envconfig:"DRONE_CALVER"`
|
||||
Short string `envconfig:"DRONE_CALVER_SHORT"`
|
||||
MajorMinor string `envconfig:"DRONE_CALVER_MAJOR_MINOR"`
|
||||
Major string `envconfig:"DRONE_CALVER_MAJOR"`
|
||||
Minor string `envconfig:"DRONE_CALVER_MINOR"`
|
||||
Micro string `envconfig:"DRONE_CALVER_MICRO"`
|
||||
Modifier string `envconfig:"DRONE_CALVER_MODIFIER"`
|
||||
}
|
||||
|
||||
// Card provides adaptive card configuration options.
|
||||
Card struct {
|
||||
Path string `envconfig:"DRONE_CARD_PATH"`
|
||||
}
|
||||
|
||||
// Commit provides the commit metadata.
|
||||
Commit struct {
|
||||
Rev string `envconfig:"DRONE_COMMIT_SHA"`
|
||||
Before string `envconfig:"DRONE_COMMIT_BEFORE"`
|
||||
After string `envconfig:"DRONE_COMMIT_AFTER"`
|
||||
Ref string `envconfig:"DRONE_COMMIT_REF"`
|
||||
Branch string `envconfig:"DRONE_COMMIT_BRANCH"`
|
||||
Source string `envconfig:"DRONE_COMMIT_SOURCE"`
|
||||
Target string `envconfig:"DRONE_COMMIT_TARGET"`
|
||||
Link string `envconfig:"DRONE_COMMIT_LINK"`
|
||||
Message string `envconfig:"DRONE_COMMIT_MESSAGE"`
|
||||
|
||||
Author struct {
|
||||
Username string `envconfig:"DRONE_COMMIT_AUTHOR"`
|
||||
Name string `envconfig:"DRONE_COMMIT_AUTHOR_NAME"`
|
||||
Email string `envconfig:"DRONE_COMMIT_AUTHOR_EMAIL"`
|
||||
Avatar string `envconfig:"DRONE_COMMIT_AUTHOR_AVATAR"`
|
||||
}
|
||||
}
|
||||
|
||||
// Deploy provides the deployment metadata.
|
||||
Deploy struct {
|
||||
ID string `envconfig:"DRONE_DEPLOY_TO"`
|
||||
Target string `envconfig:"DRONE_DEPLOY_ID"`
|
||||
}
|
||||
|
||||
// Failed provides a list of failed steps and failed stages
|
||||
// for the current pipeline.
|
||||
Failed struct {
|
||||
Steps []string `envconfig:"DRONE_FAILED_STEPS"`
|
||||
Stages []string `envconfig:"DRONE_FAILED_STAGES"`
|
||||
}
|
||||
|
||||
// Git provides the git repository metadata.
|
||||
Git struct {
|
||||
HTTPURL string `envconfig:"DRONE_GIT_HTTP_URL"`
|
||||
SSHURL string `envconfig:"DRONE_GIT_SSH_URL"`
|
||||
}
|
||||
|
||||
// PullRequest provides the pull request metadata.
|
||||
PullRequest struct {
|
||||
Number int `envconfig:"DRONE_PULL_REQUEST"`
|
||||
}
|
||||
|
||||
// Repo provides the repository metadata.
|
||||
Repo struct {
|
||||
Branch string `envconfig:"DRONE_REPO_BRANCH"`
|
||||
Link string `envconfig:"DRONE_REPO_LINK"`
|
||||
Namespace string `envconfig:"DRONE_REPO_NAMESPACE"`
|
||||
Name string `envconfig:"DRONE_REPO_NAME"`
|
||||
Private bool `envconfig:"DRONE_REPO_PRIVATE"`
|
||||
Remote string `envconfig:"DRONE_GIT_HTTP_URL"`
|
||||
SCM string `envconfig:"DRONE_REPO_SCM"`
|
||||
Slug string `envconfig:"DRONE_REPO"`
|
||||
Visibility string `envconfig:"DRONE_REPO_VISIBILITY"`
|
||||
}
|
||||
|
||||
// Stage provides the stage metadata.
|
||||
Stage struct {
|
||||
Kind string `envconfig:"DRONE_STAGE_KIND"`
|
||||
Type string `envconfig:"DRONE_STAGE_TYPE"`
|
||||
Name string `envconfig:"DRONE_STAGE_NAME"`
|
||||
Number int `envconfig:"DRONE_STAGE_NUMBER"`
|
||||
Machine string `envconfig:"DRONE_STAGE_MACHINE"`
|
||||
OS string `envconfig:"DRONE_STAGE_OS"`
|
||||
Arch string `envconfig:"DRONE_STAGE_ARCH"`
|
||||
Variant string `envconfig:"DRONE_STAGE_VARIANT"`
|
||||
Status string `envconfig:"DRONE_STAGE_STATUS"`
|
||||
Started int64 `envconfig:"DRONE_STAGE_STARTED"`
|
||||
Finished int64 `envconfig:"DRONE_STAGE_FINISHED"`
|
||||
DependsOn []string `envconfig:"DRONE_STAGE_DEPENDS_ON"`
|
||||
}
|
||||
|
||||
// Step provides the step metadata.
|
||||
Step struct {
|
||||
Number int `envconfig:"DRONE_STEP_NUMBER"`
|
||||
Name string `envconfig:"DRONE_STEP_NAME"`
|
||||
}
|
||||
|
||||
// Semver provides the semver details parsed from the
|
||||
// git tag. If the git tag is empty or is not a valid
|
||||
// semver, the values will be empty and the error field
|
||||
// will be populated with the parsing error.
|
||||
Semver struct {
|
||||
Version string `envconfig:"DRONE_SEMVER"`
|
||||
Short string `envconfig:"DRONE_SEMVER_SHORT"`
|
||||
Major string `envconfig:"DRONE_SEMVER_MAJOR"`
|
||||
Minor string `envconfig:"DRONE_SEMVER_MINOR"`
|
||||
Patch string `envconfig:"DRONE_SEMVER_PATCH"`
|
||||
Build string `envconfig:"DRONE_SEMVER_BUILD"`
|
||||
PreRelease string `envconfig:"DRONE_SEMVER_PRERELEASE"`
|
||||
Error string `envconfig:"DRONE_SEMVER_ERROR"`
|
||||
}
|
||||
|
||||
// System provides the Drone system metadata, including
|
||||
// the system version of details required to create the
|
||||
// drone website address.
|
||||
System struct {
|
||||
Proto string `envconfig:"DRONE_SYSTEM_PROTO"`
|
||||
Host string `envconfig:"DRONE_SYSTEM_HOST"`
|
||||
Hostname string `envconfig:"DRONE_SYSTEM_HOSTNAME"`
|
||||
Version string `envconfig:"DRONE_SYSTEM_VERSION"`
|
||||
}
|
||||
|
||||
// Tag provides the git tag details.
|
||||
Tag struct {
|
||||
Name string `envconfig:"DRONE_TAG"`
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2020 the Drone Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Blue Oak Model License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func GetNewPlugin(ctx context.Context, args Args) (Plugin, error) {
|
||||
|
||||
nxp := GetNewNexusPlugin()
|
||||
return &nxp, nil
|
||||
}
|
||||
|
||||
func Exec(ctx context.Context, args Args) (Plugin, error) {
|
||||
|
||||
plugin, err := GetNewPlugin(ctx, args)
|
||||
if err != nil {
|
||||
return plugin, err
|
||||
}
|
||||
|
||||
err = plugin.Init(&args)
|
||||
if err != nil {
|
||||
return plugin, err
|
||||
}
|
||||
defer func(p Plugin) {
|
||||
err := p.DeInit()
|
||||
if err != nil {
|
||||
LogPrintln(p, "Error in DeInit: "+err.Error())
|
||||
}
|
||||
}(plugin)
|
||||
|
||||
err = plugin.ValidateAndProcessArgs(args)
|
||||
if err != nil {
|
||||
return plugin, err
|
||||
}
|
||||
|
||||
err = plugin.DoPostArgsValidationSetup(args)
|
||||
if err != nil {
|
||||
return plugin, err
|
||||
}
|
||||
|
||||
err = plugin.Run()
|
||||
|
||||
err2 := plugin.WriteOutputVariables()
|
||||
if err2 != nil {
|
||||
LogPrintln(plugin, "Writing output variable UPLOAD_STATUS failed "+err2.Error())
|
||||
}
|
||||
if err != nil {
|
||||
LogPrintln(plugin, "Upload failed "+err.Error())
|
||||
return plugin, err
|
||||
}
|
||||
|
||||
err = plugin.PersistResults()
|
||||
if err != nil {
|
||||
return plugin, err
|
||||
}
|
||||
|
||||
return plugin, nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// Copyright 2020 the Drone Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Blue Oak Model License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package plugin
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPlugin(t *testing.T) {
|
||||
t.Skip()
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
// Copyright 2020 the Drone Authors. All rights reserved.
|
||||
// Use of this source code is governed by the Blue Oak Model License
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func GetNewError(s string) error {
|
||||
return errors.New(s)
|
||||
}
|
||||
|
||||
func LogPrintln(p Plugin, args ...interface{}) {
|
||||
|
||||
if !IsDevTestingMode() {
|
||||
return
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
if p.IsQuiet() {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Println(append([]interface{}{"Plugin Info:"}, args...)...)
|
||||
}
|
||||
|
||||
func LogPrintf(p Plugin, format string, v ...interface{}) {
|
||||
|
||||
if !IsDevTestingMode() {
|
||||
return
|
||||
}
|
||||
|
||||
if p != nil {
|
||||
if p.IsQuiet() {
|
||||
return
|
||||
}
|
||||
}
|
||||
logrus.Printf(format, v...)
|
||||
}
|
||||
|
||||
func IsDirExists(dir string) (bool, error) {
|
||||
info, err := os.Stat(dir)
|
||||
if os.IsNotExist(err) {
|
||||
return false, err
|
||||
}
|
||||
return info.IsDir(), nil
|
||||
}
|
||||
|
||||
func CreateDir(absolutePath string) error {
|
||||
if absolutePath == "" || absolutePath == "." || absolutePath == ".." {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := os.MkdirAll(absolutePath, os.ModePerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory %s: %w", absolutePath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetOutputVariablesStorageFilePath() string {
|
||||
if IsDevTestingMode() {
|
||||
return filepath.Join("/tmp", "drone-output")
|
||||
}
|
||||
return os.Getenv("DRONE_OUTPUT")
|
||||
}
|
||||
|
||||
func ReadFileAsString(filePath string) (string, error) {
|
||||
data, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func WriteEnvVariableAsString(key string, value interface{}) error {
|
||||
|
||||
if GetOutputVariablesStorageFilePath() == "" {
|
||||
return GetNewError("Output file path is empty, check env var DRONE_OUTPUT")
|
||||
}
|
||||
|
||||
outputFile, err := os.OpenFile(GetOutputVariablesStorageFilePath(), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open output file: %w", err)
|
||||
}
|
||||
defer outputFile.Close()
|
||||
|
||||
valueStr := fmt.Sprintf("%v", value)
|
||||
|
||||
_, err = fmt.Fprintf(outputFile, "%s=%s\n", key, valueStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to env: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsDevTestingMode() bool {
|
||||
return os.Getenv("DEV_TEST_d6c9b463090c") == "true"
|
||||
}
|
||||
|
||||
func StructToJSONWithEnvKeys(v interface{}) (string, error) {
|
||||
val := reflect.ValueOf(v)
|
||||
typ := reflect.TypeOf(v)
|
||||
|
||||
data := make(map[string]interface{})
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
key := field.Tag.Get("envconfig")
|
||||
if key != "" {
|
||||
data[key] = val.Field(i).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
jsonData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(jsonData), nil
|
||||
}
|
||||
|
||||
func GetTestWorkSpaceDir() string {
|
||||
|
||||
nexusWorkSpaceDir := os.Getenv(DefaultWorkSpaceDirEnvVarKey)
|
||||
if nexusWorkSpaceDir == "" {
|
||||
nexusWorkSpaceDir = TestWorkSpaceDir
|
||||
}
|
||||
|
||||
return nexusWorkSpaceDir
|
||||
}
|
||||
|
||||
func GetTestBuildRootDir() string {
|
||||
return GetTestWorkSpaceDir()
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultWorkSpaceDirEnvVarKey = "DRONE_WORKSPACE"
|
||||
TestWorkSpaceDir = "../test/tmp_workspace"
|
||||
)
|
||||
|
||||
//
|
||||
//
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
|
||||
# force go modules
|
||||
export GOPATH=""
|
||||
|
||||
# disable cgo
|
||||
export CGO_ENABLED=0
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
# linux
|
||||
GOOS=linux GOARCH=amd64 go build -o release/linux/amd64/drone-nexus-publish
|
||||
#GOOS=linux GOARCH=arm64 go build -o release/linux/arm64/drone-nexus-publish
|
||||
#GOOS=linux GOARCH=arm go build -o release/linux/arm/drone-nexus-publish
|
||||
|
||||
# windows
|
||||
#GOOS=windows go build -o release/windows/amd64/drone-nexus-publish.exe
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
curl https://raw.githubusercontent.com/drone/boilr-plugin/master/template/plugin/pipeline.go --output plugin/pipeline.go
|
||||
Reference in New Issue
Block a user