Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize this plugin #31

Merged
merged 22 commits into from
Dec 11, 2015
Merged

Modernize this plugin #31

merged 22 commits into from
Dec 11, 2015

Conversation

tomaswolf
Copy link
Contributor

This is a fairly large pull request, but I believe if you go through it commit by commit, you'll be able to follow what I did. I did try to have a clean and understandable commit history here...

The most fundamental change is a switch on the Jenkins base version from 1.409 and Java 5 to 1.565.3 and to Java 6 (minimum requirement for Jenkins since 1.520). I just didn't get the original version from master to build and run the tests successfully with the 1.409 POM. 1.565.3 is an LTS version from October 2014.

After that, I started fixing things that prevented me from running this plugin in our in-house setup. The individual commit messages explain it all.

Making this work for jobs with blanks in the name (commit cb2f319b) required rewriting more of maven-scm since the root cause was in there.

The last two commits attempt to fix JENKINS-26204. I never could reproduce that problem, but in looking through the code for a possible cause I stumbled upon one possible race condition (though that should not affect an installed plugin since it doesn't use "synchronous" transactions) and discovered that the path handling in the plugin suffered from the same problem as maven-scm.

Not dealt with is the "poisoned commit" problem. Currently the plugin leaves a failing commit in its queue and will retry it forever again (until Jenkins is restarted). If these retries also fail, no further commits to the SCM repository will ever succeed. It's unclear to me what the best strategy would be; just skipping the "bad" commit is no solution as it might make subsequent commits also fail. Also, such a "bad" commit may leave behind an inconsistent file and repository state (some paths added/staged, some not). Probably the best strategy to deal with failing commits is to stop the plugin (no more recording), require the user to fix it, and then let the user restart the plugin. At least that would not require the user to stop and restart the whole Jenkins.

tomaswolf added 19 commits June 29, 2015 22:15
I did not manage to get this going with the 1.409 POM. And anyway
newer Jenkins versions have some changes that this plugin should
account for.

POM changes:

* Increase minimum Jenkins version to 1.565.3 (LTS from October 2014)
  and adapt POM as needed to get tests to run successfully in my
  Eclipse/m2e setup. Also clean out some obsolete stuff relating to
  easymock, which isn't used anymore.
* Add a license (MIT).
* Include some Eclipes m2e-specific "lifecycle mappings". Unfortunately
  that's needed for Eclipse users, otherwise nothing will work.
* Fix some versions.
* Resolve the sisu-vs.-plexus problems by using a resent
  sisu-inject-plexus and making sure that comes first and thus wins.
  Exclude the very old plexus container stuff pulled in via
  maven-scm-manager-plexus; sisu-inject-plexus provides a newer one.
  Didn't touch the SCMManagerFactory, though.
  
Code changes:

* Mock Jenkins, too, not just Hudson.

Miscellaneous:

* Add the Eclipse m2e annotation processor file to .gitignore
It's the minimum requirement for Jenkins since 1.520.
Since PR jenkinsci#19 at GitHub (commit 141cc2b), we don't get a commit message
dialog anymore for changes to job configurations.

The regexp looks broken with the initial slashes and only a single
[^/]. With the Cloudbees Folders plugin jobs can be not only at

  jobs/Some_Job

but also at

  jobs/Folder1/jobs/Folder2/jobs/Some_Job

This commit simplifies the regexp and fixes it.
Having an ant pattern starting with "**" was a _very_ bad idea. It
makes the plugin search way too many places, including even its own
SCM workspace. Which then leads to recursively including ever deeper
SCM workspace hierarchies in the SCM workspace and in the repository.
Also, such an ant pattern makes the plugin scan all jobs' workspaces,
which can take a long time and may discover bogus matches. (They're by
default at $JENKINS_HOME/workspace.)

The Cloudbees Folders plugin has all jobs under $JENKINS_HOME/jobs,
and possibly nested as in

  jobs/Folder1/jobs/SomeProject
  
Both Jobs and Folders are Saveables; folders also have a config.xml.

Source changes:

* Introduce a new ConfigurationEntityMatcher that matches and
  traverses only exactly this jobs/XXX/jobs/YYY/jobs... hierarchy.
* To avoid problems with user-defined inclusions starting with * or
  even **, exclude our own SCM working directory always in ant pattern
  matching.
* Also exclude the war directory in $JENKINS_HOME.
* Undo more changes from commit 141cc2b. Since we test new for
  AbstractItem, the tests should not mock TopLevelItem. Better mock
  Job.
* Add two little test cases for config file matching for a job nested
  in a folder.
* Add minimal test cases for page URL matching for jobs.
This plugin can only put things under $JENKINS_HOME into SCM. If
workspaces are located elsewhere, it failed with an exception.

Newly, we just ignore such files. In all likelihood, users won't want
to sync workspace files anyway. The same goes for the builds directory.
People who really might want to put stuff from there into SCM can
try symlinking to their desired workspace/build roots from
$JENKINS_HOME. Restoring from SCM may, however, then produce unexpected
results.

Should fix JENKINS-18401 and related issues such as JENKINS-13593 and
JENKINS-19984.

Includes two tests for buildPathRelativeToHudsonRoot(), and use
org.junit.Assert.assertNotNull etc.instead of the hamcrest matchers.
Use Collections.emptyList() of ImmutableList.of().
With the new Jenkins baseline, we not only have hudson.*.xml but also
jenkins.*.xml.
When the plugin is dynamically loaded into a running Jenkins, it
would not properly initialize and thsu refuse to do anything until
Jenkins was restarted.

Let's initialize early, but don't do so in tests where we do manually
call init() at appropriate times.
1. JENKINS-15218: Maven SCM 1.9.1 _does_ use `git rm -r` to remove a
   directory. (At least on a machine with git 1.8.3) So I was not able
   to reproduce that particular problem.
2. JENKINS-16378: The plugin mistakenly tried to copy the full job
   directory after a job rename. That's Github issue jenkinsci#8[1]. This
   may fail catastrophically if the build directory is in the job's
   directory and contains broken symlinks, for instance for new jobs
   or a job that never failed (lastFailed will point to a non-existing
   subdirectory named "-1")
      Even when it doesn't fail, it may end up putting way too many
   things into SCM, especially if the workspaces should also be
   located in the job directories.
3. JENKINS-24881 is a duplicate of JENKINS-16378.
4. Jenkins now does have a more general "move", the plugin has to
   account for that and use onLocationChanged instead of onRenamed.
   (Also in tests!)

[1] jenkinsci#8

Changes:

* Refactored rename handling to submit only the stuff that is selected
  by some strategy.
* ConfigurationEntityMatcher: removed unused method getIncludes(),
  added a new method to match deleted paths. Added a FileSelector
  to matchingFilesFrom() to be able to restrict the traversal to
  specific subdirectories under $JENKINS_HOME.
* ChangeSet: removed registerRenamedPath (handled by transaction now).
* Transaction: change rename handling to include only matched stuff
  under the new directory
* AbstractScmSyncStrategy: createInitializationSynchronizedFileset()
  changed to account for FileSelector
* ScmSyncConfigurationPlugin: fix the need to restart Jenkins to get
  going, and provide new operations to collect files.

Tests:

* The tests were broken. Moved two tests that cannot possibly succeed
  on git but per chance can succeed on svn to the svn-specific tests.
  These tests "succeeded" in the test runs so far only because they
  never did anything--they faked a job as Item.class, which never
  triggered anything, neither before nor after commit 141cc2b. These
  tests modify the repository from outside the plugin-managed SCM
  workspace. In git, this will always require a pull. In svn, these
  two test happen to work because the outside modification is on
  different paths. It should fail if there was a path conflict, so
  these two tests are dubious anyway.
* Also removed a spurious mocking of the available strategies that
  was unused.
With all these warning markers it's a bit hard to see what's going on.
Otherwise a mistakenly typed blank before or after may break things and
leave the user puzzled about the reason.

Also give that git repository URL a form validator. Yes, if this plugin
is ever refactored to use git-plugin/git-client and Jenkins' proper
Credential handling, this will have to change or be removed again, but
I don't see this plugin being anywhere near that goal.

The validation checks input for syntactic validity and also whether git
actually can access the repository. It's still possible that pushing
something to that repository fails later on (for instance, if Jenkins'
OS user has no push permission), but at least this verifies the basic
setup and gives the user a hint that user.name and user.email should be
set, and that Jenkins' OS user may need to have a ssh key for
repository access.

I've included the validation in the plugin class itself and not in
some git specific class because the corresponding jelly form is also
the responsibility of the plugin class.

Also some minor grammar things in help texts.
In fact; I had been trapped by Eclipse's call hierarchy:
getDefaultIncludes() appeared unreferenced. In reality, it is called via
jelly from the manaualSynchronizationIncludes help text.
Add a test case for a job name starting with a dash. Is ignored; tests
will fail if it is enabled. (Bug in maven-scm: GitRemoveCommand does
not use -- to separate options from file names.)
The problem here is that this maven SCM library omits -- for git rm.

Factored out the nested class from a previous similar hack into its
own file and added a new override for GitRemoveCommand.

Enabled the test case for job name starting with a dash.
Add two more test cases for adding/deleting/renaming a job with blanks
in the name. Ignored because of maven-scm bug SCM-772.[1]

[1] https://issues.apache.org/jira/browse/SCM-772
The basic problem here is SCM-772.[1] maven-scm cannot parse the output
from git status if it contains quoted/escaped file names.

There's at least two pull requests, one attached to SCM-772 and one at
[2] aimed at fixing this; both are erroneous and don't look like they'd
go in anytime soon. (The first one would at least replace \r by \f, and
the second one only strips quotes but doesn't de-escape.)

So we fix this by providing our own commands in our own gitexe
provider and making sure that our implementation of these commands can
deal with quoted/escaped filenames.

Enabled the two test cases for job names with blanks.

Also, since we're rewriting part of maven-scm here anyway, I've also
included a minimal and proper fix for SCM-695.[3]

[1] https://issues.apache.org/jira/browse/SCM-772
[2] apache/maven-scm#26
[3] https://issues.apache.org/jira/browse/SCM-695
The latestCommitFuture is global, but was not protected against
concurrent modifications.

Code changes:

* Make sure that adding the commit to the queue and queueing the new
  job on the executor happen synchronized.
* Eliminate the global latestCommitFuture altogether. It's good enough
  to wait for the current transactions' commit future.
Same problem as in maven-scm's SCM-695: to check whether one
path refers to a parent directory of another path, it behooves one
well to handle file separators, otherwise a directory path "foo/bar"
may be seen incorrectly as a parent to be stripped from the file path
"foo/barbar/someFile.txt".
@fcamblor
Copy link
Member

fcamblor commented Jul 6, 2015

Hi, it sounds great !

I'm ashame that I didn't worked on the plugin since a long time, and don't think I will have lot of time to spend on it for maintenance.
I'll try to review the PR during my holidays (in 2 weeks), but I'll understand if you want it to be merged fast before that time.

I'm looking forward to find new maintainers on the plugin, if you want to take this responsibility, I will greatly be happy to share it given the hard work you did.
It may infuse some young blood on this plugin.

WDYT ?

@jenkinsadmin
Copy link
Member

Thank you for a pull request! Please check this document for how the Jenkins project handles pull requests

@tomaswolf
Copy link
Contributor Author

There's no hurry. The bugs fixed by this PR are all open for quite some time already, so a few more weeks won't hurt. And I can build that thing myself and install that self-built plugin :-)

I understand that you may not have much time to maintain this old plugin. That happens. As for maintaining it myself: I fear I also won't have much time for it. I already spent too much time on this :-(, even though I refrained from any major refactorings to improve separation of concerns. But also: I still see three large things to be done in this plugin, and at the moment I have no clear idea on how to tackle either of them:

  • move from maven-scm to the Jenkins plugins (SCM API, subversion plugin, git & git-client plugin). In order to not force people to have to install the git stuff if they only have svn repos, the git plugins would need to be an optional dependency, which may complicate matters further. Also,I've taken a brief look at the SCM API, and it's not entirely clear to me yet how I'd go about using that instead of maven-scm. But maven-scm also is in a sorry state and doesn't look well maintained -- lots of old patches and pull requests that got never applied...
  • the "restore" functionality needs, besides the POST issue JENKINS-19131, functionality to let the user choose from the repo history what he wants to restore. Implementing that looks like a lot of work.
  • that "poisoned commit" problem mentioned above. Evidently there's even users who _do_modify the repo directly: JENKINS-24811.

As for "young blood": I'll take that as a compliment. I did program on VAXen, though :-)).

@fcamblor
Copy link
Member

fcamblor commented Jul 6, 2015

You're right about enhancements.

I captured my vision about some of your points in JENKINS-18129 and JENKINS-18124

tomaswolf added 3 commits July 7, 2015 07:01
* ScmSyncGitCheckInCommand: typo in a code path that is never taken
  in this plugin (it always calls checkIn with only a directory, no
  files).
* Commit: check for user != null. Is that null check needed at all?
* FixedGitStatusConsumer: use simpler & more efficient File.replace()
  instead of File.replaceAll(). File.separator is guaranteed to be the
  single character File.separatorChar per its contract.
No real code changes.

Try to get a consistent indentation on all files touched in PR jenkinsci#31.
Looks like these files used 4 spaces for indentation originally, but
my Eclipse was set to use tabs.

Fixed by IDE settings to use spaces instead of tabs and reformatted all
Java files I touched. This also added some missing @OverRide
annotations, and made some single-line if-statements use blocks -- I
*never* use the short form.
In fact, this override without the @test annotation effectively
switched off this test for git. It's re-enabled now.
if (matcher.match(pattern, pathRelativeToRoot)) {
return true;
} else if (isDirectory) {
// pathRelativeFromRoot is be a directory, and the pattern end in a file name. In this case, we must claim a match.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you drop an eye on this please: https://github.com/jenkinsci/scm-sync-configuration-plugin/pull/28/files#diff-8ea250ea1ccf94b3ce908b8029599565R26

In case jobs directory is a link to a mounted volume so nextBuildNumber stays in sync with buildHistory and symlinks we don't want the scm-sync-configuration plugin to throw exceptions.

Such cases are usual when running Jenkins in Docker as a Phoenix Docker Container or simply when rehydrating the build history an keeping the nextBuildNumber in sync with rehydrated build history.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. I must say I don't quite understand the change you've referenced: I would have thought that getAbsolutePath() was a pure path operation. If we used getCanonicalPath() then that test might indeed detect symlinks, but I doubt it does with getAbsolutePath().

But anyway, check line 20 in JenkinsFilesHelper and the calls to this matches() operation here. I think that's the same test you did, just in a different place.

Also, with these changes the plugin will not synchronize build history or nextBuildNumber by default. A user would have to include those explicitly. Maybe you could explain better over at PR #28 what exactly the setup is and what exceptions were logged, and what the plugin tried to synchronize. (Note that the plugin 0.0.8 had a bug (JENKINS-16378, or issue #8 here) that mistakenly started synchronizing everything in a job's directory when a job was renamed. That should be fixed here with commit 28d4aee2 in this PR.) Finally, PR #28 appears to be based on grzegorz7's work on branch specification, which greatly confuses matters.

But I see that something went wrong with indentation. Probably my Eclipse used tabs to indent. I'll have to fix that so that the diff gets more readable.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see your PR merged then I can replay the scenario I mentioned and see whether it is still the case. It may be that the problem is not there anymore once your PR gets merged. The functionality is there and everything worked, there was just an odd log on one of the exhaustive pattern matchers, which, you may have removed now altogether.

I will go with your PR!

@cornelf
Copy link

cornelf commented Jul 7, 2015

👍 great stuff @tomaswolf

Liste de chemin \u00e0 la Ant, permettant de sp\u00e9cifier des fichiers suppl\u00e9mentaires qui seront synchronis\u00e9s avec le repository
Includes\ should\ be\ case\ sensitive\ paths\ starting\ from\ your\ JENKINS_HOME\ directory,\ without\ /\ in\ the\ beginning;\ Use\ of\ wildcards=\
Les chemins doivent \u00eatre sensibles \u00e0 la casse, bas\u00e9s depuis votre r\u00e9pertoire JENKINS_HOME, et ne d\u00e9marrant pas par un /; L''utilisation des wildcards
List\ of\ ant-like\ includes\ allowing\ to\ specify\ additional\ files\ to\ synchronize\ with\ the\ repository=Liste de chemin \u00E0 la Ant, permettant de sp\u00E9cifier des fichiers suppl\u00E9mentaires qui seront synchronis\u00E9s avec le repository
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only change here is actually this key, which needs to match the new English text in manualSynchronizationIncludes.jelly above.

I hadn't noticed the other changes; looks like eclipse automatically reformatted the properties upon saving. Sorry about that.

D'ailleurs, cela ne devrait pas être "Liste de chemins ..."?

@bdkoepke
Copy link

I've actually got a fairly large commit as well that I was almost finished working on before I saw this. @fcamblor would you be interested in adding another 'collaborator' to the project? The company I work for is very interested in the continued maintenance of this project?

@bdkoepke
Copy link

Checking out the version in this pull request, is it the correct behavior for the plugin to perform a git add but not a git commit && git push? When is git commit && git push supposed to be called? I would think that would happen once you hit 'Submit comment'.

@tomaswolf
Copy link
Contributor Author

@bdkoepke: all the actual repository operations happen in ScmSyncConfigurationBusiness.processCommitsQueue(). Line 208 commits and pushes (in git) or checks in (in svn) the changes. The plugin maintains its own commit objects, queues those and then processes this queue asynchronously.

@trevoro
Copy link

trevoro commented Aug 11, 2015

+1

@cornelf
Copy link

cornelf commented Sep 7, 2015

👍
Loads of bug-fixes and improvements in this PR, it deserves a 1.0.0 release.

@jessesanford
Copy link

+1

2 similar comments
@kleini
Copy link

kleini commented Oct 2, 2015

+1

@kyounger
Copy link

kyounger commented Oct 9, 2015

+1

@svvitale
Copy link

Any progress on getting this merged? Good stuff here!

@cmarquezrusso
Copy link

+1

@clarkstuth
Copy link

Why has this not been merged yet? I'm also running into this issue.

@kpmueller
Copy link

Please merge this :(

+1

@cmarquezrusso
Copy link

+1

@rodrigc
Copy link
Contributor

rodrigc commented Nov 5, 2015

@tomaswolf: This is good stuff. I use SCM sync heavily, and have hit JENKINS-24686 before.
Do you want to become a maintainer for this plugin so that you can merge this stuff in?
If not I can ask @fcamblor if I can be added as a maintainer for this plugin.

@tomaswolf
Copy link
Contributor Author

@rodrigc, @fcamblor: as I had written above, I fear I also won't have much time for maintaining this plugin, so it wouldn't make much sense to add me as a maintainer.

@rodrigc
Copy link
Contributor

rodrigc commented Nov 6, 2015

@tomaswolf: fair enough. This plugin is too important, and you did a lot of good work,
to just let sit there. I will step up to get your stuff in. @fcamblor: is it OK if I ask to become a maintainer for this plugin?

@rodrigc
Copy link
Contributor

rodrigc commented Nov 6, 2015

@tomaswolf: just to verify, are you using your patch to SCM Sync in a production environment?
I heavily use your plugin and have hit some of the issues you have fixed in your patch, so just want confirm.

@tomaswolf
Copy link
Contributor Author

@rodrigc: yes, I'm using this in a production environment. Jenkins 1.620, using a Gerrit-managed git repo. (Gerrit 2.11.4) Works fine for us. Apart from the problems mentioned above and explicitly not handled by this pull request (poisoned commit, no support in the plugin to restore, no way to stop and restart just the plugin, plus a few minor things such as no way to specify the branch to push to -- but there's another PR by somebody else who took a stab at that) we encountered no problems with the plugin itself.

We do occasionally run into a problem with pushes, but that's unrelated to the plugin. It's some compatibility problem with thin packs between the old cgit version our Jenkins uses (1.9.4) and the JGit in Gerrit; when it happens once in a while, we commit manually using git push --no-thin.

@rodrigc
Copy link
Contributor

rodrigc commented Nov 10, 2015

I sent a request to add myself as a plugin maintainer so that I can help get your fixes in.
https://groups.google.com/forum/#!topic/jenkinsci-dev/jSxy6Llr2is

@jessesanford
Copy link

@rodrigc thanks for taking the lead on this!

@sethherr
Copy link

Bumping because I'd love to hear when this is merged, 👍

@doronshai
Copy link

+1

@trygveaa
Copy link

trygveaa commented Dec 9, 2015

@rodrigc: I see that you were accepted as a maintainer. Do you have any updates regarding this PR?

@rodrigc rodrigc merged commit 046227a into jenkinsci:master Dec 11, 2015
@kyounger
Copy link

Christmas does come early.

rodrigc pushed a commit that referenced this pull request Dec 12, 2015
No real code changes.

Try to get a consistent indentation on all files touched in PR #31.
Looks like these files used 4 spaces for indentation originally, but
my Eclipse was set to use tabs.

Fixed by IDE settings to use spaces instead of tabs and reformatted all
Java files I touched. This also added some missing @OverRide
annotations, and made some single-line if-statements use blocks -- I
*never* use the short form.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.