Skip to content

Commit

Permalink
Use Gradle extractor 5 in Gradle >= 6.8.1
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi committed Aug 21, 2023
1 parent 8230595 commit 3856161
Show file tree
Hide file tree
Showing 30 changed files with 1,296 additions and 90 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ build-info-go.exe
bi
bi.exe

# Dependency directories (remove the comment below to include it)
# vendor/
# Gradle
.gradle
165 changes: 89 additions & 76 deletions build/gradle.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
package build

import (
"bytes"
_ "embed"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"

"github.com/jfrog/build-info-go/utils"
"github.com/jfrog/gofrog/version"
"github.com/jfrog/jfrog-client-go/utils/errorutils"

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / ubuntu, node 14, python 3.8

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / ubuntu, node 16, python 3.9

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / ubuntu, node 16.9, python 3.x

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / windows, node 14, python 3.8

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / windows, node 16, python 3.9

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / windows, node 16.9, python 3.x

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / macOS, node 14, python 3.8

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / macOS, node 16, python 3.9

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:

Check failure on line 16 in build/gradle.go

View workflow job for this annotation

GitHub Actions / macOS, node 16.9, python 3.x

no required module provides package github.com/jfrog/jfrog-client-go/utils/errorutils; to add it:
"github.com/jfrog/jfrog-client-go/utils/log"

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / ubuntu, node 14, python 3.8

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / ubuntu, node 16, python 3.9

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / ubuntu, node 16.9, python 3.x

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / windows, node 14, python 3.8

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / windows, node 16, python 3.9

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / windows, node 16.9, python 3.x

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / macOS, node 14, python 3.8

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / macOS, node 16, python 3.9

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:

Check failure on line 17 in build/gradle.go

View workflow job for this annotation

GitHub Actions / macOS, node 16.9, python 3.x

no required module provides package github.com/jfrog/jfrog-client-go/utils/log; to add it:
)

const (
extractorPropsDir = "BUILDINFO_PROPFILE"
GradleExtractorFileName = "build-info-extractor-gradle-%s-uber.jar"
gradleInitScriptTemplate = "gradle.init"
GradleExtractorRemotePath = "org/jfrog/buildinfo/build-info-extractor-gradle/%s"
GradleExtractorDependencyVersion = "4.32.0"
extractorPropsDir = "BUILDINFO_PROPFILE"
gradleExtractorFileName = "build-info-extractor-gradle-%s-uber.jar"
gradleInitScriptTemplate = "gradle.init"
gradleExtractorRemotePath = "org/jfrog/buildinfo/build-info-extractor-gradle/%s"
gradleExtractor4DependencyVersion = "4.32.0"
gradleExtractor5DependencyVersion = "5.1.0"
)

var versionRegex = regexp.MustCompile(`Gradle (\d+\.\d+(?:\.\d+|-\w+-\d+)?)`)

//go:embed init-gradle-extractor-4.gradle
var gradleInitScriptExtractor4 string

//go:embed init-gradle-extractor-5.gradle
var gradleInitScriptExtractor5 string

