<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	"github.com/jfrog/jfrog-client-go/artifactory"
	"github.com/jfrog/jfrog-client-go/artifactory/buildinfo"
	"github.com/jfrog/jfrog-client-go/artifactory/services"
	"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
	"github.com/jfrog/jfrog-client-go/utils/log"

	"github.com/ovh/cds/contrib/grpcplugins"
	art "github.com/ovh/cds/contrib/integrations/artifactory"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/grpcplugin/integrationplugin"
)

/*
This plugin have to be used as a build info plugin

Artifactory build info plugin must configured as following:
	name: artifactory-build-info-plugin
	type: integration
	author: "<NAME>"
	description: "OVH Artifactory Build Info Plugin"

$ cdsctl admin plugins import artifactory-build-info-plugin.yml

Build the present binaries and import in CDS:
	os: linux
	arch: amd64
	cmd: <path-to-binary-file>

$ cdsctl admin plugins binary-add artifactory-build-info-plugin artifactory-build-info-plugin-bin.yml <path-to-binary-file>
*/

type artifactoryBuildInfoPlugin struct {
	integrationplugin.Common
}

type executionContext struct {
	buildInfo         string
	projectKey        string
	workflowName      string
	version           string
	lowMaturitySuffix string
}

func (e *artifactoryBuildInfoPlugin) Manifest(_ context.Context, _ *empty.Empty) (*integrationplugin.IntegrationPluginManifest, error) {
	return &integrationplugin.IntegrationPluginManifest{
		Name:        "OVH Artifactory Build Info Plugin",
		Author:      "<NAME>",
		Description: "OVH Artifactory Build Info Plugin",
		Version:     sdk.VERSION,
	}, nil
}

