Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Twine support #1267

Merged
merged 9 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions artifactory/commands/python/twine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package python

import (
"errors"
"fmt"
"github.com/jfrog/build-info-go/build"
"github.com/jfrog/build-info-go/utils/pythonutils"
buildUtils "github.com/jfrog/jfrog-cli-core/v2/common/build"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
"os"
"os/exec"
"strings"
)

const (
_configFileOptionKey = "--config-file"
_repositoryUrlOptionKey = "--repository-url"
_usernameOptionKey = "--username"
_passwordOptionKey = "--password"
_usernamePrefixOptionKey = "-u"
_passwordPrefixOptionKey = "-p"
_repositoryUrlEnvKey = "TWINE_REPOSITORY_URL"
_usernameEnvKey = "TWINE_USERNAME"
_passwordEnvKey = "TWINE_PASSWORD"
// Artifactory endpoint for pypi deployment.
_apiPypi = "api/pypi/"
_twineExecName = "twine"
_uploadCmdName = "upload"
)

var twineRepoConfigFlags = []string{_configFileOptionKey, _repositoryUrlOptionKey, _usernameOptionKey, _passwordOptionKey, _usernamePrefixOptionKey, _passwordPrefixOptionKey}

type TwineCommand struct {
serverDetails *config.ServerDetails
commandName string
args []string
targetRepo string
buildConfiguration *buildUtils.BuildConfiguration
}

func NewTwineCommand(commandName string) *TwineCommand {
return &TwineCommand{
commandName: commandName,
}
}

func (tc *TwineCommand) CommandName() string {
return "twine_" + tc.commandName
}

func (tc *TwineCommand) ServerDetails() (*config.ServerDetails, error) {
return tc.serverDetails, nil
}

func (tc *TwineCommand) SetServerDetails(serverDetails *config.ServerDetails) *TwineCommand {
tc.serverDetails = serverDetails
return tc
}

func (tc *TwineCommand) SetTargetRepo(targetRepo string) *TwineCommand {
tc.targetRepo = targetRepo
return tc
}

func (tc *TwineCommand) SetArgs(args []string) *TwineCommand {
tc.args = args
return tc
}

func (tc *TwineCommand) Run() (err error) {
// Assert no forbidden flags were provided.
if tc.isRepoConfigFlagProvided() {
return errorutils.CheckErrorf(tc.getRepoConfigFlagProvidedErr())
}
if err = tc.extractAndFilterArgs(tc.args); err != nil {
return err
}
callbackFunc, err := tc.setAuthEnvVars()
defer func() {
err = errors.Join(err, callbackFunc())
}()

collectBuild, err := tc.buildConfiguration.IsCollectBuildInfo()
if err != nil {
return err
}
// If build info is not collected, or this is not an upload command, run the twine command directly.
if !collectBuild || tc.commandName != _uploadCmdName {
return tc.runPlainTwineCommand()
}
return tc.uploadAndCollectBuildInfo()
}

func (tc *TwineCommand) extractAndFilterArgs(args []string) (err error) {
cleanArgs := append([]string(nil), args...)
cleanArgs, tc.buildConfiguration, err = buildUtils.ExtractBuildDetailsFromArgs(cleanArgs)
if err != nil {
return
}
tc.args = cleanArgs
return
}

func (tc *TwineCommand) setAuthEnvVars() (callbackFunc func() error, err error) {
oldRepoUrl := os.Getenv(_repositoryUrlEnvKey)
oldUsername := os.Getenv(_usernameEnvKey)
oldPassword := os.Getenv(_passwordEnvKey)
callbackFunc = func() error {
return errors.Join(os.Setenv(_repositoryUrlOptionKey, oldRepoUrl), os.Setenv(_usernameEnvKey, oldUsername), os.Setenv(_passwordEnvKey, oldPassword))
}

if err = os.Setenv(_repositoryUrlEnvKey, utils.AddTrailingSlashIfNeeded(tc.serverDetails.ArtifactoryUrl)+_apiPypi+tc.targetRepo); err != nil {
return
}

username := tc.serverDetails.User
password := tc.serverDetails.Password
// Get credentials from access-token if exists.
if tc.serverDetails.GetAccessToken() != "" {
if username == "" {
username = auth.ExtractUsernameFromAccessToken(tc.serverDetails.GetAccessToken())
}
password = tc.serverDetails.GetAccessToken()
}

if err = os.Setenv(_usernameEnvKey, username); err != nil {
return
}
err = os.Setenv(_passwordEnvKey, password)
return
}

