Specs, get-stx.py: use 'https://dl.vrany.io/public/smalltalkx/devel' as artifact repository
/*
* Jenkins Pipeline definitions for Smalltalk/X jv-branch
*
* Following stepss are defined here:
*
* * `build()` - compiles and tests all supported configurations
* No artifacts are archived
*
* * `integration()` - like `build()` steps, then all test pass on all
* configurations, archive artifacts and push staged changes to opstream
* (if approvec by the user)
*/
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
/**
* Supported configurations. To overwrite, do:
*
* ...
* steps = load "steps.groovy"
* ...
* steps.configurations = [ 'BUILD_TARGET': 'mips-sgi-irix' ]
* ...
*
* Default value: see below.
*/
configurations = [
'BUILD_TARGET': ['i686-pc-mingw32' , 'x86_64-w64-mingw32', 'i686-pc-linux-gnu', 'x86_64-pc-linux-gnu' ]
]
/** Repository (forest) to build. To overwrite do:
*
* ...
* steps = load "steps.groovy"
* ...
* steps.repo = "https://swing.fit.cvut.cz/hg/stx-goodies-builder-rake"
* ...
*
* Default value (if not set):
* For normal jobs this is the value of 'Repository' field from SCM configuration.
*
* Due to Jenkins internals, the fallback to use SCM configuration must be done
* by 'user' of this variable (i.e., use `repo != null ? repo : scm.getSource())
*/
repo = null; // null means `scm.getSource()`
/** Branch to build. To overwrite do:
*
* ...
* steps = load "steps.groovy"
* ...
* steps.branch = "issue-123"
* ...
*
* Default value:
* For multibranch jobs, this is the value of BRANCH_NAME environment variable.
* For normal jobs this is the value of 'Branch' field from SCM configuration.
*
* Due to Jenkins internals, the fallback to use SCM configuration must be done
* by 'user' of this variable (i.e., use `branch != null ? branch : scm.getBranch())
*/
branch = env.BRANCH_NAME
/**
* Workspace to use. To overwrite do:
*
* ...
* steps = load "steps.groovy"
* ...
* steps.workspace = "some-other-job"
* ...
*
* Default value:
* Name of current job.
*/
workspace = env.JOB_NAME
/*
* "Default" steps:
*/
def build() {
stage ( "Build" ) {
matrix ( configurations ) {
stage ( "Checkout - {${env.BUILD_TARGET}} " ) {
credentials_to_use = 'none'
try {
credentials_to_use = scm.getCredentialsId()
} catch(Exception e) {
// Nothing, use 'none'
}
sshagent(credentials: [credentials_to_use], ignoreMissing: true) {
repo_to_use = repo != null ? repo : scm.getSource()
branch_to_use = branch != null ? branch : scm.getBranch()
sh """
if [ -f build.rb ]; then
hg pull --ssh "ssh -o 'StrictHostKeyChecking off'" ${repo_to_use}
else
hg clone --ssh "ssh -o 'StrictHostKeyChecking off'" ${repo_to_use} .
fi
hg up ${branch_to_use}
"""
sh "ruby build.rb --project \"stx:jv-branch\" --build-target ${env.BUILD_TARGET} update"
}
}
stage ( "Compile - {${env.BUILD_TARGET}}") {
sh "ruby build.rb --project \"stx:jv-branch\" --build-target ${env.BUILD_TARGET} compile"
}
}
}
stage ( "Test" ) {
matrix ( configurations ) {
stage ( "Test - {${env.BUILD_TARGET}}") {
/*
* Some tests requires display, so:
*
* * on *NIX hosts, launch Xvfb
* * on Windows, do nothing. Windows slave must be configured
* so it has an access to interactive window station (which
* means it has to run under user session, not as a service)
*/
if ( isUnix() ) {
wrap([$class: 'Xvfb', autoDisplayName: true, additionalOptions: '-screen 0 1024x768x24 -pixdepths 24 4 8 15 16 32', parallelBuild: true]) {
sh "ruby build.rb --project \"stx:jv-branch\" --build-target ${env.BUILD_TARGET} test"
}
} else {
sh "ruby build.rb --project \"stx:jv-branch\" --build-target ${env.BUILD_TARGET} test"
}
junit allowEmptyResults: true, testResults: "reports/*build${env.BUILD_NUMBER}*.xml"
}
}
}
}
/*
* "Integration" steps
*/
def integration() {
build()
/*
* If a single test fails, abort the steps. There's no point
* archiving a broken build.
*/
println "Smalltalk/X built, job status is: ${currentBuild.result}"
if ( currentBuild.result == 'UNSTABLE' ) {
return;
}
artifacts()
/*
* Check if there are changes to be pushed to upstream. If so,
* ask user to approve that push
*/
if ( changes() ) {
def integrate = false;
if (env.JENKINS_URL == "https://swing.fit.cvut.cz/jenkins/") {
integrate = true;
} else {
integrate = input(message: 'Integrate all staged changes to upstream?',
parameters: [
booleanParam(name: "Integrate changes",
description: 'If checked, all staged changes will be pushed to an upstream repository',
defaultValue: true)]);
}
if ( integrate ) {
push()
}
}
publish("devel")
}
/*
* Publish built artifacts to download server
*/
def publish(dir = "private-builds") {
build(job: 'stx_jv_publish',
parameters: [
string(name: 'job', value: env.JOB_NAME),
string(name: 'bld', value: env.BUILD_NUMBER),
string(name: 'srv', value: env.STX_PUBLISH_SERVER),
string(name: 'dir', value: "${env.STX_PUBLISH_DIRECTORY}/${dir}")
],
wait: false)
}
/*
* Utility. Return true, if there are any changes to be pushed to an upstream,
* false othervise.
*/
def changes() {
changes = false;
any ( configurations ) {
withCredentialsForUpstream() { user, pass ->
status = sh ( script: "rake \"workflow:out-upstream[${user}, ${pass}]\"", returnStatus: true)
}
changes = status == 0;
}
return changes;
}
def combine(configurations, axes = null, axis = 0, partial = new HashMap(), combinations = []) {
def axes0 = axes
if (axes0 == null) {
axes0 = configurations.keySet().toArray();
}
if ( axis < axes0.length ) {
for ( value in configurations[axes0[axis]] ) {
def combined = partial.clone()
combined[axes0[axis]] = value
combine(configurations, axes0, axis + 1, combined, combinations)
}
} else {
combinations.add(partial)
}
return combinations;
}
def matrix(configurations, block) {
def combinations = combine(configurations).toArray()
def branches = [failFast: true]
for (i = 0; i < combinations.length; i++) {
def index = i
def conf = combinations[i];
branches["${conf.BUILD_TARGET}"] = {
node ( conf.BUILD_TARGET ) {
def newEnv = []
for (k in conf.keySet()) {
newEnv.add("${k}=${conf[k]}")
}
withEnv ( newEnv ) {
ws ("workspace/${workspace}/${env.BUILD_TARGET}") {
block()
}
}
}
}
}
parallel branches
}
def any(configurations, block) {
def axes = configurations.keySet().toArray()
def conf = [:]
for (axis in axes) {
conf[axis] = configurations[axis][0]
}
node ( conf.BUILD_TARGET ) {
def newEnv = []
for (k in conf.keySet()) {
newEnv.add("${k}=${conf[k]}")
}
withEnv ( newEnv ) {
ws ("workspace/${workspace}/${env.BUILD_TARGET}") {
block()
}
}
}
}
def artifacts() {
matrix ( configurations ) {
stage ( "Artifacts - {${env.BUILD_TARGET}}") {
if (env.BUILD_TARGET != 'i686-pc-mingw32' && env.BUILD_TARGET != 'i686-pc-linux-gnu') {
sh "ruby build.rb --project \"stx:jv-branch\" --build-target ${env.BUILD_TARGET} artifacts"
archiveArtifacts artifacts: "artifacts/*build${env.BUILD_NUMBER}*.zip, artifacts/*build${env.BUILD_NUMBER}*.bz2, artifacts/*build${env.BUILD_NUMBER}*.sha256", fingerprint: true//, onlyIfSuccessful: true
} else {
echo "Artifacts for ${env.BUILD_TARGET} are no longer archived. Use 64bit."
}
}
}
}
/**
* Push changes to upstream reporitory. To be called after a successful
* build. See #build()
*/
def push() {
any ( configurations ) {
stage ( "Push to upstream" ) {
withCredentialsForUpstream { user, pass ->
sh "rake \"workflow:push-upstream[${user}, ${pass}]\""
}
}
}
}
/**
* Cleanup the workspace
*/
def cleanup() {
matrix ( configurations ) {
stage ( "Cleanup - {${env.BUILD_TARGET}}") {
cleanWs cleanWhenAborted: false, cleanWhenFailure: false, cleanWhenNotBuilt: false, cleanWhenUnstable: false
}
}
}
/*
* Utility. Executes given block with credentials for upstream repository.
*/
def withCredentialsForUpstream(block) {
/*
* Kludge: Upstream repositories may be on a different (public)
* server. To access repos on there, I (JV) don't
* want to use the same key / password as for checkouts from
* staging repositories,
*
* Therefore, also look for another credentials with ID
* `workflow:push-upstream`. If it exists, then use these to
* push to upstrem repository. If no such credentials exist,
* use standard credentials.
*
* Also, the push-upstream also pushed back to repositories
* we checked out (staging repositories). This is to correctly
* propagate phase changes back, especially when draft changeset
* become public.
*
* So, we need to use both while pushing.
*
* Kludge: we assume here that "staging" credentials are using
* SSH. This is a limitation coming from Rakefiles.
*/
def currentJob = Jenkins.getInstance().getItemByFullName(env.JOB_NAME)
def stagingCredsId = "workflow-push-upstream";
def stagingCreds = null;
def upstreamCred = null;
for (StandardUsernameCredentials c : CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class, currentJob)) {
if (c.getId().equals(scm.getCredentialsId())) {
stagingCreds = c;
} else if (c.getId().equals(stagingCredsId)) {
upstreamCred = c;
}
}
//
// Validate credentials
//
if (stagingCreds == null) {
error("Staging repository credentials not found (id ${scm.getCredentialsId()})")
} else if (! (stagingCreds instanceof SSHUserPrivateKey)) {
error("Staging repository credentials are not of type 'SSH private key' (id ${scm.getCredentialsId()})")
}
if (upstreamCred == null) {
println "Upstream repository credentials not found (id ${stagingCredsId}), using staging credentials for upstream"
upstreamCred = stagingCreds;
}
println "Using staging repository credentials ${stagingCreds.getId()}: ${stagingCreds.getDescription()}"
println "Using upstream repository credentials ${upstreamCred.getId()}: ${upstreamCred.getDescription()}"
sshagent([ stagingCreds.getId() ]) {
if (upstreamCred instanceof SSHUserPrivateKey) {
sshagent([ upstreamCred.getId() ]) {
block(null, null)
}
} else {
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: upstreamCred.getId(), passwordVariable: 'pass', usernameVariable: 'user']]) {
block(user, pass)
}
}
}
}
return this;