1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00

Merge pull request #947 from SAP/goGithub

Add githubPublishRelease step
This commit is contained in:
Sven Merk 2019-11-06 12:33:05 +01:00 committed by GitHub
commit ee57a1e79d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1030 additions and 16 deletions

217
cmd/githubPublishRelease.go Normal file
View File

@ -0,0 +1,217 @@
package cmd
import (
"context"
"fmt"
"mime"
"os"
"path/filepath"
"strings"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/google/go-github/v28/github"
"github.com/pkg/errors"
piperGithub "github.com/SAP/jenkins-library/pkg/github"
)
type githubRepoClient interface {
CreateRelease(ctx context.Context, owner string, repo string, release *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error)
DeleteReleaseAsset(ctx context.Context, owner string, repo string, id int64) (*github.Response, error)
GetLatestRelease(ctx context.Context, owner string, repo string) (*github.RepositoryRelease, *github.Response, error)
ListReleaseAssets(ctx context.Context, owner string, repo string, id int64, opt *github.ListOptions) ([]*github.ReleaseAsset, *github.Response, error)
UploadReleaseAsset(ctx context.Context, owner string, repo string, id int64, opt *github.UploadOptions, file *os.File) (*github.ReleaseAsset, *github.Response, error)
}
type githubIssueClient interface {
ListByRepo(ctx context.Context, owner string, repo string, opt *github.IssueListByRepoOptions) ([]*github.Issue, *github.Response, error)
}
func githubPublishRelease(myGithubPublishReleaseOptions githubPublishReleaseOptions) error {
ctx, client, err := piperGithub.NewClient(myGithubPublishReleaseOptions.Token, myGithubPublishReleaseOptions.APIURL, myGithubPublishReleaseOptions.UploadURL)
if err != nil {
log.Entry().WithError(err).Fatal("Failed to get GitHub client.")
}
err = runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, client.Repositories, client.Issues)
if err != nil {
log.Entry().WithError(err).Fatal("Failed to publish GitHub release.")
}
return nil
}
func runGithubPublishRelease(ctx context.Context, myGithubPublishReleaseOptions *githubPublishReleaseOptions, ghRepoClient githubRepoClient, ghIssueClient githubIssueClient) error {
var publishedAt github.Timestamp
lastRelease, resp, err := ghRepoClient.GetLatestRelease(ctx, myGithubPublishReleaseOptions.Owner, myGithubPublishReleaseOptions.Repository)
if err != nil {
if resp.StatusCode == 404 {
//no previous release found -> first release
myGithubPublishReleaseOptions.AddDeltaToLastRelease = false
log.Entry().Debug("This is the first release.")
} else {
return errors.Wrap(err, "Error occured when retrieving latest GitHub release.")
}
}
publishedAt = lastRelease.GetPublishedAt()
log.Entry().Debugf("Previous GitHub release published: '%v'", publishedAt)
//updating assets only supported on latest release
if myGithubPublishReleaseOptions.UpdateAsset && myGithubPublishReleaseOptions.Version == "latest" {
return uploadReleaseAsset(ctx, lastRelease.GetID(), myGithubPublishReleaseOptions, ghRepoClient)
}
releaseBody := ""
if len(myGithubPublishReleaseOptions.ReleaseBodyHeader) > 0 {
releaseBody += myGithubPublishReleaseOptions.ReleaseBodyHeader + "\n"
}
if myGithubPublishReleaseOptions.AddClosedIssues {
releaseBody += getClosedIssuesText(ctx, publishedAt, myGithubPublishReleaseOptions, ghIssueClient)
}
if myGithubPublishReleaseOptions.AddDeltaToLastRelease {
releaseBody += getReleaseDeltaText(myGithubPublishReleaseOptions, lastRelease)
}
release := github.RepositoryRelease{
TagName: &myGithubPublishReleaseOptions.Version,
TargetCommitish: &myGithubPublishReleaseOptions.Commitish,
Name: &myGithubPublishReleaseOptions.Version,
Body: &releaseBody,
}
createdRelease, _, err := ghRepoClient.CreateRelease(ctx, myGithubPublishReleaseOptions.Owner, myGithubPublishReleaseOptions.Repository, &release)
if err != nil {
return errors.Wrapf(err, "Creation of release '%v' failed", *release.TagName)
}
log.Entry().Infof("Release %v created on %v/%v", *createdRelease.TagName, myGithubPublishReleaseOptions.Owner, myGithubPublishReleaseOptions.Repository)
if len(myGithubPublishReleaseOptions.AssetPath) > 0 {
return uploadReleaseAsset(ctx, createdRelease.GetID(), myGithubPublishReleaseOptions, ghRepoClient)
}
return nil
}
func getClosedIssuesText(ctx context.Context, publishedAt github.Timestamp, myGithubPublishReleaseOptions *githubPublishReleaseOptions, ghIssueClient githubIssueClient) string {
closedIssuesText := ""
options := github.IssueListByRepoOptions{
State: "closed",
Direction: "asc",
Since: publishedAt.Time,
}
if len(myGithubPublishReleaseOptions.Labels) > 0 {
options.Labels = myGithubPublishReleaseOptions.Labels
}
ghIssues, _, err := ghIssueClient.ListByRepo(ctx, myGithubPublishReleaseOptions.Owner, myGithubPublishReleaseOptions.Repository, &options)
if err != nil {
log.Entry().WithError(err).Error("Failed to get GitHub issues.")
}
prTexts := []string{"**List of closed pull-requests since last release**"}
issueTexts := []string{"**List of closed issues since last release**"}
for _, issue := range ghIssues {
if issue.IsPullRequest() && !isExcluded(issue, myGithubPublishReleaseOptions.ExcludeLabels) {
prTexts = append(prTexts, fmt.Sprintf("[#%v](%v): %v", issue.GetNumber(), issue.GetHTMLURL(), issue.GetTitle()))
log.Entry().Debugf("Added PR #%v to release", issue.GetNumber())
} else if !issue.IsPullRequest() && !isExcluded(issue, myGithubPublishReleaseOptions.ExcludeLabels) {
issueTexts = append(issueTexts, fmt.Sprintf("[#%v](%v): %v", issue.GetNumber(), issue.GetHTMLURL(), issue.GetTitle()))
log.Entry().Debugf("Added Issue #%v to release", issue.GetNumber())
}
}
if len(prTexts) > 1 {
closedIssuesText += "\n" + strings.Join(prTexts, "\n") + "\n"
}
if len(issueTexts) > 1 {
closedIssuesText += "\n" + strings.Join(issueTexts, "\n") + "\n"
}
return closedIssuesText
}
func getReleaseDeltaText(myGithubPublishReleaseOptions *githubPublishReleaseOptions, lastRelease *github.RepositoryRelease) string {
releaseDeltaText := ""
//add delta link to previous release
releaseDeltaText += "\n**Changes**\n"
releaseDeltaText += fmt.Sprintf(
"[%v...%v](%v/%v/%v/compare/%v...%v)\n",
lastRelease.GetTagName(),
myGithubPublishReleaseOptions.Version,
myGithubPublishReleaseOptions.ServerURL,
myGithubPublishReleaseOptions.Owner,
myGithubPublishReleaseOptions.Repository,
lastRelease.GetTagName(), myGithubPublishReleaseOptions.Version,
)
return releaseDeltaText
}
func uploadReleaseAsset(ctx context.Context, releaseID int64, myGithubPublishReleaseOptions *githubPublishReleaseOptions, ghRepoClient githubRepoClient) error {
assets, _, err := ghRepoClient.ListReleaseAssets(ctx, myGithubPublishReleaseOptions.Owner, myGithubPublishReleaseOptions.Repository, releaseID, &github.ListOptions{})
if err != nil {
return errors.Wrap(err, "Failed to get list of release assets.")
}
var assetID int64
for _, a := range assets {
if a.GetName() == filepath.Base(myGithubPublishReleaseOptions.AssetPath) {
assetID = a.GetID()
break
}
}
if assetID != 0 {
//asset needs to be deleted first since API does not allow for replacement
_, err := ghRepoClient.DeleteReleaseAsset(ctx, myGithubPublishReleaseOptions.Owner, myGithubPublishReleaseOptions.Repository, assetID)
if err != nil {
return errors.Wrap(err, "Failed to delete release asset.")
}
}
mediaType := mime.TypeByExtension(filepath.Ext(myGithubPublishReleaseOptions.AssetPath))
if mediaType == "" {
mediaType = "application/octet-stream"
}
log.Entry().Debugf("Using mediaType '%v'", mediaType)
name := filepath.Base(myGithubPublishReleaseOptions.AssetPath)
log.Entry().Debugf("Using file name '%v'", name)
opts := github.UploadOptions{
Name: name,
MediaType: mediaType,
}
file, err := os.Open(myGithubPublishReleaseOptions.AssetPath)
defer file.Close()
if err != nil {
return errors.Wrapf(err, "Failed to load release asset '%v'", myGithubPublishReleaseOptions.AssetPath)
}
log.Entry().Info("Starting to upload release asset.")
asset, _, err := ghRepoClient.UploadReleaseAsset(ctx, myGithubPublishReleaseOptions.Owner, myGithubPublishReleaseOptions.Repository, releaseID, &opts, file)
if err != nil {
return errors.Wrap(err, "Failed to upload release asset.")
}
log.Entry().Infof("Done uploading asset '%v'.", asset.GetURL())
return nil
}
func isExcluded(issue *github.Issue, excludeLabels []string) bool {
//issue.Labels[0].GetName()
for _, ex := range excludeLabels {
for _, l := range issue.Labels {
if ex == l.GetName() {
return true
}
}
}
return false
}

