Skip to content

Instantly share code, notes, and snippets.

@renoirb
Created August 16, 2021 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save renoirb/71afc1aa2e2e5e243e00a0b96cf177e1 to your computer and use it in GitHub Desktop.
Save renoirb/71afc1aa2e2e5e243e00a0b96cf177e1 to your computer and use it in GitHub Desktop.
Jenkinsfile when using RushJS.io and releasing only on CI

Example of a Jenkinsfile when publish on Nexus and use with RushJS.io

#!/usr/bin/groovy
/* groovylint-disable LineLength, NestedBlockDepth, DuplicateStringLiteral */
/* groovylint-disable-next-line CompileStatic, NoDef, UnusedVariable, VariableName, VariableTypeRequired */
@Library('jenkins-pipeline-library@release/3') _
import com.corp.PipelineUtils
// https://gist.github.com/wbern/f7f45f1b940a13f9b9389e23029608e7#file-jenkinsfile-rush-publish-step-L5
PipelineUtils pipelineUtils = new PipelineUtils()
String githubCredentials = 'my-vault-app-credential-name'
String githubSshAgentCredentialId = '00000000-0000-0000-0000-000000000000'
String githubUserEmail = 'FIXME+john.doe@mycorp.com'
String githubUserName = 'MyApp Team'
String repoHeadBranchName = 'master'
String nexusRegistry = 'https://nexus.example.org/repository/npm-all/'
String slackChannel = '#myappteam-ci'
def branchName = pipelineUtils.getBranchName()
def branchNameSlug = branchName.toLowerCase().replaceAll('[^a-z0-9]', '-')
def repositoryUrl = pipelineUtils.getRepositoryUrl()
// git@github.com:mycorp/repo-example-sandbox.git => 'https://github.com/mycorp/repo-example-sandbox
String repositoryUrlWeb = repositoryUrl.replaceAll('git@github.com:mycorp', 'https://github.com/mycorp').replaceAll('.git\$', '')
String projectSlug = repositoryUrl.tokenize('/').last().replaceFirst(".git\$", '')
Map pipelineVars = [
/**
* This might happen when we're on a branch, where all test pass
* AND we are ready for merging on master.
*/
readyToPublish: false,
]
pipeline {
environment {
CI_SERVER = 'yes-please so that @renoirb/conventions-use-bili to be in CI mode'
GITHUB_REPO_URL = "${repositoryUrl}"
GITHUB_REPO_URL_WEB = "${repositoryUrlWeb}"
GITHUB_REPO_BRANCH = "${branchName}"
GITHUB_REPO_BRANCH_SLUG = "${branchNameSlug}"
SLACK_CHANNEL = "${slackChannel}"
GITHUB_ACTION = 'Jenkins'
}
options {
timestamps()
skipStagesAfterUnstable()
ansiColor('xterm')
timeout(time: 120, unit: 'MINUTES')
buildDiscarder(logRotator(daysToKeepStr: '10', numToKeepStr: '30'))
skipDefaultCheckout()
}
agent {
docker {
label 'ec2-fleet'
image 'jcr.io/builder/node:14-3.1.2'
args '-e HOME=/tmp --group-add docker -v /var/run/docker.sock:/var/run/docker.sock:rw'
}
}
// https://rushjs.io/pages/maintainer/enabling_ci_builds/
stages {
stage('Checkout') {
steps {
script {
println "=== On stage 0 Checkout (${branchName}) ===\n"
scmVars = checkout([
$class: 'GitSCM',
userRemoteConfigs: [
[
name: 'origin',
refspec: "+refs/heads/${branchName}:refs/origin/${branchName} +refs/heads/${repoHeadBranchName}:refs/origin/${repoHeadBranchName}",
credentialsId: githubCredentials,
url: env.GITHUB_REPO_URL
],
],
branches: [
[name: "refs/heads/${branchName}"],
],
browser: [
$class: 'GithubWeb',
repoUrl: repositoryUrlWeb,
],
doGenerateSubmoduleConfigurations: false,
extensions: [
[$class: 'CleanBeforeCheckout']
],
submoduleCfg: [],
])
for (arg in scmVars) {
env["${arg.key}"] = arg.value.toString()
}
// Some user custom scripts rely on GIT_COMMIT_MESSAGE. Do not remove it event if unused in this script
String commitMessage = sh(returnStdout: true, script: 'git log -1 --pretty=%B | cat').trim()
env.GIT_COMMIT_MESSAGE = commitMessage
githubUserName = sh(returnStdout: true, script: "git log --name-status HEAD^..HEAD --format='%an' | head -n 1").trim()
env.GIT_COMMITTER_NAME = githubUserName
sh "git config --global user.name '${githubUserName}'"
githubUserEmail = sh(returnStdout: true, script: "git log --name-status HEAD^..HEAD --format='%ae' | head -n 1").trim()
env.GIT_COMMITTER_EMAIL = githubUserEmail
sh "git config --global user.email '${githubUserEmail}'"
println '\n---- BEGIN DEBUGGING ----\n'
sh 'node common/scripts/install-run-rush.js meta'
sh 'printenv | sort'
sh 'git config --global --list'
sh 'git config --local --list'
sh 'git remote -v'
sh 'git branch'
sh 'git branch -a'
sh 'test -f .git/config && cat .git/config'
println '---- END DEBUGGING ----\n'
sh 'node common/scripts/install-run-rush.js install'
// sh 'node common/scripts/install-run-rush.js install --debug-package-manager'
}
}
post {
failure {
script {
slack.danger channel: slackChannel, stage: 'Setup', project: projectSlug
}
}
}
}
stage('Build') {
when {
not {
anyOf {
/**
* Rush when doing the "rush publish" command creates and pushes a timestamped branch starting with "publish-" (e.g. publish-1628890734019)
* we do not want to run CI on those temporary branches
*/
expression { return branchName =~ '^publish-' }
/**
* Rush when doing the "rush version --bump ..." command creates and pushes a timestamped branch starting with "version/bump-" (e.g. version/bump-1628890734019)
* we do not want to run CI on those temporary branches.
* Also, i'm unsure if Jenkinsfile =~ RegEx will require escaping slash. So we'll use the branchNameSlug.
*/
expression { return branchNameSlug =~ '^version-bump-' }
}
}
}
steps {
script {
println '=== On stage 2 Build ===\n'
sh 'node common/scripts/install-run-rush.js rebuild --verbose'
}
}
post {
failure {
script {
slack.danger channel: slackChannel, stage: 'Build', project: projectSlug
}
}
}
}
stage('Verify') {
failFast true
parallel {
stage('Lint') {
steps {
script {
println 'Verify 1/2 Lint'
withCredentials([usernamePassword(credentialsId: githubCredentials, usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')]) {
sh "node common/scripts/install-run-rush.js prettier --branch origin/${repoHeadBranchName} --check"
}
}
}
}
stage('Test') {
steps {
script {
println 'Verify 2/2 Test'
sh 'node common/scripts/install-run-rush.js test'
}
}
}
stage('Changelog') {
when {
not {
anyOf {
branch repoHeadBranchName
expression { return branchName =~ '^publish-' }
expression { return branchNameSlug =~ '^version-bump-' }
}
}
}
steps {
script {
println 'Verify 3/2 Changelog'
sh 'node common/scripts/install-run-rush.js list --version --path'
// env['DEBUG'] = '*'
// https://nodejs.org/dist/latest-v14.x/docs/api/all.html#cli_node_debug_module
// https://www.alxolr.com/articles/debugging-tools-and-practices-in-node-js#debug-nodejs-internals-with-node_debug
// env['NODE_DEBUG'] = 'net,fs,http'
// Bookmarks:
// - https://gist.github.com/wbern/f7f45f1b940a13f9b9389e23029608e7#file-jenkinsfile-rush-publish-step-L5
// - https://github.com/microsoft/rushstack/issues/1558#issuecomment-545707200
sshagent([githubSshAgentCredentialId]) {
println '---- BEGIN DEBUGGING ----\n'
// sh 'node common/scripts/install-run-rush.js meta'
// sh 'printenv | sort'
// sh 'test -f .git/config && cat .git/config'
sh 'git status'
sh 'git log --pretty=format:"%C(blue)%ad%Creset %C(yellow)%h%C(green)%d%Creset %C(blue)%s %C(magenta) [%an]%Creset"'
println '---- END DEBUGGING ----\n'
// sh "node common/scripts/install-run-rush.js change --target-branch origin/${repoHeadBranchName} --verify --no-fetch"
// outcome = sh(returnStatus: true, script: 'node common/scripts/install-run-rush.js change --verify --no-fetch')
outcome = sh(returnStatus: true, script: 'node common/scripts/install-run-rush.js change --verify')
pipelineVars.readyToPublish = outcome == 0
println "=== readyToPublish?: ${pipelineVars.readyToPublish} ===="
if (outcome > 0) {
error "The branch ${branchName} is not ready for releasing, it misses changelog"
}
}
}
}
}
}
post {
failure {
script {
slack.danger channel: slackChannel, stage: 'Verify', project: projectSlug
}
}
}
}
/**
* Any change MUST be done BEFORE being merged to the HEAD branch (e.g. master)
* meaning that publish should be part of it too but not add changes
*/
stage('Release') {
when {
allOf {
expression { return pipelineVars.readyToPublish }
}
}
stages {
stage('Publish') {
when {
/**
* In contrast to rush automatically generated branch "publish-", we will have to create a
* "release-" branch that should be forked from master to do the publishing to Nexus
*/
expression { return branchName =~ '^release-' }
}
steps {
script {
println '=== On stage 5 Release Publish ===\n'
// We get ENOBUFS
// https://developer.ibm.com/articles/nodejs-memory-management-in-container-environments/
// sh "test -f /sys/fs/cgroup/memory/memory.limit_in_bytes && cat /sys/fs/cgroup/memory/memory.limit_in_bytes"
withCredentials([usernamePassword(credentialsId: githubCredentials, usernameVariable: 'GITHUB_USER', passwordVariable: 'GITHUB_TOKEN')]) {
sshagent([githubSshAgentCredentialId]) {
println '---------- BEGIN DEBUGGING ----------\n'
println '--- THE FOLLOWING SHOULD BE EMPTY ---\n'
println '-------------------------------------\n'
sh 'git ls-files --others --exclude-standard'
println '---------- END DEBUGGING ----------\n'
// What we want is to do is to make rush publish to NPM and write the changelog, but not push to master, let GitHub do the squashing
// Lets make the tagging from when we are on master instead.
// Take changes, publish to Nexus
sh "node common/scripts/install-run-rush.js publish --apply --publish --regenerate-changelogs --set-access-level public --registry ${nexusRegistry}"
// sh "node common/scripts/install-run-rush.js publish --publish --apply -b ${repoHeadBranchName} --add-commit-details --commit ${env.GIT_COMMIT}"
// Make changes to Git, push
sh "node common/scripts/install-run-rush.js publish --apply --add-commit-details --ignore-git-hooks --target-branch ${branchName}"
}
}
}
}
}
stage('Version Bump') {
when {
expression { return branchNameSlug =~ '^version-bump-' }
}
}
}
post {
failure {
script {
slack.danger channel: slackChannel, stage: 'Release', project: projectSlug
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment