Defining closures in a loop - how to prevent a variable mutating - jenkins-workflow

I have the following workflow code:
stage "Deploy to Prod Sites"
def branches = [:]
for (i=0; i< sites.size(); i++) {
site=sites[i]
echo "site = $site"
branches[site] = {
buildJob (app, site, revision)
}
}
parallel branches
When I run this, each branch has the same value for site.
How do I ensure that the value of site contains the value from when the closure was defined rather than run?

the answer is def site
so
stage "Deploy to Prod Sites"
def branches = [:]
for (i=0; i< sites.size(); i++) {
def site=sites[i]
echo "site = $site"
branches[site] = {
buildJob (app, site, revision)
}
}
parallel branches
This now works as expected.

Related

Is there a way to figure out what file in GiHub causes the build in Jenkins

I currently set up Jenkins and GitHub so that when a push is sent to GitHub a build is automatically triggered. Is there a way to get the name of the file that causes the build. For example, if I have a file in GitHub named test.txt in a repo called TestRepo and I commit a change in test.txt, how could I figure out the build was caused by a change in test.txt and not due to another file in the repo.
Assuming you are using the GitHub plugin for polling, the build will be triggered by a commit/s not by a specific file. You can see the changes log directly in Jenkins in 'Changes' tab or if you need to get the list of files in the pipeline you can try to have a step with something like 'git diff --name-only $GIT_PREVIOUS_COMMIT $GIT_COMMIT' after the checkout step.
You can use the following Groovy script to get the files changed. You can simply retrieve the changeset with currentBuild.changeSets and process the changeset as shown below to get the files that changed.
pipeline {
agent any
stages {
stage('Build') {
steps {
git (url: 'https://github.com/xxxx/sample.git', branch: 'main')
script {
def changedFiles = getFilesChanged()
echo "${changedFiles}"
}
}
}
}
}
def getFilesChanged() {
def filesList = []
def changeLogSets = currentBuild.changeSets
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
def files = new ArrayList(entry.affectedFiles)
for (int k = 0; k < files.size(); k++) {
def file = files[k]
filesList.add(file.path)
}
}
}
return filesList
}

Jenkins checking change in repository before build

I am using a monorepo for my microservices and i have a pipeline for each service.I have a webhook that notifies jenkins whenever a change is made in the repository. So the problem now is i want to check a particular folder in the repository if there is a change before some operations. Thanks
Solution 01 Processing the Webhook Request
When the Webhook notifies Jenkins it will send a bunch of details to Jenkins. Including the change logs. You should be receiving the below details. So as the initial step you can process the details coming from Github and determine whether the folder you are looking for is changed.
"added": [ ],
"removed": [ ],
"modified": ["test/testfile.yaml"]
You can use this as a reference.
Solution 02 without Processng the Webhook
It seems you cannot get this done with a simple pattern matching as directory name checking is not supported at the moment. But as a workaround, you can use something like the below. You can specify the directory name you want to check as folderName = "DIR_NAME"
pipeline {
agent any
stages {
stage('Cloning') {
steps {
// Get some code from a GitHub repository
git branch: 'main', url: 'https://github.com/yasassri/sample.git'
}
}
stage('Example Deploy') {
when { expression {
def changeLogSets = currentBuild.changeSets
def folderName = "testdir2"
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
def files = new ArrayList(entry.affectedFiles)
for (int k = 0; k < files.size(); k++) {
def file = files[k]
if(file.path.contains(folderName)){
return true
}
}
}
}
return false
}
}
steps {
echo 'Deploying==================='
}
}
}
}
References: https://support.cloudbees.com/hc/en-us/articles/217630098-How-to-access-Changelogs-in-a-Pipeline-Job-

amend all comments of all commits of a repository