View File

@ -0,0 +1,189 @@
package cmd
import (
"os"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/spf13/cobra"
)
type githubPublishReleaseOptions struct {
AddClosedIssues bool `json:"addClosedIssues,omitempty"`
AddDeltaToLastRelease bool `json:"addDeltaToLastRelease,omitempty"`
AssetPath string `json:"assetPath,omitempty"`
Commitish string `json:"commitish,omitempty"`
ExcludeLabels []string `json:"excludeLabels,omitempty"`
APIURL string `json:"apiUrl,omitempty"`
Owner string `json:"owner,omitempty"`
Repository string `json:"repository,omitempty"`
ServerURL string `json:"serverUrl,omitempty"`
Token string `json:"token,omitempty"`
UploadURL string `json:"uploadUrl,omitempty"`
Labels []string `json:"labels,omitempty"`
ReleaseBodyHeader string `json:"releaseBodyHeader,omitempty"`
UpdateAsset bool `json:"updateAsset,omitempty"`
Version string `json:"version,omitempty"`
}
var myGithubPublishReleaseOptions githubPublishReleaseOptions
var githubPublishReleaseStepConfigJSON string
// GithubPublishReleaseCommand Publish a release in GitHub
func GithubPublishReleaseCommand() *cobra.Command {
metadata := githubPublishReleaseMetadata()
var createGithubPublishReleaseCmd = &cobra.Command{
Use: "githubPublishRelease",
Short: "Publish a release in GitHub",
Long: `This step creates a tag in your GitHub repository together with a release.
The release can be filled with text plus additional information like:
* Closed pull request since last release
* Closed issues since last release
* Link to delta information showing all commits since last release
The result looks like
![Example release](../images/githubRelease.png)`,
PreRunE: func(cmd *cobra.Command, args []string) error {
log.SetStepName("githubPublishRelease")
log.SetVerbose(generalConfig.verbose)
return PrepareConfig(cmd, &metadata, "githubPublishRelease", &myGithubPublishReleaseOptions, openPiperFile)
},
RunE: func(cmd *cobra.Command, args []string) error {
return githubPublishRelease(myGithubPublishReleaseOptions)
},
}
addGithubPublishReleaseFlags(createGithubPublishReleaseCmd)
return createGithubPublishReleaseCmd
}
func addGithubPublishReleaseFlags(cmd *cobra.Command) {
cmd.Flags().BoolVar(&myGithubPublishReleaseOptions.AddClosedIssues, "addClosedIssues", false, "If set to `true`, closed issues and merged pull-requests since the last release will added below the `releaseBodyHeader`")
cmd.Flags().BoolVar(&myGithubPublishReleaseOptions.AddDeltaToLastRelease, "addDeltaToLastRelease", false, "If set to `true`, a link will be added to the relese information that brings up all commits since the last release.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.AssetPath, "assetPath", os.Getenv("PIPER_assetPath"), "Path to a release asset which should be uploaded to the list of release assets.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.Commitish, "commitish", "master", "Target git commitish for the release")
cmd.Flags().StringSliceVar(&myGithubPublishReleaseOptions.ExcludeLabels, "excludeLabels", []string{}, "Allows to exclude issues with dedicated list of labels.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.APIURL, "apiUrl", "https://api.github.com", "Set the GitHub API url.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.Owner, "owner", os.Getenv("PIPER_owner"), "Set the GitHub organization.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.Repository, "repository", os.Getenv("PIPER_repository"), "Set the GitHub repository.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.ServerURL, "serverUrl", "https://github.com", "GitHub server url for end-user access.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.Token, "token", os.Getenv("PIPER_token"), "GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.UploadURL, "uploadUrl", "https://uploads.github.com", "Set the GitHub API url.")
cmd.Flags().StringSliceVar(&myGithubPublishReleaseOptions.Labels, "labels", []string{}, "Labels to include in issue search.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.ReleaseBodyHeader, "releaseBodyHeader", os.Getenv("PIPER_releaseBodyHeader"), "Content which will appear for the release.")
cmd.Flags().BoolVar(&myGithubPublishReleaseOptions.UpdateAsset, "updateAsset", false, "Specify if a release asset should be updated only.")
cmd.Flags().StringVar(&myGithubPublishReleaseOptions.Version, "version", os.Getenv("PIPER_version"), "Define the version number which will be written as tag as well as release name.")
cmd.MarkFlagRequired("apiUrl")
cmd.MarkFlagRequired("owner")
cmd.MarkFlagRequired("repository")
cmd.MarkFlagRequired("serverUrl")
cmd.MarkFlagRequired("token")
cmd.MarkFlagRequired("uploadUrl")
cmd.MarkFlagRequired("version")
}
// retrieve step metadata
func githubPublishReleaseMetadata() config.StepData {
var theMetaData = config.StepData{
Spec: config.StepSpec{
Inputs: config.StepInputs{
Parameters: []config.StepParameters{
{
Name: "addClosedIssues",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
},
{
Name: "addDeltaToLastRelease",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
},
{
Name: "assetPath",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
},
{
Name: "commitish",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
},
{
Name: "excludeLabels",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "[]string",
Mandatory: false,
},
{
Name: "apiUrl",
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
},
{
Name: "owner",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
},
{
Name: "repository",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
},
{
Name: "serverUrl",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
},
{
Name: "token",
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
},
{
Name: "uploadUrl",
Scope: []string{"GENERAL", "PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
},
{
Name: "labels",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "[]string",
Mandatory: false,
},
{
Name: "releaseBodyHeader",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: false,
},
{
Name: "updateAsset",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "bool",
Mandatory: false,
},
{
Name: "version",
Scope: []string{"PARAMETERS", "STAGES", "STEPS"},
Type: "string",
Mandatory: true,
},
},
},
},
}
return theMetaData
}

View File

@ -0,0 +1,16 @@
package cmd
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGithubPublishReleaseCommand(t *testing.T) {
testCmd := GithubPublishReleaseCommand()
// only high level testing performed - details are tested in step generation procudure
assert.Equal(t, "githubPublishRelease", testCmd.Use, "command name incorrect")
}

View File

@ -0,0 +1,383 @@
package cmd
import (
"context"
"fmt"
"net/http"
"os"
"path/filepath"
"testing"
"time"
"github.com/google/go-github/v28/github"
"github.com/stretchr/testify/assert"
)
type ghRCMock struct {
createErr error
latestRelease *github.RepositoryRelease
release *github.RepositoryRelease
delErr error
delID int64
delOwner string
delRepo string
listErr error
listID int64
listOwner string
listReleaseAssets []*github.ReleaseAsset
listRepo string
listOpts *github.ListOptions
latestStatusCode int
latestErr error
uploadID int64
uploadOpts *github.UploadOptions
uploadOwner string
uploadRepo string
}
func (g *ghRCMock) CreateRelease(ctx context.Context, owner string, repo string, release *github.RepositoryRelease) (*github.RepositoryRelease, *github.Response, error) {
g.release = release
return release, nil, g.createErr
}
func (g *ghRCMock) DeleteReleaseAsset(ctx context.Context, owner string, repo string, id int64) (*github.Response, error) {
g.delOwner = owner
g.delRepo = repo
g.delID = id
return nil, g.delErr
}
func (g *ghRCMock) GetLatestRelease(ctx context.Context, owner string, repo string) (*github.RepositoryRelease, *github.Response, error) {
hc := http.Response{StatusCode: 200}
if g.latestStatusCode != 0 {
hc.StatusCode = g.latestStatusCode
}
ghResp := github.Response{Response: &hc}
return g.latestRelease, &ghResp, g.latestErr
}
func (g *ghRCMock) ListReleaseAssets(ctx context.Context, owner string, repo string, id int64, opt *github.ListOptions) ([]*github.ReleaseAsset, *github.Response, error) {
g.listID = id
g.listOwner = owner
g.listRepo = repo
g.listOpts = opt
return g.listReleaseAssets, nil, g.listErr
}
func (g *ghRCMock) UploadReleaseAsset(ctx context.Context, owner string, repo string, id int64, opt *github.UploadOptions, file *os.File) (*github.ReleaseAsset, *github.Response, error) {
g.uploadID = id
g.uploadOwner = owner
g.uploadRepo = repo
g.uploadOpts = opt
return nil, nil, nil
}
type ghICMock struct {
issues []*github.Issue
lastPublished time.Time
owner string
repo string
options *github.IssueListByRepoOptions
}
func (g *ghICMock) ListByRepo(ctx context.Context, owner string, repo string, opt *github.IssueListByRepoOptions) ([]*github.Issue, *github.Response, error) {
g.owner = owner
g.repo = repo
g.options = opt
g.lastPublished = opt.Since
return g.issues, nil, nil
}
func TestRunGithubPublishRelease(t *testing.T) {
ctx := context.Background()
t.Run("Success - first release & no body", func(t *testing.T) {
ghIssueClient := ghICMock{}
ghRepoClient := ghRCMock{
latestStatusCode: 404,
latestErr: fmt.Errorf("not found"),
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
AddDeltaToLastRelease: true,
Commitish: "master",
Owner: "TEST",
Repository: "test",
ServerURL: "https://github.com",
ReleaseBodyHeader: "Header",
Version: "1.0",
}
err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient)
assert.NoError(t, err, "Error occured but none expected.")
assert.Equal(t, "Header\n", ghRepoClient.release.GetBody())
})
t.Run("Success - subsequent releases & with body", func(t *testing.T) {
lastTag := "1.0"
lastPublishedAt := github.Timestamp{Time: time.Date(2019, 01, 01, 0, 0, 0, 0, time.UTC)}
ghRepoClient := ghRCMock{
createErr: nil,
latestRelease: &github.RepositoryRelease{
TagName: &lastTag,
PublishedAt: &lastPublishedAt,
},
}
prHTMLURL := "https://github.com/TEST/test/pull/1"
prTitle := "Pull"
prNo := 1
issHTMLURL := "https://github.com/TEST/test/issues/2"
issTitle := "Issue"
issNo := 2
ghIssueClient := ghICMock{
issues: []*github.Issue{
{Number: &prNo, Title: &prTitle, HTMLURL: &prHTMLURL, PullRequestLinks: &github.PullRequestLinks{URL: &prHTMLURL}},
{Number: &issNo, Title: &issTitle, HTMLURL: &issHTMLURL},
},
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
AddClosedIssues: true,
AddDeltaToLastRelease: true,
Commitish: "master",
Owner: "TEST",
Repository: "test",
ServerURL: "https://github.com",
ReleaseBodyHeader: "Header",
Version: "1.1",
}
err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient)
assert.NoError(t, err, "Error occured but none expected.")
assert.Equal(t, "Header\n\n**List of closed pull-requests since last release**\n[#1](https://github.com/TEST/test/pull/1): Pull\n\n**List of closed issues since last release**\n[#2](https://github.com/TEST/test/issues/2): Issue\n\n**Changes**\n[1.0...1.1](https://github.com/TEST/test/compare/1.0...1.1)\n", ghRepoClient.release.GetBody())
assert.Equal(t, "1.1", ghRepoClient.release.GetName())
assert.Equal(t, "1.1", ghRepoClient.release.GetTagName())
assert.Equal(t, "master", ghRepoClient.release.GetTargetCommitish())
assert.Equal(t, lastPublishedAt.Time, ghIssueClient.lastPublished)
})
t.Run("Success - update asset", func(t *testing.T) {
var releaseID int64 = 1
ghIssueClient := ghICMock{}
ghRepoClient := ghRCMock{
latestRelease: &github.RepositoryRelease{
ID: &releaseID,
},
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
UpdateAsset: true,
AssetPath: filepath.Join("testdata", t.Name()+"_test.txt"),
Version: "latest",
}
err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient)
assert.NoError(t, err, "Error occured but none expected.")
assert.Nil(t, ghRepoClient.release)
assert.Equal(t, releaseID, ghRepoClient.listID)
assert.Equal(t, releaseID, ghRepoClient.uploadID)
})
t.Run("Error - get release", func(t *testing.T) {
ghIssueClient := ghICMock{}
ghRepoClient := ghRCMock{
latestErr: fmt.Errorf("Latest release error"),
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{}
err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient)
assert.Equal(t, "Error occured when retrieving latest GitHub release.: Latest release error", fmt.Sprint(err))
})
t.Run("Error - create release", func(t *testing.T) {
ghIssueClient := ghICMock{}
ghRepoClient := ghRCMock{
createErr: fmt.Errorf("Create release error"),
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
Version: "1.0",
}
err := runGithubPublishRelease(ctx, &myGithubPublishReleaseOptions, &ghRepoClient, &ghIssueClient)
assert.Equal(t, "Creation of release '1.0' failed: Create release error", fmt.Sprint(err))
})
}
func TestGetClosedIssuesText(t *testing.T) {
ctx := context.Background()
publishedAt := github.Timestamp{Time: time.Date(2019, 01, 01, 0, 0, 0, 0, time.UTC)}
t.Run("No issues", func(t *testing.T) {
ghIssueClient := ghICMock{}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
Version: "1.0",
}
res := getClosedIssuesText(ctx, publishedAt, &myGithubPublishReleaseOptions, &ghIssueClient)
assert.Equal(t, "", res)
})
t.Run("All issues", func(t *testing.T) {
ctx := context.Background()
publishedAt := github.Timestamp{Time: time.Date(2019, 01, 01, 0, 0, 0, 0, time.UTC)}
prHTMLURL := []string{"https://github.com/TEST/test/pull/1", "https://github.com/TEST/test/pull/2"}
prTitle := []string{"Pull1", "Pull2"}
prNo := []int{1, 2}
issHTMLURL := []string{"https://github.com/TEST/test/issues/3", "https://github.com/TEST/test/issues/4"}
issTitle := []string{"Issue3", "Issue4"}
issNo := []int{3, 4}
ghIssueClient := ghICMock{
issues: []*github.Issue{
{Number: &prNo[0], Title: &prTitle[0], HTMLURL: &prHTMLURL[0], PullRequestLinks: &github.PullRequestLinks{URL: &prHTMLURL[0]}},
{Number: &prNo[1], Title: &prTitle[1], HTMLURL: &prHTMLURL[1], PullRequestLinks: &github.PullRequestLinks{URL: &prHTMLURL[1]}},
{Number: &issNo[0], Title: &issTitle[0], HTMLURL: &issHTMLURL[0]},
{Number: &issNo[1], Title: &issTitle[1], HTMLURL: &issHTMLURL[1]},
},
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
Owner: "TEST",
Repository: "test",
}
res := getClosedIssuesText(ctx, publishedAt, &myGithubPublishReleaseOptions, &ghIssueClient)
assert.Equal(t, "\n**List of closed pull-requests since last release**\n[#1](https://github.com/TEST/test/pull/1): Pull1\n[#2](https://github.com/TEST/test/pull/2): Pull2\n\n**List of closed issues since last release**\n[#3](https://github.com/TEST/test/issues/3): Issue3\n[#4](https://github.com/TEST/test/issues/4): Issue4\n", res)
assert.Equal(t, "TEST", ghIssueClient.owner, "Owner not properly passed")
assert.Equal(t, "test", ghIssueClient.repo, "Repo not properly passed")
assert.Equal(t, "closed", ghIssueClient.options.State, "Issue state not properly passed")
assert.Equal(t, "asc", ghIssueClient.options.Direction, "Sort direction not properly passed")
assert.Equal(t, publishedAt.Time, ghIssueClient.options.Since, "PublishedAt not properly passed")
})
}
func TestGetReleaseDeltaText(t *testing.T) {
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
Owner: "TEST",
Repository: "test",
ServerURL: "https://github.com",
Version: "1.1",
}
lastTag := "1.0"
lastRelease := github.RepositoryRelease{
TagName: &lastTag,
}
res := getReleaseDeltaText(&myGithubPublishReleaseOptions, &lastRelease)
assert.Equal(t, "\n**Changes**\n[1.0...1.1](https://github.com/TEST/test/compare/1.0...1.1)\n", res)
}
func TestUploadReleaseAsset(t *testing.T) {
ctx := context.Background()
t.Run("Success - existing asset", func(t *testing.T) {
var releaseID int64 = 1
assetName := "Success_-_existing_asset_test.txt"
var assetID int64 = 11
ghRepoClient := ghRCMock{
latestRelease: &github.RepositoryRelease{
ID: &releaseID,
},
listReleaseAssets: []*github.ReleaseAsset{
{Name: &assetName, ID: &assetID},
},
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
Owner: "TEST",
Repository: "test",
AssetPath: filepath.Join("testdata", t.Name()+"_test.txt"),
}
err := uploadReleaseAsset(ctx, releaseID, &myGithubPublishReleaseOptions, &ghRepoClient)
assert.NoError(t, err, "Error occured but none expected.")
assert.Equal(t, "TEST", ghRepoClient.listOwner, "Owner not properly passed - list")
assert.Equal(t, "test", ghRepoClient.listRepo, "Repo not properly passed - list")
assert.Equal(t, releaseID, ghRepoClient.listID, "Relase ID not properly passed - list")
assert.Equal(t, "TEST", ghRepoClient.delOwner, "Owner not properly passed - del")
assert.Equal(t, "test", ghRepoClient.delRepo, "Repo not properly passed - del")
assert.Equal(t, assetID, ghRepoClient.delID, "Relase ID not properly passed - del")
assert.Equal(t, "TEST", ghRepoClient.uploadOwner, "Owner not properly passed - upload")
assert.Equal(t, "test", ghRepoClient.uploadRepo, "Repo not properly passed - upload")
assert.Equal(t, releaseID, ghRepoClient.uploadID, "Relase ID not properly passed - upload")
assert.Equal(t, "text/plain; charset=utf-8", ghRepoClient.uploadOpts.MediaType, "Wrong MediaType passed - upload")
})
t.Run("Success - no asset", func(t *testing.T) {
var releaseID int64 = 1
assetName := "notFound"
var assetID int64 = 11
ghRepoClient := ghRCMock{
latestRelease: &github.RepositoryRelease{
ID: &releaseID,
},
listReleaseAssets: []*github.ReleaseAsset{
{Name: &assetName, ID: &assetID},
},
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{
Owner: "TEST",
Repository: "test",
AssetPath: filepath.Join("testdata", t.Name()+"_test.txt"),
}
err := uploadReleaseAsset(ctx, releaseID, &myGithubPublishReleaseOptions, &ghRepoClient)
assert.NoError(t, err, "Error occured but none expected.")
assert.Equal(t, int64(0), ghRepoClient.delID, "Relase ID should not be populated")
})
t.Run("Error - List Assets", func(t *testing.T) {
var releaseID int64 = 1
ghRepoClient := ghRCMock{
listErr: fmt.Errorf("List Asset Error"),
}
myGithubPublishReleaseOptions := githubPublishReleaseOptions{}
err := uploadReleaseAsset(ctx, releaseID, &myGithubPublishReleaseOptions, &ghRepoClient)
assert.Equal(t, "Failed to get list of release assets.: List Asset Error", fmt.Sprint(err), "Wrong error received")
})
}
func TestIsExcluded(t *testing.T) {
l1 := "label1"
l2 := "label2"
tt := []struct {
issue *github.Issue
excludeLabels []string
expected bool
}{
{issue: nil, excludeLabels: nil, expected: false},
{issue: &github.Issue{}, excludeLabels: nil, expected: false},
{issue: &github.Issue{Labels: []github.Label{{Name: &l1}}}, excludeLabels: nil, expected: false},
{issue: &github.Issue{Labels: []github.Label{{Name: &l1}}}, excludeLabels: []string{"label0"}, expected: false},
{issue: &github.Issue{Labels: []github.Label{{Name: &l1}}}, excludeLabels: []string{"label1"}, expected: true},
{issue: &github.Issue{Labels: []github.Label{{Name: &l1}, {Name: &l2}}}, excludeLabels: []string{}, expected: false},
{issue: &github.Issue{Labels: []github.Label{{Name: &l1}, {Name: &l2}}}, excludeLabels: []string{"label1"}, expected: true},
}
for k, v := range tt {
assert.Equal(t, v.expected, isExcluded(v.issue, v.excludeLabels), fmt.Sprintf("Run %v failed", k))
}
}

View File

@ -1,8 +1,6 @@
package cmd
import (
//"os"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/spf13/cobra"

View File

@ -41,6 +41,7 @@ func Execute() {
rootCmd.AddCommand(ConfigCommand())
rootCmd.AddCommand(VersionCommand())
rootCmd.AddCommand(KarmaExecuteTestsCommand())
rootCmd.AddCommand(GithubPublishReleaseCommand())
addRootFlags(rootCmd)
if err := rootCmd.Execute(); err != nil {

View File

@ -0,0 +1 @@
TEST

View File

@ -0,0 +1 @@
TEST

View File

@ -0,0 +1 @@
TEST

View File

@ -1,8 +1,6 @@
package cmd
import (
//"os"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/spf13/cobra"

2
go.mod
View File

@ -4,10 +4,12 @@ go 1.13
require (
github.com/ghodss/yaml v1.0.0
github.com/google/go-github/v28 v28.1.1
github.com/google/go-cmp v0.3.1
github.com/pkg/errors v0.8.1
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.5
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.2.2
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
)

21
go.sum
View File

@ -1,3 +1,4 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@ -9,6 +10,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
@ -42,10 +49,24 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -30,7 +30,7 @@ type stepInfo struct {
const stepGoTemplate = `package cmd
import (
//"os"
{{if .OSImport}}"os"{{end}}
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"
@ -148,10 +148,11 @@ func processMetaFiles(metadataFiles []string, openFile func(s string) (io.ReadCl
fmt.Printf("Step name: %v\n", stepData.Metadata.Name)
err = setDefaultParameters(&stepData)
osImport := false
osImport, err = setDefaultParameters(&stepData)
checkError(err)
myStepInfo := getStepInfo(&stepData)
myStepInfo := getStepInfo(&stepData, osImport)
step := stepTemplate(myStepInfo)
err = writeFile(fmt.Sprintf("cmd/%v_generated.go", stepData.Metadata.Name), step, 0644)
@ -172,14 +173,16 @@ func fileWriter(filename string, data []byte, perm os.FileMode) error {
return ioutil.WriteFile(filename, data, perm)
}
func setDefaultParameters(stepData *config.StepData) error {
func setDefaultParameters(stepData *config.StepData) (bool, error) {
//ToDo: custom function for default handling, support all relevant parameter types
osImportRequired := false
for k, param := range stepData.Spec.Inputs.Parameters {
if param.Default == nil {
switch param.Type {
case "string":
param.Default = fmt.Sprintf("os.Getenv(\"PIPER_%v\")", param.Name)
osImportRequired = true
case "bool":
// ToDo: Check if default should be read from env
param.Default = "false"
@ -187,7 +190,7 @@ func setDefaultParameters(stepData *config.StepData) error {
// ToDo: Check if default should be read from env
param.Default = "[]string{}"
default:
return fmt.Errorf("Meta data type not set or not known: '%v'", param.Type)
return false, fmt.Errorf("Meta data type not set or not known: '%v'", param.Type)
}
} else {
switch param.Type {
@ -202,16 +205,16 @@ func setDefaultParameters(stepData *config.StepData) error {
case "[]string":
param.Default = fmt.Sprintf("[]string{\"%v\"}", strings.Join(param.Default.([]string), "\", \""))
default:
return fmt.Errorf("Meta data type not set or not known: '%v'", param.Type)
return false, fmt.Errorf("Meta data type not set or not known: '%v'", param.Type)
}
}
stepData.Spec.Inputs.Parameters[k] = param
}
return nil
return osImportRequired, nil
}
func getStepInfo(stepData *config.StepData) stepInfo {
func getStepInfo(stepData *config.StepData, osImport bool) stepInfo {
return stepInfo{
StepName: stepData.Metadata.Name,
CobraCmdFuncName: fmt.Sprintf("%vCommand", strings.Title(stepData.Metadata.Name)),
@ -220,6 +223,7 @@ func getStepInfo(stepData *config.StepData) stepInfo {
Long: stepData.Metadata.LongDescription,
Metadata: stepData.Spec.Inputs.Parameters,
FlagsFunc: fmt.Sprintf("add%vFlags", strings.Title(stepData.Metadata.Name)),
OSImport: osImport,
}
}
@ -291,6 +295,7 @@ func longName(long string) string {
func golangName(name string) string {
properName := strings.Replace(name, "Api", "API", -1)
properName = strings.Replace(properName, "api", "API", -1)
properName = strings.Replace(properName, "Url", "URL", -1)
properName = strings.Replace(properName, "Id", "ID", -1)
properName = strings.Replace(properName, "Json", "JSON", -1)

View File

@ -112,10 +112,12 @@ func TestSetDefaultParameters(t *testing.T) {
"[]string{}",
}
err := setDefaultParameters(&stepData)
osImport, err := setDefaultParameters(&stepData)
assert.NoError(t, err, "error occured but none expected")
assert.Equal(t, true, osImport, "import of os package required")
for k, v := range expected {
assert.Equal(t, v, stepData.Spec.Inputs.Parameters[k].Default, fmt.Sprintf("default not correct for parameter %v", k))
}
@ -145,7 +147,7 @@ func TestSetDefaultParameters(t *testing.T) {
}
for k, v := range stepData {
err := setDefaultParameters(&v)
_, err := setDefaultParameters(&v)
assert.Error(t, err, fmt.Sprintf("error expected but none occured for parameter %v", k))
}
})
@ -168,7 +170,7 @@ func TestGetStepInfo(t *testing.T) {
},
}
myStepInfo := getStepInfo(&stepData)
myStepInfo := getStepInfo(&stepData, true)
assert.Equal(t, "testStep", myStepInfo.StepName, "StepName incorrect")
assert.Equal(t, "TestStepCommand", myStepInfo.CobraCmdFuncName, "CobraCmdFuncName incorrect")
@ -177,6 +179,7 @@ func TestGetStepInfo(t *testing.T) {
assert.Equal(t, "Long Test description", myStepInfo.Long, "Long incorrect")
assert.Equal(t, stepData.Spec.Inputs.Parameters, myStepInfo.Metadata, "Metadata incorrect")
assert.Equal(t, "addTestStepFlags", myStepInfo.FlagsFunc, "FlagsFunc incorrect")
assert.Equal(t, "addTestStepFlags", myStepInfo.FlagsFunc, "FlagsFunc incorrect")
}
@ -200,6 +203,7 @@ func TestGolangName(t *testing.T) {
expected string
}{
{input: "testApi", expected: "TestAPI"},
{input: "apiTest", expected: "APITest"},
{input: "testUrl", expected: "TestURL"},
{input: "testId", expected: "TestID"},
{input: "testJson", expected: "TestJSON"},

View File

@ -1,7 +1,7 @@
package cmd
import (
//"os"
"os"
"github.com/SAP/jenkins-library/pkg/config"
"github.com/SAP/jenkins-library/pkg/log"

23
pkg/github/github.go Normal file
View File

@ -0,0 +1,23 @@
package github
import (
"context"
"github.com/google/go-github/v28/github"
"golang.org/x/oauth2"
)
//NewClient creates a new GitHub client using an OAuth token for authentication
func NewClient(token, apiURL, uploadURL string) (context.Context, *github.Client, error) {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token},
)
tc := oauth2.NewClient(ctx, ts)
client, err := github.NewEnterpriseClient(apiURL, uploadURL, tc)
if err != nil {
return ctx, nil, err
}
return ctx, client, nil
}

View File

@ -0,0 +1,154 @@
metadata:
name: githubPublishRelease
description: Publish a release in GitHub
longDescription: |
This step creates a tag in your GitHub repository together with a release.
The release can be filled with text plus additional information like:
* Closed pull request since last release
* Closed issues since last release
* Link to delta information showing all commits since last release
The result looks like
![Example release](../images/githubRelease.png)
spec:
inputs:
secrets:
- name: githubTokenCredentialsId
description: Jenkins 'Secret text' credentials ID containing token to authenticate to GitHub.
type: jenkins
params:
- name: addClosedIssues
description: 'If set to `true`, closed issues and merged pull-requests since the last release will added below the `releaseBodyHeader`'
scope:
- PARAMETERS
- STAGES
- STEPS
type: bool
default: false
- name: addDeltaToLastRelease
description: 'If set to `true`, a link will be added to the relese information that brings up all commits since the last release.'
scope:
- PARAMETERS
- STAGES
- STEPS
type: bool
default: false
- name: assetPath
description: Path to a release asset which should be uploaded to the list of release assets.
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: commitish
description: 'Target git commitish for the release'
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
default: "master"
- name: excludeLabels
description: 'Allows to exclude issues with dedicated list of labels.'
scope:
- PARAMETERS
- STAGES
- STEPS
type: '[]string'
- name: apiUrl
aliases:
- name: githubApiUrl
description: Set the GitHub API url.
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
type: string
default: https://api.github.com
mandatory: true
- name: owner
aliases:
- name: githubOrg
description: 'Set the GitHub organization.'
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
mandatory: true
- name: repository
aliases:
- name: githubRepo
description: 'Set the GitHub repository.'
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
mandatory: true
- name: serverUrl
aliases:
- name: githubServerUrl
description: 'GitHub server url for end-user access.'
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
default: https://github.com
mandatory: true
- name: token
aliases:
- name: githubToken
description: 'GitHub personal access token as per https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line'
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
type: string
mandatory: true
- name: uploadUrl
aliases:
- name: githubUploadUrl
description: Set the GitHub API url.
scope:
- GENERAL
- PARAMETERS
- STAGES
- STEPS
type: string
default: https://uploads.github.com
mandatory: true
- name: labels
description: 'Labels to include in issue search.'
scope:
- PARAMETERS
- STAGES
- STEPS
type: '[]string'
- name: releaseBodyHeader
description: Content which will appear for the release.
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
- name: updateAsset
description: Specify if a release asset should be updated only.
scope:
- PARAMETERS
- STAGES
- STEPS
type: bool
- name: version
description: 'Define the version number which will be written as tag as well as release name.'
scope:
- PARAMETERS
- STAGES
- STEPS
type: string
mandatory: true