Reputation: 590
I have a jenkins job for my puppet stuff and really all those jobs do are validation and deployment to puppet. The question I have is that as part of that build I'd like to force an update via bundle
which will update the puppetfile
which needs to then be checked in as part of that PR.
One of the concerns I have is that the commit to the PR will end up triggering another build, which will trigger another update... so on and so forth.
The only thing I was able to find in regards to this was that you can control to some extent how the jobs run (restart/stop and new from new commits) but those seem to be blanket settings and not specific to the job instance and no way that I could find via the pipeline stuff to control that.
I have to imagine this is something that people do and that there is a method to this but my google foo is failing or I'm searching on the wrong terms.
Also, if it matters we're using github enterprise onsite.
Upvotes: 0
Views: 220
Reputation: 3483
Disclaimer: There might be a better way than what I am about to describe (it seems heavy handed) but we also found the built-in Jenkins exclusion settings insufficient. Primarily because it only applied to triggered builds not scheduled Multi-branch pipeline scans. Also this only applies to Pipeline Groovy DSL jobs; not to freestyle jobs or declarative Pipeline jobs.
Summary
Best explained in pseudocode
if git changed files since last run == 0 then
no need to change puppetfile
end build
endif
if 1 file changed and it is puppetfile then
if the contents of the change is just the auto-changed puppetfile stuff
no need to change puppetfile again
end build
endif
endif
if we get here then more stuff changed so do the auto-change puppetfile stuff
Details
For a concrete implementation I will use a Javascript project's package.json
as an example but it's exactly the same situation as the puppetfile
. Just replace package.json
with puppetfile
and version:
with whatever content change you auto-generate that should not trigger another build.
First we need some utility methods:
/**
* Utility method to run git and handle splitting its output
*/
def git( command ) {
def output = []
outputRaw = sh(
script: "git $command",
returnStdout: true
)
if ( !outputRaw.empty ) {
output = outputRaw.split('\n')
}
return output
}
/**
* The names of files changed since the last successful build
*/
def getFilesChangedSinceLastSuccessfulBuild() {
def filesChanged = []
if (lastSuccessfulCommit) {
filesChanged = git "diff --name-only $currentCommit '^$lastSuccessfulCommit'"
}
echo "DEBUG: Files changed $filesChanged (size ${filesChanged.size()})"
return filesChanged
}
/**
* Get changes as a list without context
*/
def getContextFreeChanges() {
def contentsChanged = []
if (lastSuccessfulCommit) {
contentsChanged = git "diff --unified=0 $lastSuccessfulCommit $currentCommit | egrep '^\\+ ' || exit 0"
}
echo "DEBUG: Contents changed: $contentsChanged (size ${contentsChanged.size()})"
return contentsChanged
}
Then we take those and create needToBumpVersion()
(rename as appropriate) which ties it all together to figure out of any auto-generate changes need to happen:
/**
* Do we need to actually increment version in package.json
*/
def needToBumpVersion() {
def filesChanged = getFilesChangedSinceLastSuccessfulBuild()
if (filesChanged.size() == 0) {
echo "INFO: No files changed, no need to bump version"
return false
}
if (filesChanged.size() == 1 && filesChanged[0] == 'package.json') {
def contentsChanged = getContextFreeChanges()
if (contentsChanged.size() == 1 && contentsChanged[0] =~ /"version":/) {
echo "INFO: Don't need to bump version, it has already been done"
return false
}
}
echo "INFO: We do need to bump the version, more than just package.json version string changed"
return true
}
and finally you can then use this method to figure out whether to make the auto-generated changes and commit them:
if (needToBumpVersion()) {
// Do bump here
}
Upvotes: 1