Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
RobiNino committed Aug 5, 2024
2 parents ee2d82d + eb84173 commit 5ce9dde
Show file tree
Hide file tree
Showing 16 changed files with 98 additions and 66 deletions.
6 changes: 3 additions & 3 deletions artifactory/commands/buildinfo/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (bpc *BuildPublishCommand) Run() error {
return err
}

if err = recordCommandSummary(buildInfo, buildLink, bpc.serverDetails.Url, bpc.buildConfiguration.GetProject(), majorVersion); err != nil {
if err = recordCommandSummary(buildInfo, buildLink, bpc.serverDetails.Url, majorVersion); err != nil {
return err
}

Expand Down Expand Up @@ -232,12 +232,12 @@ func (bpc *BuildPublishCommand) getNextBuildNumber(buildName string, servicesMan
return strconv.Itoa(latestBuildNumber), nil
}

func recordCommandSummary(buildInfo *buildinfo.BuildInfo, buildLink, serverUrl, projectKey string, majorVersion int) (err error) {
func recordCommandSummary(buildInfo *buildinfo.BuildInfo, buildLink, serverUrl string, majorVersion int) (err error) {
if !commandsummary.ShouldRecordSummary() {
return
}
buildInfo.BuildUrl = buildLink
buildInfoSummary, err := commandsummary.New(commandssummaries.NewBuildInfo(serverUrl, projectKey, majorVersion), "build-info")
buildInfoSummary, err := commandsummary.New(commandssummaries.NewBuildInfo(serverUrl, majorVersion), "build-info")
if err != nil {
return
}
Expand Down
17 changes: 13 additions & 4 deletions artifactory/commands/commandssummaries/buildinfosummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ const (

type BuildInfoSummary struct {
platformUrl string
projectKey string
majorVersion int
}

func NewBuildInfo(platformUrl, projectKey string, majorVersion int) *BuildInfoSummary {
func NewBuildInfo(platformUrl string, majorVersion int) *BuildInfoSummary {
return &BuildInfoSummary{
platformUrl: platformUrl,
projectKey: projectKey,
majorVersion: majorVersion,
}
}
Expand Down Expand Up @@ -62,17 +60,28 @@ func (bis *BuildInfoSummary) buildInfoTable(builds []*buildInfo.BuildInfo) strin
func (bis *BuildInfoSummary) buildInfoModules(builds []*buildInfo.BuildInfo) string {
var markdownBuilder strings.Builder
markdownBuilder.WriteString("\n\n ### Modules Published As Part of This Build \n\n")
var shouldGenerate bool
for _, build := range builds {
for _, module := range build.Modules {
if len(module.Artifacts) == 0 {
continue
}

switch module.Type {
case buildInfo.Docker, buildInfo.Maven, buildInfo.Npm, buildInfo.Go, buildInfo.Generic, buildInfo.Terraform:
markdownBuilder.WriteString(bis.generateModuleMarkdown(module))
shouldGenerate = true
default:
// Skip unsupported module types.
continue
}
}
}

// If no supported module with artifacts was found, avoid generating the markdown.
if !shouldGenerate {
return ""
}
return markdownBuilder.String()
}

Expand Down Expand Up @@ -107,5 +116,5 @@ func (bis *BuildInfoSummary) generateArtifactUrl(artifact buildInfo.Artifact) st
if strings.TrimSpace(artifact.OriginalDeploymentRepo) == "" {
return ""
}
return generateArtifactUrl(bis.platformUrl, path.Join(artifact.OriginalDeploymentRepo, artifact.Path), bis.projectKey, bis.majorVersion)
return generateArtifactUrl(bis.platformUrl, path.Join(artifact.OriginalDeploymentRepo, artifact.Path), bis.majorVersion)
}
37 changes: 36 additions & 1 deletion artifactory/commands/commandssummaries/buildinfosummary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestBuildInfoTable(t *testing.T) {
}

func TestBuildInfoModules(t *testing.T) {
gh := &BuildInfoSummary{}
gh := &BuildInfoSummary{platformUrl: platformUrl, majorVersion: 7}
var builds = []*buildinfo.BuildInfo{
{
Name: "buildName",
Expand Down Expand Up @@ -71,6 +71,41 @@ func TestBuildInfoModules(t *testing.T) {
assert.Equal(t, getTestDataFile(t, "modules.md"), gh.buildInfoModules(builds))
}

// Validate that if no supported module with artifacts was found, we avoid generating the markdown.
func TestBuildInfoModulesEmpty(t *testing.T) {
gh := &BuildInfoSummary{}
var builds = []*buildinfo.BuildInfo{
{
Name: "buildName",
Number: "123",
Started: "2024-05-05T12:47:20.803+0300",
BuildUrl: "http://myJFrogPlatform/builds/buildName/123",
Modules: []buildinfo.Module{
{
Type: buildinfo.Maven,
Artifacts: []buildinfo.Artifact{},
Dependencies: []buildinfo.Dependency{{
Id: "dep1",
},
},
},
{
Type: buildinfo.Gradle,
Artifacts: []buildinfo.Artifact{
{
Name: "gradleArtifact",
Path: "dir/gradleArtifact",
OriginalDeploymentRepo: "gradle-local",
},
},
},
},
},
}

assert.Empty(t, gh.buildInfoModules(builds))
}

func getTestDataFile(t *testing.T, fileName string) string {
modulesPath := filepath.Join(".", "testdata", fileName)
content, err := os.ReadFile(modulesPath)
Expand Down
4 changes: 2 additions & 2 deletions artifactory/commands/commandssummaries/testdata/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<pre>📦 libs-release
└── 📁 path
└── 📁 to
└── <a href=ui/repos/tree/General/libs-release/path/to/artifact1 target="_blank">artifact1</a>
└── <a href=https://myplatform.com/ui/repos/tree/General/libs-release/path/to/artifact1?clearFilter=true target="_blank">artifact1</a>

</pre>
####
Expand All @@ -18,6 +18,6 @@
<pre>📦 generic-local
└── 📁 path
└── 📁 to
└── <a href=ui/repos/tree/General/generic-local/path/to/artifact2 target="_blank">artifact2</a>
└── <a href=https://myplatform.com/ui/repos/tree/General/generic-local/path/to/artifact2?clearFilter=true target="_blank">artifact2</a>

</pre>
6 changes: 2 additions & 4 deletions artifactory/commands/commandssummaries/uploadsummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ type UploadSummary struct {
uploadTree *utils.FileTree
uploadedArtifacts ResultsWrapper
platformUrl string
projectKey string
majorVersion int
}

Expand All @@ -24,10 +23,9 @@ type ResultsWrapper struct {
Results []UploadResult `json:"results"`
}

func NewUploadSummary(platformUrl, projectKey string, majorVersion int) *UploadSummary {
func NewUploadSummary(platformUrl string, majorVersion int) *UploadSummary {
return &UploadSummary{
platformUrl: platformUrl,
projectKey: projectKey,
majorVersion: majorVersion,
}
}
Expand Down Expand Up @@ -63,5 +61,5 @@ func (us *UploadSummary) generateFileTreeMarkdown() string {
}

func (us *UploadSummary) buildUiUrl(targetPath string) string {
return generateArtifactUrl(us.platformUrl, targetPath, us.projectKey, us.majorVersion)
return generateArtifactUrl(us.platformUrl, targetPath, us.majorVersion)
}
10 changes: 3 additions & 7 deletions artifactory/commands/commandssummaries/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,14 @@ import (
)

const (
artifactory7UiFormat = "%sui/repos/tree/General/%s"
artifactory7UiFormat = "%sui/repos/tree/General/%s?clearFilter=true"
artifactory6UiFormat = "%sartifactory/webapp/#/artifacts/browse/tree/General/%s"
)

func generateArtifactUrl(rtUrl, pathInRt, project string, majorVersion int) string {
func generateArtifactUrl(rtUrl, pathInRt string, majorVersion int) string {
rtUrl = clientUtils.AddTrailingSlashIfNeeded(rtUrl)
if majorVersion == 6 {
return fmt.Sprintf(artifactory6UiFormat, rtUrl, pathInRt)
}
uri := fmt.Sprintf(artifactory7UiFormat, rtUrl, pathInRt)
if project != "" {
uri += "?projectKey=" + project
}
return uri
return fmt.Sprintf(artifactory7UiFormat, rtUrl, pathInRt)
}
6 changes: 3 additions & 3 deletions artifactory/commands/commandssummaries/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ func TestGenerateArtifactUrl(t *testing.T) {
majorVersion int
expected string
}{
{"artifactory 7 without project", "", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file"},
{"artifactory 7 with project", "proj", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file?projectKey=proj"},
{"artifactory 7 without project", "", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file?clearFilter=true"},
{"artifactory 7 with project", "proj", 7, "https://myplatform.com/ui/repos/tree/General/repo/path/file?clearFilter=true"},
{"artifactory 6 without project", "", 6, "https://myplatform.com/artifactory/webapp/#/artifacts/browse/tree/General/repo/path/file"},
}

for _, testCase := range cases {
t.Run(testCase.testName, func(t *testing.T) {
artifactUrl := generateArtifactUrl(platformUrl, fullPath, testCase.projectKey, testCase.majorVersion)
artifactUrl := generateArtifactUrl(platformUrl, fullPath, testCase.majorVersion)
assert.Equal(t, testCase.expected, artifactUrl)
})
}
Expand Down
7 changes: 2 additions & 5 deletions artifactory/commands/generic/props.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package generic

import (
"errors"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/utils"
"github.com/jfrog/jfrog-cli-core/v2/common/spec"
"github.com/jfrog/jfrog-cli-core/v2/utils/config"
Expand Down Expand Up @@ -69,11 +70,7 @@ func searchItems(spec *spec.SpecFiles, servicesManager artifactory.ArtifactorySe
temp := []*content.ContentReader{}
defer func() {
for _, reader := range temp {
e := reader.Close()
if err == nil {
err = e
}

err = errors.Join(err, reader.Close())
}
}()
for i := 0; i < len(spec.Files); i++ {
Expand Down
7 changes: 5 additions & 2 deletions artifactory/commands/generic/setprops.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package generic

import (
"errors"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
)

Expand All @@ -21,7 +22,7 @@ func (setProps *SetPropsCommand) CommandName() string {
return "rt_set_properties"
}

func (setProps *SetPropsCommand) Run() error {
func (setProps *SetPropsCommand) Run() (err error) {
serverDetails, err := setProps.ServerDetails()
if errorutils.CheckError(err) != nil {
return err
Expand All @@ -35,7 +36,9 @@ func (setProps *SetPropsCommand) Run() error {
if err != nil {
return err
}
defer reader.Close()
defer func() {
err = errors.Join(err, reader.Close())
}()
propsParams := GetPropsParams(reader, setProps.props)
success, err := servicesManager.SetProps(propsParams)

Expand Down
11 changes: 3 additions & 8 deletions artifactory/commands/generic/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (uc *UploadCommand) upload() (err error) {
successCount = summary.TotalSucceeded
failCount = summary.TotalFailed

if err = recordCommandSummary(servicesManager, summary, serverDetails.Url, uc.buildConfiguration); err != nil {
if err = recordCommandSummary(servicesManager, summary, serverDetails.Url); err != nil {
return
}
}
Expand Down Expand Up @@ -282,7 +282,7 @@ func createDeleteSpecForSync(deletePattern string, syncDeletesProp string) *spec
BuildSpec()
}

func recordCommandSummary(servicesManager artifactory.ArtifactoryServicesManager, summary *rtServicesUtils.OperationSummary, platformUrl string, buildConfig *build.BuildConfiguration) (err error) {
func recordCommandSummary(servicesManager artifactory.ArtifactoryServicesManager, summary *rtServicesUtils.OperationSummary, platformUrl string) (err error) {
if !commandsummary.ShouldRecordSummary() {
return
}
Expand All @@ -292,12 +292,7 @@ func recordCommandSummary(servicesManager artifactory.ArtifactoryServicesManager
return err
}

// Get project key if exists
var projectKey string
if buildConfig != nil {
projectKey = buildConfig.GetProject()
}
uploadSummary, err := commandsummary.New(commandssummaries.NewUploadSummary(platformUrl, projectKey, majorVersion), "upload")
uploadSummary, err := commandsummary.New(commandssummaries.NewUploadSummary(platformUrl, majorVersion), "upload")
if err != nil {
return
}
Expand Down
23 changes: 11 additions & 12 deletions artifactory/commands/golang/archive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ package golang

import (
"bytes"
"github.com/jfrog/gofrog/crypto"
"github.com/stretchr/testify/assert"
"os"
"path/filepath"
"reflect"
"testing"

"github.com/jfrog/build-info-go/utils"

"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-core/v2/utils/log"
"github.com/jfrog/jfrog-cli-core/v2/utils/tests"
Expand All @@ -33,21 +32,21 @@ func TestArchiveProject(t *testing.T) {
mod string
version string
excludedPatterns []string
expected map[utils.Algorithm]string
expected map[crypto.Algorithm]string
}{
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", nil, map[utils.Algorithm]string{utils.MD5: "5b3603a7bf637622516673b845249205", utils.SHA1: "7386685c432c39428c9cb8584a2b970139c5e626", utils.SHA256: "eefd8aa3f9ac89876c8442d5feebbc837666bf40114d201219e3e6d51c208949"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir1/*"}, map[utils.Algorithm]string{utils.MD5: "c2eeb4ef958edee91570690bf4111fc7", utils.SHA1: "d77e10eaa9bd863a9ff3775d3e452041e6f5aa40", utils.SHA256: "ecf66c1256263b2b4386efc299fa0c389263608efda9d1d91af8a746e6c5709a"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir2/*"}, map[utils.Algorithm]string{utils.MD5: "bbe78a98ba10c1428f3a364570015e11", utils.SHA1: "99fd22ea2fe9c2c48124e741881fc3a555458a7e", utils.SHA256: "e2299f3c4e1f22d36befba191a347783dc2047e8e38cf6b9b96c273090f6e25b"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir2/*", "testdata/dir3/*"}, map[utils.Algorithm]string{utils.MD5: "28617d6e74fce3dd2bab21b1bd65009b", utils.SHA1: "410814fbf21afdfb9c5b550151a51c2e986447fa", utils.SHA256: "e877c07315d6d3ad69139035defc08c04b400b36cd069b35ea3c2960424f2dc6"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir2/*", "./testdata/dir3/dir4/*"}, map[utils.Algorithm]string{utils.MD5: "46a3ded48ed7998b1b35c80fbe0ffab5", utils.SHA1: "a26e73e7d29e49dd5d9c87da8f7c93cf929750df", utils.SHA256: "cf224b12eca12de4a052ef0f444519d64b6cecaf7b06050a02998be190e88847"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir3/*"}, map[utils.Algorithm]string{utils.MD5: "c2a2dd6a7af84c2d88a48caf0c3aec34", utils.SHA1: "193d761317a602d18566561678b7bddc4773385c", utils.SHA256: "3efcd8b0d88081ec64333ff98b43616d283c4d52ed26cd7c8df646d9ea452c31"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"*.txt"}, map[utils.Algorithm]string{utils.MD5: "e93953b4be84d7753e0f33589b7dc4ba", utils.SHA1: "280c7492f57262b6e0af56b06c9db6a128e32ab9", utils.SHA256: "e7357986c59bf670af1e2f4868edb1406a87d328b7681b15cf038491cdc7e88c"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./*/dir4/*.txt"}, map[utils.Algorithm]string{utils.MD5: "785f0c0c7b20dfd716178856edb79834", utils.SHA1: "d07204277ece1d7bef6a9f289a56afb91d66125f", utils.SHA256: "6afa0dd70bfa7c6d3aca1a3dfcd6465c542d64136c6391fa611795e6fa5800ce"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", nil, map[crypto.Algorithm]string{crypto.MD5: "5b3603a7bf637622516673b845249205", crypto.SHA1: "7386685c432c39428c9cb8584a2b970139c5e626", crypto.SHA256: "eefd8aa3f9ac89876c8442d5feebbc837666bf40114d201219e3e6d51c208949"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir1/*"}, map[crypto.Algorithm]string{crypto.MD5: "c2eeb4ef958edee91570690bf4111fc7", crypto.SHA1: "d77e10eaa9bd863a9ff3775d3e452041e6f5aa40", crypto.SHA256: "ecf66c1256263b2b4386efc299fa0c389263608efda9d1d91af8a746e6c5709a"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir2/*"}, map[crypto.Algorithm]string{crypto.MD5: "bbe78a98ba10c1428f3a364570015e11", crypto.SHA1: "99fd22ea2fe9c2c48124e741881fc3a555458a7e", crypto.SHA256: "e2299f3c4e1f22d36befba191a347783dc2047e8e38cf6b9b96c273090f6e25b"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir2/*", "testdata/dir3/*"}, map[crypto.Algorithm]string{crypto.MD5: "28617d6e74fce3dd2bab21b1bd65009b", crypto.SHA1: "410814fbf21afdfb9c5b550151a51c2e986447fa", crypto.SHA256: "e877c07315d6d3ad69139035defc08c04b400b36cd069b35ea3c2960424f2dc6"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir2/*", "./testdata/dir3/dir4/*"}, map[crypto.Algorithm]string{crypto.MD5: "46a3ded48ed7998b1b35c80fbe0ffab5", crypto.SHA1: "a26e73e7d29e49dd5d9c87da8f7c93cf929750df", crypto.SHA256: "cf224b12eca12de4a052ef0f444519d64b6cecaf7b06050a02998be190e88847"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./testdata/dir3/*"}, map[crypto.Algorithm]string{crypto.MD5: "c2a2dd6a7af84c2d88a48caf0c3aec34", crypto.SHA1: "193d761317a602d18566561678b7bddc4773385c", crypto.SHA256: "3efcd8b0d88081ec64333ff98b43616d283c4d52ed26cd7c8df646d9ea452c31"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"*.txt"}, map[crypto.Algorithm]string{crypto.MD5: "e93953b4be84d7753e0f33589b7dc4ba", crypto.SHA1: "280c7492f57262b6e0af56b06c9db6a128e32ab9", crypto.SHA256: "e7357986c59bf670af1e2f4868edb1406a87d328b7681b15cf038491cdc7e88c"}},
{buff, filepath.Join(pwd, "testdata"), "myproject.com/module/name", "v1.0.0", []string{"./*/dir4/*.txt"}, map[crypto.Algorithm]string{crypto.MD5: "785f0c0c7b20dfd716178856edb79834", crypto.SHA1: "d07204277ece1d7bef6a9f289a56afb91d66125f", crypto.SHA256: "6afa0dd70bfa7c6d3aca1a3dfcd6465c542d64136c6391fa611795e6fa5800ce"}},
}
for _, testData := range archiveWithExclusion {
err = archiveProject(testData.buff, testData.filePath, testData.mod, testData.version, testData.excludedPatterns)
assert.NoError(t, err)
actual, err := utils.CalcChecksums(buff)
actual, err := crypto.CalcChecksums(buff)
assert.NoError(t, err)

if !reflect.DeepEqual(testData.expected, actual) {
Expand Down
6 changes: 3 additions & 3 deletions artifactory/commands/golang/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"errors"
"fmt"
buildinfo "github.com/jfrog/build-info-go/entities"
biutils "github.com/jfrog/build-info-go/utils"
"github.com/jfrog/gofrog/crypto"
"github.com/jfrog/gofrog/version"
"io"
"os"
Expand Down Expand Up @@ -161,14 +161,14 @@ func readModFile(version, projectPath, deploymentRepo, relPathInRepo string, cre
return content, nil, nil
}

checksums, err := biutils.CalcChecksums(bytes.NewBuffer(content))
checksums, err := crypto.CalcChecksums(bytes.NewBuffer(content))
if err != nil {
return nil, nil, errorutils.CheckError(err)
}

// Add mod file as artifact
artifact := &buildinfo.Artifact{Name: version + ".mod", Type: "mod", OriginalDeploymentRepo: deploymentRepo, Path: relPathInRepo}
artifact.Checksum = buildinfo.Checksum{Sha1: checksums[biutils.SHA1], Md5: checksums[biutils.MD5]}
artifact.Checksum = buildinfo.Checksum{Sha1: checksums[crypto.SHA1], Md5: checksums[crypto.MD5]}
return content, artifact, nil
}

Expand Down
6 changes: 3 additions & 3 deletions artifactory/commands/transferfiles/state/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package state

import (
"fmt"
"github.com/jfrog/gofrog/crypto"
"path/filepath"
"strconv"
"strings"
"sync/atomic"
"time"

"github.com/jfrog/build-info-go/utils"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
Expand Down Expand Up @@ -100,11 +100,11 @@ func GetRepositoryTransferDir(repoKey string) (string, error) {
}

func getRepositoryHash(repoKey string) (string, error) {
checksums, err := utils.CalcChecksums(strings.NewReader(repoKey), utils.SHA1)
checksums, err := crypto.CalcChecksums(strings.NewReader(repoKey), crypto.SHA1)
if err = errorutils.CheckError(err); err != nil {
return "", err
}
return checksums[utils.SHA1], nil
return checksums[crypto.SHA1], nil
}

func GetJfrogTransferRepoSubDir(repoKey, subDirName string) (string, error) {
Expand Down
Loading

0 comments on commit 5ce9dde

Please sign in to comment.