func (tc *TwineCommand) runPlainTwineCommand() error {
log.Debug("Running twine command:", tc.commandName, strings.Join(tc.args, " "))
args := append([]string{tc.commandName}, tc.args...)
cmd := exec.Command(_twineExecName, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Run()
return err
RobiNino marked this conversation as resolved.
Show resolved Hide resolved
}

func (tc *TwineCommand) uploadAndCollectBuildInfo() error {
buildInfo, err := buildUtils.PrepareBuildPrerequisites(tc.buildConfiguration)
if err != nil {
return err
}

defer func() {
if buildInfo != nil && err != nil {
RobiNino marked this conversation as resolved.
Show resolved Hide resolved
err = errors.Join(err, buildInfo.Clean())
}
}()

var pythonModule *build.PythonModule
pythonModule, err = buildInfo.AddPythonModule("", pythonutils.Twine)
if err != nil {
return err
}
if tc.buildConfiguration.GetModule() != "" {
pythonModule.SetName(tc.buildConfiguration.GetModule())
}

artifacts, err := pythonModule.TwineUploadWithLogParsing(tc.args)
if err != nil {
return err
}
for i := range artifacts {
artifacts[i].OriginalDeploymentRepo = tc.targetRepo
}
if err = pythonModule.AddArtifacts(artifacts); err != nil {
return err
}
log.Debug(fmt.Sprintf("Command finished successfully. %d artifacs were added to build info.", len(artifacts)))
return nil
}

func (tc *TwineCommand) isRepoConfigFlagProvided() bool {
for _, arg := range tc.args {
for _, flag := range twineRepoConfigFlags {
if strings.HasPrefix(arg, flag) {
return true
}
}
}
return false
}

func (tc *TwineCommand) getRepoConfigFlagProvidedErr() string {
return "twine command must not be executed with the following flags: " + coreutils.ListToText(twineRepoConfigFlags)
}
1 change: 1 addition & 0 deletions artifactory/utils/commandsummary/buildinfosummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var (
buildInfo.Generic: true,
buildInfo.Terraform: true,
buildInfo.Docker: true,
buildInfo.Python: true,
}
)

Expand Down
4 changes: 3 additions & 1 deletion common/commands/configfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ func handleInteractiveConfigCreation(configFile *ConfigFile, confType project.Pr
switch confType {
case project.Go:
return configFile.setDeployerResolver()
case project.Pip, project.Pipenv, project.Poetry:
case project.Pip, project.Pipenv:
return configFile.setDeployerResolver()
case project.Poetry:
return configFile.setResolver(false)
case project.Yarn:
return configFile.setResolver(false)
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,6 @@ require (

replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240918081224-1c584cc334c7

//replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20240909072259-13bf8722d051
replace github.com/jfrog/build-info-go => github.com/RobiNino/build-info-go v1.0.1-0.20240918090844-3f25abf57e9d

//replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.7.6-0.20240909061051-2d36ae4bd05a
// replace github.com/jfrog/gofrog => github.com/jfrog/gofrog v1.3.3-0.20231223133729-ef57bd08cedc
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/RobiNino/build-info-go v1.0.1-0.20240918090844-3f25abf57e9d h1:3r3qr7CEe9hj9gqGZf9Kb50ifesEIM1I071salOtef0=
github.com/RobiNino/build-info-go v1.0.1-0.20240918090844-3f25abf57e9d/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
Expand Down Expand Up @@ -92,8 +94,6 @@ github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+
github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI=
github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw=
github.com/jfrog/build-info-go v1.9.36 h1:bKoYW3o+U70Zbz2kt5NT84N5JWNxdDXHOf+kVdzK+j4=
github.com/jfrog/build-info-go v1.9.36/go.mod h1:JcISnovFXKx3wWf3p1fcMmlPdt6adxScXvoJN4WXqIE=
github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s=
github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4=
github.com/jfrog/jfrog-client-go v1.28.1-0.20240918081224-1c584cc334c7 h1:h/bLASJGFaI3QOow1Ix63RZB8kZpAClkA/NpAVWRroc=
Expand Down
12 changes: 6 additions & 6 deletions utils/coreutils/cmdutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func FindFlagFirstMatch(flags, args []string) (flagIndex, flagValueIndex int, fl
}

func ExtractServerIdFromCommand(args []string) (cleanArgs []string, serverId string, err error) {
return extractStringOptionFromArgs(args, "server-id")
return ExtractStringOptionFromArgs(args, "server-id")
}

func ExtractThreadsFromArgs(args []string, defaultValue int) (cleanArgs []string, threads int, err error) {
Expand Down Expand Up @@ -181,12 +181,12 @@ func ExtractLicensesFromArgs(args []string) (cleanArgs []string, licenses bool,

// Used by docker scan (Xray)
func ExtractRepoPathFromArgs(args []string) (cleanArgs []string, repoPath string, err error) {
return extractStringOptionFromArgs(args, "repo-path")
return ExtractStringOptionFromArgs(args, "repo-path")
}

// Used by docker scan (Xray)
func ExtractWatchesFromArgs(args []string) (cleanArgs []string, watches string, err error) {
return extractStringOptionFromArgs(args, "watches")
return ExtractStringOptionFromArgs(args, "watches")
}

func ExtractDetailedSummaryFromArgs(args []string) (cleanArgs []string, detailedSummary bool, err error) {
Expand All @@ -198,14 +198,14 @@ func ExtractXrayScanFromArgs(args []string) (cleanArgs []string, xrayScan bool,
}

func ExtractXrayOutputFormatFromArgs(args []string) (cleanArgs []string, format string, err error) {
return extractStringOptionFromArgs(args, "format")
return ExtractStringOptionFromArgs(args, "format")
}

func ExtractTagFromArgs(args []string) (cleanArgs []string, tag string, err error) {
return extractStringOptionFromArgs(args, "tag")
return ExtractStringOptionFromArgs(args, "tag")
}

func extractStringOptionFromArgs(args []string, optionName string) (cleanArgs []string, value string, err error) {
func ExtractStringOptionFromArgs(args []string, optionName string) (cleanArgs []string, value string, err error) {
cleanArgs = append([]string(nil), args...)

flagIndex, valIndex, value, err := FindFlag("--"+optionName, cleanArgs)
Expand Down
Loading