I'm moving our on premise tfs repositories to visualstudio online. In the process I'd prefer to convert everything to git.
I found this (*) post online and everything works. But now I would like to use libgit2sharp to amend all the comments so to point to the correct work items.
I've cobbled together a bit of code that should do the trick:
Dictionary<string,string> links; //contains all links between new and old workitems, keys: old workitems, values: new workitems
using (var repo = new Repository(#"D:\gittfs\testproject"))
{
foreach (var commit in repo.Commits)
{
var commitMessage = commit.Message;
var regex = new Regex(#"#[0-9]*");
foreach (Match match in regex.Matches(commitMessage))
{
string newId;
if (links.TryGetValue(match.ToString(), out newId))
{
commitMessage = commitMessage.Replace(match.ToString(), newId);
}
}
var c = repo.Commit(commitMessage, new CommitOptions { AmendPreviousCommit = true });
}
}
This code runs without a problem and if I compare c.Message with commit.Message, I can see several of these being replaced. The problem is that after the program has ran, none of the amended commits are in the repository. So I think I'm still doing something wrong?
(*)https://www.microsoft.com/en-gb/developers/articles/week02mar2014/migrating-a-tfs-tfvc-based-team-project-to-a-git-team-project-retaining-as-much-source-and-work-item-history-as-possible/
I think you might rather be after some git filter-branch like feature.
LibGit2Sharp exposes this through the HistoryRewriter type.
You can peek at the FilterBranchFixture test suite for inspiration.
following code did the trick for me. thnx nulltoken!
var rewriteHistoryOptions = new RewriteHistoryOptions()
{
CommitHeaderRewriter = commit =>
{
var commitMessage = commit.Message;
bool stuffreplaced = false;
var r = new Regex(#"#[0-9]*\ ");
foreach (Match match in r.Matches(commit.Message))
{
string value;
if (links.TryGetValue(match.ToString().TrimEnd(), out value))
{
commitMessage = commitMessage.Replace(match.ToString().Trim(), value);
Console.WriteLine(commit.Id + ": " + match.ToString() + " replaced by " + value);
stuffreplaced = true;
counter++;
}
}
if (stuffreplaced)
{
return CommitRewriteInfo.From(commit, message: commitMessage);
}
else
{
return CommitRewriteInfo.From(commit);
}
}
};
repo.Refs.RewriteHistory(rewriteHistoryOptions, repo.Commits);

finding "active" branch in EGIT repository

I have to modify a faulty program/feature, which is an extension to org.eclipse.jgit
the program overrides the close method of the PushResultDialog and passes the Repository to an method.
Here i want to find the branch which was actually pushed/checked out. I am only interested if its the master, if not i don't wanna do anything.
else i need the list of the pushed files.
First the program looked like this :
head = repo.resolve(Constants.HEAD);
RevCommit commit = rw.parseCommit(head);
PersonIdent committerIdent = commit.getCommitterIdent();
sCommitter = committerIdent.getName();
String sBranch = "?";
for (Map.Entry<String, Ref> e : repo.getAllRefs().entrySet()) {
if (e.getKey().startsWith(Constants.R_HEADS)) {
Ref ref = e.getValue();
if (rw.isMergedInto(commit, rw.parseCommit(ref.getObjectId()))) {
sTemp = ref.getName();
int i = sTemp.lastIndexOf('/');
if (i == -1)
continue;
sBranch = sTemp.substring(i + 1);
System.out.println("Ref " + sBranch
+ " < contains > " + commit);
if (sBranch.equalsIgnoreCase("master")) {
break;
} else {
return;
}
}
}
}
RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
DiffFormatter df = new DiffFormatter(
DisabledOutputStream.INSTANCE);
df.setRepository(repo);
df.setDiffComparator(RawTextComparator.DEFAULT);
df.setDetectRenames(true);
List<DiffEntry> diffs = df.scan(parent.getTree(),
commit.getTree());
for (DiffEntry diff : diffs) {
sTemp = diff.getNewPath();
pushedObjects.add(sTemp);
}
now .. this works as long as the workflow in eclipse is just "checkout master" "pull" "merge branch" "push"
any other order seems to mess with the order of the branches in the set, and it stumbles over this "else" :
if (sBranch.equalsIgnoreCase("master")) {
break;
} else {
return;
}
the question is : is there an eaasy method to pick the right branch?
Ok, i think i can simply look at the HEAD in the set:
e.getKey().startsWith(Constants.HEAD)
and then parse the branchname, that should always be the branch which is checked out, if (in my case),if it is the master, i am happy.

Clearing special permissions from folders in a branch

We're a fairly large project with a single trunk branch. Most of it uses the default permissions, but a few folders have custom permissions - say, only "Builders" group is allowed to check-in.
We want to allow people to create their own private branches out of trunk, where they can freely check-in and merge later (hopefully often). However, creating a branch, the special permissions are copied along with the folders, meaning that people can't freely check-in into their branch.
Is there a way to clear special permissions from a branch, or a folder?
Is there a way to do so automatically, so anyone creating a branch under /private/** will not encounter this problem?
I found out about tf permission (Example: tf permission /inherit:yes itemSpec). However, the /recursive switch doesn't work with it. I guess I could write something that runs it recursively...
Edit: I finally got around to writing a tool for it:
static int Main(string[] args)
{
if (args.Length == 0 || args.Any(a => !a.StartsWith("$/")))
{
Console.WriteLine("Removes all explicit permissions and enables inheritance for a subtree.\n"
+ "Example: " + Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location) + " $/project/path1 $/project/path2");
return 3;
}
WorkspaceInfo wi = Workstation.Current.GetLocalWorkspaceInfo(Environment.CurrentDirectory);
if (wi == null)
{
Console.WriteLine("Can't determine workspace for current directory: " + Environment.CurrentDirectory);
return 2;
}
var Tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(wi.ServerUri);
VersionControlServer VersionControlServer = Tfs.GetService<VersionControlServer>();
Console.WriteLine("Server: {0} Getting permissions...", wi.ServerUri);
ItemSecurity[] perms = VersionControlServer.GetPermissions(args, RecursionType.Full);
Console.WriteLine("Will remove explicit permissions from the following items:");
var changes = new List<SecurityChange>();
foreach (ItemSecurity perm in perms)
{
Console.WriteLine(" " + perm.ServerItem);
changes.Add(new InheritanceChange(perm.ServerItem, inherit: true));
foreach (AccessEntry e in perm.Entries)
{
changes.Add(new PermissionChange(perm.ServerItem, e.IdentityName, null, null, PermissionChange.AllItemPermissions));
}
}
Console.WriteLine("Enter to confirm:");
Console.ReadLine();
var successfulchanges = VersionControlServer.SetPermissions(changes.ToArray());
if (successfulchanges.Length == changes.Count)
{
Console.WriteLine("Explicit permissions removed from all items");
return 0;
}
else
{
Console.WriteLine("Explicit permissions removed only from:");
foreach (var c in successfulchanges)
{
Console.WriteLine(" " + c.Item);
}
return 1;
}
}