type GradleModule struct {
// The build which contains the gradle module.
containingBuild *Build
Expand Down Expand Up @@ -47,21 +63,15 @@ type gradleExtractorDetails struct {

// Add a new Gradle module to a given build.
func newGradleModule(containingBuild *Build, srcPath string) (*GradleModule, error) {
home, err := os.UserHomeDir()
if err != nil {
return nil, err
}
extractorLocalPath := filepath.Join(home, dependenciesDirName, "gradle", GradleExtractorDependencyVersion)
return &GradleModule{
srcPath: srcPath,
containingBuild: containingBuild,
gradleExtractorDetails: &gradleExtractorDetails{
localPath: extractorLocalPath,
tasks: []string{"artifactoryPublish"},
propsDir: filepath.Join(containingBuild.tempDirPath, PropertiesTempFolderName),
props: map[string]string{},
tasks: []string{"artifactoryPublish"},
propsDir: filepath.Join(containingBuild.tempDirPath, PropertiesTempFolderName),
props: map[string]string{},
},
}, err
}, nil
}

func (gm *GradleModule) SetExtractorDetails(localExtractorPath, extractorPropsDir string, tasks []string, useWrapper, usePlugin bool, downloadExtractorFunc func(downloadTo, downloadFrom string) error, props map[string]string) *GradleModule {
Expand All @@ -78,35 +88,78 @@ func (gm *GradleModule) SetExtractorDetails(localExtractorPath, extractorPropsDi
// Generates Gradle build-info.
func (gm *GradleModule) CalcDependencies() (err error) {
gm.containingBuild.logger.Info("Running gradle...")
if gm.srcPath == "" {
if gm.srcPath, err = os.Getwd(); err != nil {
wd, err := os.Getwd()
if err != nil {
return err
}
if gm.srcPath != "" {
if err = os.Chdir(gm.srcPath); err != nil {
return
}
defer os.Chdir(wd)
}

if err = downloadGradleDependencies(gm.gradleExtractorDetails.localPath, gm.gradleExtractorDetails.downloadExtractorFunc, gm.containingBuild.logger); err != nil {
return err
gradleExecPath, err := GetGradleExecPath(gm.gradleExtractorDetails.useWrapper)
if err != nil {
return
}
if !gm.gradleExtractorDetails.usePlugin {
gradlePluginFilename := fmt.Sprintf(GradleExtractorFileName, GradleExtractorDependencyVersion)

gm.gradleExtractorDetails.initScript, err = getInitScript(gm.gradleExtractorDetails.localPath, gradlePluginFilename)
if err != nil {
if err := gm.downloadGradleExtractor(gradleExecPath); err != nil {
return err
}
}
gradleRunConfig, err := gm.createGradleRunConfig()
gradleRunConfig, err := gm.createGradleRunConfig(gradleExecPath)
if err != nil {
return err
}
return gradleRunConfig.runCmd()
return gradleRunConfig.runCmd(os.Stdout, os.Stderr)
}

func (gm *GradleModule) createGradleRunConfig() (*gradleRunConfig, error) {
gradleExecPath, err := GetGradleExecPath(gm.gradleExtractorDetails.useWrapper)
func (gm *GradleModule) downloadGradleExtractor(gradleExecPath string) (err error) {
gradleExtractorVersion, initScriptPattern, err := gm.getExtractorVersionAndInitScript(gradleExecPath)
if err != nil {
return nil, err
return err
}
log.Debug("Using Gradle build-info extractor", gradleExtractorVersion)

dependencyLocalPath := filepath.Join(gm.gradleExtractorDetails.localPath, gradleExtractorVersion)
if err = downloadGradleDependencies(dependencyLocalPath, gradleExtractorVersion, gm.gradleExtractorDetails.downloadExtractorFunc, gm.containingBuild.logger); err != nil {
return err
}
gradlePluginFilename := fmt.Sprintf(gradleExtractorFileName, gradleExtractorVersion)

gm.gradleExtractorDetails.initScript, err = getInitScript(initScriptPattern, dependencyLocalPath, gradlePluginFilename)
return
}

func (gm *GradleModule) getExtractorVersionAndInitScript(gradleExecPath string) (string, string, error) {
gradleRunConfig := &gradleRunConfig{
gradle: gradleExecPath,
tasks: "--version",
logger: gm.containingBuild.logger,
}

outBuffer := new(bytes.Buffer)
errBuffer := new(bytes.Buffer)
if err := gradleRunConfig.runCmd(outBuffer, errBuffer); err != nil {
return "", "", err
}
if errBuffer.Len() > 0 {
return "", "", errorutils.CheckErrorf("unexpected error occurred during attempt to get the Gradle version: %s", errBuffer.String())
}

match := versionRegex.FindStringSubmatch(outBuffer.String())
if len(match) == 0 {
return "", "", errorutils.CheckErrorf("couldn't parse the Gradle version: %s", outBuffer.String())
}
log.Info("Using Gradle version:", match[1])
if version.NewVersion(match[1]).AtLeast("6.8.1") {
return gradleExtractor5DependencyVersion, gradleInitScriptExtractor5, nil
}
return gradleExtractor4DependencyVersion, gradleInitScriptExtractor4, nil
}

func (gm *GradleModule) createGradleRunConfig(gradleExecPath string) (*gradleRunConfig, error) {
buildInfoPath, err := createEmptyBuildInfoFile(gm.containingBuild)
if err != nil {
return nil, err
Expand All @@ -125,13 +178,13 @@ func (gm *GradleModule) createGradleRunConfig() (*gradleRunConfig, error) {
}, nil
}

func downloadGradleDependencies(downloadTo string, downloadExtractorFunc func(downloadTo, downloadPath string) error, logger utils.Log) error {
filename := fmt.Sprintf(GradleExtractorFileName, GradleExtractorDependencyVersion)
filePath := fmt.Sprintf(GradleExtractorRemotePath, GradleExtractorDependencyVersion)
func downloadGradleDependencies(downloadTo, gradleExtractorVersion string, downloadExtractorFunc func(downloadTo, downloadPath string) error, logger utils.Log) error {
filename := fmt.Sprintf(gradleExtractorFileName, gradleExtractorVersion)
filePath := fmt.Sprintf(gradleExtractorRemotePath, gradleExtractorVersion)
return utils.DownloadDependencies(downloadTo, filename, filePath, downloadExtractorFunc, logger)
}

func getInitScript(gradleDependenciesDir, gradlePluginFilename string) (string, error) {
func getInitScript(initScriptPattern, gradleDependenciesDir, gradlePluginFilename string) (string, error) {
gradleDependenciesDir, err := filepath.Abs(gradleDependenciesDir)
if err != nil {
return "", err
Expand All @@ -145,7 +198,7 @@ func getInitScript(gradleDependenciesDir, gradlePluginFilename string) (string,

gradlePluginPath := filepath.Join(gradleDependenciesDir, gradlePluginFilename)
gradlePluginPath = strings.ReplaceAll(gradlePluginPath, "\\", "\\\\")
initScriptContent := strings.ReplaceAll(GradleInitScript, "${pluginLibDir}", gradlePluginPath)
initScriptContent := strings.ReplaceAll(initScriptPattern, "${pluginLibDir}", gradlePluginPath)
if !utils.IsPathExists(gradleDependenciesDir) {
err = os.MkdirAll(gradleDependenciesDir, 0777)
if err != nil {
Expand Down Expand Up @@ -192,54 +245,14 @@ func (config *gradleRunConfig) GetCmd() *exec.Cmd {
return exec.Command(cmd[0], cmd[1:]...)
}

func (config *gradleRunConfig) runCmd() error {
func (config *gradleRunConfig) runCmd(stdout, stderr io.Writer) error {
command := config.GetCmd()
command.Env = os.Environ()
for k, v := range config.env {
command.Env = append(command.Env, k+"="+v)
}
command.Env = append(command.Env, extractorPropsDir+"="+config.extractorPropsFile)
command.Stderr = os.Stderr
command.Stdout = os.Stderr
command.Stderr = stderr
command.Stdout = stdout
return command.Run()
}

const GradleInitScript = `import org.jfrog.gradle.plugin.artifactory.ArtifactoryPlugin
import org.jfrog.gradle.plugin.artifactory.task.ArtifactoryTask
initscript {
dependencies {
classpath fileTree('${pluginLibDir}')
}
}
addListener(new BuildInfoPluginListener())
class BuildInfoPluginListener extends BuildAdapter {
def void projectsLoaded(Gradle gradle) {
Map<String, String> projectProperties = new HashMap<String, String>(gradle.startParameter.getProjectProperties())
projectProperties.put("build.start", Long.toString(System.currentTimeMillis()))
gradle.startParameter.setProjectProperties(projectProperties)
Project root = gradle.getRootProject()
root.logger.debug("Artifactory plugin: projectsEvaluated: ${root.name}")
if (!"buildSrc".equals(root.name)) {
root.allprojects {
apply {
apply plugin: ArtifactoryPlugin
}
}
}
// Set the "mavenJava" and "ivyJava" publications or
// "archives" configuration to all Artifactory tasks.
for (Project p : root.getAllprojects()) {
Task t = p.getTasks().findByName(ArtifactoryTask.ARTIFACTORY_PUBLISH_TASK_NAME)
if (t != null) {
ArtifactoryTask task = (ArtifactoryTask) t
task.setCiServerBuild()
}
}
}
}
`
64 changes: 53 additions & 11 deletions build/gradle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,61 @@ import (
"github.com/stretchr/testify/assert"
)

var downloadExtractorsFromReleasesCases = []struct {
extractorVersion string
}{
{extractorVersion: gradleExtractor4DependencyVersion},
{extractorVersion: gradleExtractor5DependencyVersion},
}

func TestDownloadExtractorsFromReleases(t *testing.T) {
tempDirPath, err := utils.CreateTempDir()
assert.NoError(t, err)
defer func() {
assert.NoError(t, utils.RemoveTempDir(tempDirPath))
assert.NoError(t, utils.CleanOldDirs())
}()
for _, testCase := range downloadExtractorsFromReleasesCases {
t.Run(testCase.extractorVersion, func(t *testing.T) {
tempDirPath, err := utils.CreateTempDir()
assert.NoError(t, err)
defer func() {
assert.NoError(t, utils.RemoveTempDir(tempDirPath))
assert.NoError(t, utils.CleanOldDirs())
}()

// Download JAR
err = downloadGradleDependencies(tempDirPath, nil, &utils.NullLog{})
// Download JAR
err = downloadGradleDependencies(tempDirPath, testCase.extractorVersion, nil, &utils.NullLog{})
assert.NoError(t, err)

// Make sure the Gradle build-info extractor JAR exist
expectedJarPath := filepath.Join(tempDirPath, fmt.Sprintf(gradleExtractorFileName, testCase.extractorVersion))
assert.FileExists(t, expectedJarPath)
})
}
}

var getExtractorVersionAndInitScriptCases = []struct {
projectName string
expectedExtractorVersion string
expectedInitScriptPattern string
}{
{projectName: "gradle-6.8", expectedExtractorVersion: gradleExtractor4DependencyVersion, expectedInitScriptPattern: gradleInitScriptExtractor4},
{projectName: "gradle-6.8.1", expectedExtractorVersion: gradleExtractor5DependencyVersion, expectedInitScriptPattern: gradleInitScriptExtractor5},
{projectName: "gradle-7.0", expectedExtractorVersion: gradleExtractor5DependencyVersion, expectedInitScriptPattern: gradleInitScriptExtractor5},
}

func TestGetExtractorVersionAndInitScript(t *testing.T) {
gradleModule := &GradleModule{containingBuild: &Build{logger: &utils.NullLog{}}}
gradleExe, err := GetGradleExecPath(true)
assert.NoError(t, err)
for _, testCase := range getExtractorVersionAndInitScriptCases {
t.Run(testCase.projectName, func(t *testing.T) {
projectPath := filepath.Join("testdata", "gradle", testCase.projectName, gradleExe)
gradleExtractorVersion, initScriptPattern, err := gradleModule.getExtractorVersionAndInitScript(projectPath)
assert.NoError(t, err)
assert.Equal(t, testCase.expectedExtractorVersion, gradleExtractorVersion)
assert.Equal(t, testCase.expectedInitScriptPattern, initScriptPattern)
})
}
}

// Make sure the Gradle build-info extractor JAR exist
expectedJarPath := filepath.Join(tempDirPath, fmt.Sprintf(GradleExtractorFileName, GradleExtractorDependencyVersion))
assert.FileExists(t, expectedJarPath)
func TestGetGradlePluginVersionError(t *testing.T) {
gradleModule := &GradleModule{containingBuild: &Build{logger: &utils.NullLog{}}}
_, _, err := gradleModule.getExtractorVersionAndInitScript("non-exist")
assert.ErrorContains(t, err, "executable file not found in $PATH")
}
38 changes: 38 additions & 0 deletions build/init-gradle-extractor-4.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import org.jfrog.gradle.plugin.artifactory.ArtifactoryPlugin
import org.jfrog.gradle.plugin.artifactory.task.ArtifactoryTask

initscript {
dependencies {
classpath fileTree('${pluginLibDir}')
}
}

addListener(new BuildInfoPluginListener())

class BuildInfoPluginListener extends BuildAdapter {

def void projectsLoaded(Gradle gradle) {
Map<String, String> projectProperties = new HashMap<String, String>(gradle.startParameter.getProjectProperties())
projectProperties.put("build.start", Long.toString(System.currentTimeMillis()))
gradle.startParameter.setProjectProperties(projectProperties)
Project root = gradle.getRootProject()
root.logger.debug("Artifactory plugin: projectsEvaluated: ${root.name}")
if (!"buildSrc".equals(root.name)) {
root.allprojects {
apply {
apply plugin: ArtifactoryPlugin
}
}
}

// Set the "mavenJava" and "ivyJava" publications or
// "archives" configuration to all Artifactory tasks.
for (Project p : root.getAllprojects()) {
Task t = p.getTasks().findByName(ArtifactoryTask.ARTIFACTORY_PUBLISH_TASK_NAME)
if (t != null) {
ArtifactoryTask task = (ArtifactoryTask) t
task.setCiServerBuild()
}
}
}
}
Loading

0 comments on commit 3856161

Please sign in to comment.