Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
.be-*
vendor
simple-relmgt
debug
*.bak
.vscode
github-release
*.sw[op]
125 changes: 125 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
def releaseStatus = 3 // Not a release branch
def officialReleaseFileFound = 1
def releaseCmdPath = 'tmp/simple-relmgt'

pipeline {
agent any

stages {
stage('prepare build environment') {
steps {
sh('''#!/bin/bash -e
source ./build-env.sh
create-go-build-env.sh''')
}
}

stage('prepare deployment environment') {
steps {
sh('''#!/bin/bash
mkdir -p tmp
rm -f tmp/simple-relmgt
curl -L -s -O tmp/simple-relmgt https://github.com/forj-oss/simple-relmgt/releases/download/latest/simple-relmgt >/dev/null
if [[ -f tmp/simple-relmgt ]]
then
chmod +x tmp/simple-relmgt
tmp/simple-relmgt --version
else
echo "No official simple-relmgt found. Using local built one."
exit 0
fi
''')

script {
officialReleaseFileFound = sh(script: '[ -f ' + releaseCmdPath + ' ]', returnStatus: true)
if (officialReleaseFileFound == 0) {
releaseStatus = sh(script: releaseCmdPath + ' check', returnStatus: true)
} else {
releaseCmdPath = "./simple-relmgt"
}
}
}
}
stage('Release PR status') {
when {
changeRequest target: 'master'
expression { return officialReleaseFileFound == 0 }
}
steps {
sh(releaseCmdPath + ' status')
}
}
stage('Install dependencies') {
steps {
sh('''#!/bin/bash -e
source ./build-env.sh
glide i''')
}
}

stage('Build') {
steps {
withEnv(["DOCKER_JENKINS_HOME=${env.DOCKER_JENKINS_MOUNT}"]) {
sh('''#!/bin/bash -e
source ./build-env.sh
go build''')
}
}
}
stage('Tests') {
steps {
sh('''
#!/bin/bash -e
source ./build-env.sh
go test simple-relmgt simple-relmgt/cmds/draftcmd simple-relmgt/cmds/checkcmd simple-relmgt/cmds/releasecmd simple-relmgt/cmds/statecmd simple-relmgt/cmds/tagcmd'''
)
}
}

stage('Release PR status from built binary') {
when {
changeRequest target: 'master'
expression { return officialReleaseFileFound != 0 }
}
steps {
script {
echo('Using built simple-relmgt...')
releaseStatus = sh(script: releaseCmdPath + ' check', returnStatus: true)
}
}
}

stage('tag it') {
when {
branch 'master'
expression { return releaseStatus == 0 }
}
steps {
sh(releaseCmdPath + ' tag-it') // git tag, push it and create a draft github release
}
}

stage('Deploy') {
when {
branch 'master'
expression { return releaseStatus == 0 }
}
steps {
withCredentials([
usernamePassword(credentialsId: 'github-jenkins-cred', usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')
]) {
sh('''#!/bin/bash -e
source ./build-env.sh
publish.sh latest''')
sh(releaseCmdPath + ' release-it') // release the draft github release
}
}
}
}

post {
success {
deleteDir()
}
}
}
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,56 @@ Following are the steps used to release a project.
The release state is defined by another regexp detected in the release note. `simple-relmgt` will detect it. If not found, by default, it will set `released` except if we change the default use case.
additionally, `simple-relmgt` will push some artifacts to the github release.

## Jenkinsfile

On `Jenkinsfile` side, there is 2 run context option: Download it from github and run it, or run it from docker.

The following example is based on the download use case.

```Jenkinsfile
pipeline {
agent any
environment {
env.RELEASE_STATUS = sh(
script: 'simple-relmgt check',
returnStatus: true
)
}
stages {
/* optional */
stage('Release PR status') {
when {
changeRequest target: 'master'
}
steps {
sh('simple-relmgt status')
}
}
stage('tag it') {
when {
branch 'master'
environment name: 'RELEASE_STATUS', value: '0'
}
steps {
sh('simple-relmgt tag-it') // git tag, push it and create a draft github release
}
}
stage ('...'){}
stage ('release it') {
when {
branch 'master'
environment name: 'RELEASE_STATUS', value: '0'
}
steps {
sh('simple-relmgt release-it') // release the draft github release
}
stage ('Any post release tasks...'){}

}
}
}
```

## Possible futur

For now, we thought this simple automated release process, will be good in most cases. But we may need to enhance it with [github deployment API](https://developer.github.com/v3/repos/deployments/).
Expand Down
88 changes: 88 additions & 0 deletions cmds/checkcmd/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package checkcmd

import (
"fmt"
"io/ioutil"
"regexp"
"simple-relmgt/core"

"github.com/alecthomas/kingpin"
version "github.com/hashicorp/go-version"
)

// Check control the check command
type Check struct {
cmd *kingpin.CmdClause

config *core.Config
github *core.Github
git *core.Git

versionFile string
extractVersionRE *regexp.Regexp

releaseVersion string

release *core.Release
}

const (
CheckCmd = "check"
)

// Action execute the `check` command
func (c *Check) Action([]string) (code int) {
c.config = core.NewConfig("release-mgt.yaml")

_, err := c.config.Load()
kingpin.FatalIfError(err, "Unable to load %s properly.", c.config.Filename())

c.github = core.NewGithub()

err = c.github.CheckGithub()
kingpin.FatalIfError(err, "Unable to get github-release")

c.git = core.NewGit()

err = c.git.OpenRepo()
kingpin.FatalIfError(err, "Unable to open the local repository.")

var data []byte
data, err = ioutil.ReadFile(c.versionFile)
if err != nil {
fmt.Printf("Unable to read release version file %s. %s", c.versionFile, err)
return 3
}

result := c.extractVersionRE.FindStringSubmatch(string(data))
if result == nil {
fmt.Printf("Release version file (%s) found, but version string has not been detected from '%s'.", c.versionFile, core.DefaultExtractVersion)
return 2
}
c.releaseVersion = result[1]
fmt.Printf("Release version detected: %s (in %s)\n", c.releaseVersion, c.versionFile)

c.release = core.NewRelease()
code, err = c.release.CheckVersion(c.releaseVersion)
if err != nil {
fmt.Printf("%s\n", err)
} else {
fmt.Printf("Release %s ready to be published.\n", c.releaseVersion)
}
return
}

// Init initialize the check cli commands
func (c *Check) Init(app *kingpin.Application) {
if c == nil || app == nil {
return
}
c.cmd = app.Command(CheckCmd, "Provide a return code on the release status")

c.versionFile = core.DefaultVersionFile

var err error
c.extractVersionRE, err = regexp.Compile(fmt.Sprintf(core.DefaultExtractVersion, version.SemverRegexpRaw))
kingpin.FatalIfError(err, "Unable to initialize check command")

}
30 changes: 30 additions & 0 deletions cmds/draftcmd/draft.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package draftcmd

import (
"fmt"

"github.com/alecthomas/kingpin"
)

// Draft control the draft-it command
type Draft struct {
cmd *kingpin.CmdClause
}

const (
DraftItCmd = "draft-it"
)

// Action execute the `check` command
func (c *Draft) Action([]string) (code int) {
fmt.Printf("%s not currently defined\n", DraftItCmd)
return 5 // Function not defined
}

// Init initialize the check cli commands
func (c *Draft) Init(app *kingpin.Application) {
if c == nil || app == nil {
return
}
c.cmd = app.Command(DraftItCmd, "Step to create a draft release")
}
32 changes: 32 additions & 0 deletions cmds/releasecmd/release.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package releasecmd

import (
"fmt"

"github.com/alecthomas/kingpin"
)

// Release control the release-it command
type Release struct {
cmd *kingpin.CmdClause
}

const (
ReleaseItCmd = "release-it"
)

// Action execute the `check` command
func (c *Release) Action([]string) (code int) {
fmt.Printf("%s not currently defined\n", ReleaseItCmd)
return 5 // Function not defined

}

// Init initialize the check cli commands
func (c *Release) Init(app *kingpin.Application) {
if c == nil || app == nil {
return
}
c.cmd = app.Command(ReleaseItCmd, "Step to release the code after build success.")
return
}
Loading