func (e *artifactoryBuildInfoPlugin) Run(ctx context.Context, opts *integrationplugin.RunQuery) (*integrationplugin.RunResult, error) {
	artifactoryURL := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigURL)]
	token := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigToken)]
	tokenName := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigTokenName)]
	lowMaturitySuffix := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigPromotionLowMaturity)]
	artifactoryProjectKey := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigProjectKey)]
	buildInfo := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigBuildInfoPrefix)]

	version := opts.GetOptions()["cds.version"]
	projectKey := opts.GetOptions()["cds.project"]
	workflowName := opts.GetOptions()["cds.workflow"]

	ctx, cancel := context.WithTimeout(ctx, 15*time.Minute)
	defer cancel()

	artiClient, err := art.CreateArtifactoryClient(ctx, artifactoryURL, token)
	if err != nil {
		return fail("unable to create artifactory client: %v", err)
	}
	log.SetLogger(log.NewLogger(log.INFO, os.Stdout))

	buildInfoName := fmt.Sprintf("%s/%s/%s", buildInfo, projectKey, workflowName)

	// Check existing build info
	if err := e.deleteExistingBuild(artiClient, artifactoryProjectKey, buildInfoName, version); err != nil {
		return fail("unable to clean existing build: %v", err)
	}

	nodeRunURL := opts.GetOptions()["cds.ui.pipeline.run"]
	runURL := nodeRunURL[0:strings.Index(nodeRunURL, "/node/")]

	fmt.Printf("Creating Artifactory Build %s %s on project %s...\n", buildInfoName, version, artifactoryProjectKey)

	// Get the build agent from env variable set by worker
	workerName := os.Getenv("CDS_WORKER")
	if workerName == "" {
		workerName = "CDS"
	}

	buildInfoRequest := &buildinfo.BuildInfo{
		Properties: map[string]string{},
		Name:       buildInfoName,
		Agent: &buildinfo.Agent{
			Name:    "artifactory-build-info-plugin",
			Version: sdk.VERSION,
		},
		BuildAgent: &buildinfo.Agent{
			Name:    workerName,
			Version: sdk.VERSION,
		},
		ArtifactoryPrincipal:     fmt.Sprintf("token:%s", tokenName),
		ArtifactoryPluginVersion: sdk.VERSION,
		Started:                  time.Now().Format("2006-01-02T15:04:05.999-07:00"),
		Number:                   version,
		BuildUrl:                 runURL,
		Modules:                  []buildinfo.Module{},
		VcsList:                  make([]buildinfo.Vcs, 0),
	}

	gitUrl := opts.GetOptions()["git.url"]
	if gitUrl == "" {
		gitUrl = opts.GetOptions()["git.http_url"]
	}

	buildInfoRequest.VcsList = append(buildInfoRequest.VcsList, buildinfo.Vcs{
		Branch:   opts.GetOptions()["git.branch"],
		Message:  opts.GetOptions()["git.message"],
		Url:      gitUrl,
		Revision: opts.GetOptions()["git.hash"],
	})

	execContext := executionContext{
		buildInfo:         buildInfo,
		lowMaturitySuffix: lowMaturitySuffix,
		workflowName:      workflowName,
		version:           version,
		projectKey:        projectKey,
	}
	modules, err := e.computeBuildInfoModules(artiClient, execContext)
	if err != nil {
		return fail("unable to compute build info: %v", err)
	}
	buildInfoRequest.Modules = modules
	if _, err := artiClient.PublishBuildInfo(buildInfoRequest, artifactoryProjectKey); err != nil {
		return fail("unable to push build info: %v", err)
	}

	// Temporary code
	if opts.GetOptions()["cds.proj.xray.enabled"] == "true" {
		fmt.Printf("Triggering XRay Build %s %s scan...\n", buildInfoName, version)<commit_msg>fix(contrib/artifactory): push build info retry (#6159)<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/golang/protobuf/ptypes/empty"
	"github.com/jfrog/jfrog-client-go/artifactory"
	"github.com/jfrog/jfrog-client-go/artifactory/buildinfo"
	"github.com/jfrog/jfrog-client-go/artifactory/services"
	"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
	"github.com/jfrog/jfrog-client-go/utils/log"

	"github.com/ovh/cds/contrib/grpcplugins"
	art "github.com/ovh/cds/contrib/integrations/artifactory"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/grpcplugin/integrationplugin"
)

/*
This plugin have to be used as a build info plugin

Artifactory build info plugin must configured as following:
	name: artifactory-build-info-plugin
	type: integration
	author: "<NAME>"
	description: "OVH Artifactory Build Info Plugin"

$ cdsctl admin plugins import artifactory-build-info-plugin.yml

Build the present binaries and import in CDS:
	os: linux
	arch: amd64
	cmd: <path-to-binary-file>

$ cdsctl admin plugins binary-add artifactory-build-info-plugin artifactory-build-info-plugin-bin.yml <path-to-binary-file>
*/

type artifactoryBuildInfoPlugin struct {
	integrationplugin.Common
}

type executionContext struct {
	buildInfo         string
	projectKey        string
	workflowName      string
	version           string
	lowMaturitySuffix string
}

func (e *artifactoryBuildInfoPlugin) Manifest(_ context.Context, _ *empty.Empty) (*integrationplugin.IntegrationPluginManifest, error) {
	return &integrationplugin.IntegrationPluginManifest{
		Name:        "OVH Artifactory Build Info Plugin",
		Author:      "<NAME>",
		Description: "OVH Artifactory Build Info Plugin",
		Version:     sdk.VERSION,
	}, nil
}

func (e *artifactoryBuildInfoPlugin) Run(ctx context.Context, opts *integrationplugin.RunQuery) (*integrationplugin.RunResult, error) {
	artifactoryURL := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigURL)]
	token := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigToken)]
	tokenName := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigTokenName)]
	lowMaturitySuffix := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigPromotionLowMaturity)]
	artifactoryProjectKey := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigProjectKey)]
	buildInfo := opts.GetOptions()[fmt.Sprintf("cds.integration.artifact_manager.%s", sdk.ArtifactoryConfigBuildInfoPrefix)]

	version := opts.GetOptions()["cds.version"]
	projectKey := opts.GetOptions()["cds.project"]
	workflowName := opts.GetOptions()["cds.workflow"]

	ctx, cancel := context.WithTimeout(ctx, 15*time.Minute)
	defer cancel()

	artiClient, err := art.CreateArtifactoryClient(ctx, artifactoryURL, token)
	if err != nil {
		return fail("unable to create artifactory client: %v", err)
	}
	log.SetLogger(log.NewLogger(log.INFO, os.Stdout))

	buildInfoName := fmt.Sprintf("%s/%s/%s", buildInfo, projectKey, workflowName)

	// Check existing build info
	if err := e.deleteExistingBuild(artiClient, artifactoryProjectKey, buildInfoName, version); err != nil {
		return fail("unable to clean existing build: %v", err)
	}

	nodeRunURL := opts.GetOptions()["cds.ui.pipeline.run"]
	runURL := nodeRunURL[0:strings.Index(nodeRunURL, "/node/")]

	fmt.Printf("Creating Artifactory Build %s %s on project %s...\n", buildInfoName, version, artifactoryProjectKey)

	// Get the build agent from env variable set by worker
	workerName := os.Getenv("CDS_WORKER")
	if workerName == "" {
		workerName = "CDS"
	}

	buildInfoRequest := &buildinfo.BuildInfo{
		Properties: map[string]string{},
		Name:       buildInfoName,
		Agent: &buildinfo.Agent{
			Name:    "artifactory-build-info-plugin",
			Version: sdk.VERSION,
		},
		BuildAgent: &buildinfo.Agent{
			Name:    workerName,
			Version: sdk.VERSION,
		},
		ArtifactoryPrincipal:     fmt.Sprintf("token:%s", tokenName),
		ArtifactoryPluginVersion: sdk.VERSION,
		Started:                  time.Now().Format("2006-01-02T15:04:05.999-07:00"),
		Number:                   version,
		BuildUrl:                 runURL,
		Modules:                  []buildinfo.Module{},
		VcsList:                  make([]buildinfo.Vcs, 0),
	}

	gitUrl := opts.GetOptions()["git.url"]
	if gitUrl == "" {
		gitUrl = opts.GetOptions()["git.http_url"]
	}

	buildInfoRequest.VcsList = append(buildInfoRequest.VcsList, buildinfo.Vcs{
		Branch:   opts.GetOptions()["git.branch"],
		Message:  opts.GetOptions()["git.message"],
		Url:      gitUrl,
		Revision: opts.GetOptions()["git.hash"],
	})

	execContext := executionContext{
		buildInfo:         buildInfo,
		lowMaturitySuffix: lowMaturitySuffix,
		workflowName:      workflowName,
		version:           version,
		projectKey:        projectKey,
	}
	modules, err := e.computeBuildInfoModules(artiClient, execContext)
	if err != nil {
		return fail("unable to compute build info: %v", err)
	}
	buildInfoRequest.Modules = modules
	var nbAttempts int
	for {
		nbAttempts++
		_, err := artiClient.PublishBuildInfo(buildInfoRequest, artifactoryProjectKey)
		if err == nil {
			break
		} else if nbAttempts >= 3 {
			return fail("unable to push build info: %v", err)
		} else {
			fmt.Printf("Error while pushing buildinfo %s %s. Retrying...\n", buildInfoName, version)
		}
	}

	// Temporary code
	if opts.GetOptions()["cds.proj.xray.enabled"] == "true" {
		fmt.Printf("Triggering XRay Build %s %s scan...\n", buildInfoName, version)
{'fraction_non_alphanumeric': 0.13359776435590012, 'fraction_numerical': 0.005318669431172812, 'mean_word_length': 4.411707317073171, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7488723', 'n_tokens_mistral': 3945, 'n_tokens_neox': 3711, 'n_words': 716}