Make hooks/commit-msg available over HTTP
We have to move the old scproot assets into gerrit-server so they are commonly available to both the SSH and HTTP daemon packages. Bug: issue 392 Change-Id: Ie0dc95529f26b14535c2e1041863a441333516b3 Signed-off-by: Shawn O. Pearce <sop@google.com> Reviewed-by: Nico Sallembien <nsallembien@google.com>
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.tools.hooks;
|
||||
|
||||
import com.google.gerrit.server.util.HostPlatform;
|
||||
|
||||
import org.eclipse.jgit.dircache.DirCache;
|
||||
import org.eclipse.jgit.dircache.DirCacheBuilder;
|
||||
import org.eclipse.jgit.dircache.DirCacheEntry;
|
||||
import org.eclipse.jgit.lib.Commit;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.FileMode;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectWriter;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.RefUpdate;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class CommitMsgHookTest extends HookTestCase {
|
||||
private final String SOB1 = "Signed-off-by: J Author <ja@example.com>\n";
|
||||
private final String SOB2 = "Signed-off-by: J Committer <jc@example.com>\n";
|
||||
|
||||
@Override
|
||||
public void runBare() throws Throwable {
|
||||
try {
|
||||
super.runBare();
|
||||
} catch (SkipTestOnThisPlatform e) {
|
||||
System.err.println(" - Skipping " + getName() + " on this system");
|
||||
}
|
||||
}
|
||||
|
||||
private static class SkipTestOnThisPlatform extends RuntimeException {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
if (HostPlatform.isWin32()) {
|
||||
throw new SkipTestOnThisPlatform();
|
||||
}
|
||||
|
||||
super.setUp();
|
||||
final Date when = author.getWhen();
|
||||
final TimeZone tz = author.getTimeZone();
|
||||
|
||||
author = new PersonIdent("J. Author", "ja@example.com");
|
||||
author = new PersonIdent(author, when, tz);
|
||||
|
||||
committer = new PersonIdent("J. Committer", "jc@example.com");
|
||||
committer = new PersonIdent(committer, when, tz);
|
||||
}
|
||||
|
||||
public void testEmptyMessages() throws Exception {
|
||||
// Empty input must yield empty output so commit will abort.
|
||||
// Note we must consider different commit templates formats.
|
||||
//
|
||||
hookDoesNotModify("");
|
||||
hookDoesNotModify(" ");
|
||||
hookDoesNotModify("\n");
|
||||
hookDoesNotModify("\n\n");
|
||||
hookDoesNotModify(" \n ");
|
||||
|
||||
hookDoesNotModify("#");
|
||||
hookDoesNotModify("#\n");
|
||||
hookDoesNotModify("# on branch master\n# Untracked files:\n");
|
||||
hookDoesNotModify("\n# on branch master\n# Untracked files:\n");
|
||||
hookDoesNotModify("\n\n# on branch master\n# Untracked files:\n");
|
||||
|
||||
hookDoesNotModify("\n# on branch master\ndiff --git a/src b/src\n"
|
||||
+ "new file mode 100644\nindex 0000000..c78b7f0\n");
|
||||
}
|
||||
|
||||
public void testChangeIdAlreadySet() throws Exception {
|
||||
// If a Change-Id is already present in the footer, the hook must
|
||||
// not modify the message but instead must leave the identity alone.
|
||||
//
|
||||
hookDoesNotModify("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: Iaeac9b4149291060228ef0154db2985a31111335\n");
|
||||
hookDoesNotModify("fix: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I388bdaf52ed05b55e62a22d0a20d2c1ae0d33e7e\n");
|
||||
hookDoesNotModify("fix-a-widget: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: Id3bc5359d768a6400450283e12bdfb6cd135ea4b\n");
|
||||
hookDoesNotModify("FIX: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I1b55098b5a2cce0b3f3da783dda50d5f79f873fa\n");
|
||||
hookDoesNotModify("Fix-A-Widget: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I4f4e2e1e8568ddc1509baecb8c1270a1fb4b6da7\n");
|
||||
}
|
||||
|
||||
public void testTimeAltersId() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
|
||||
call("a\n"));
|
||||
|
||||
tick();
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I3251906b99dda598a58a6346d8126237ee1ea800\n",//
|
||||
call("a\n"));
|
||||
|
||||
tick();
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I69adf9208d828f41a3d7e41afbca63aff37c0c5c\n",//
|
||||
call("a\n"));
|
||||
}
|
||||
|
||||
public void testFirstParentAltersId() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
|
||||
call("a\n"));
|
||||
|
||||
setHEAD();
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I51e86482bde7f92028541aaf724d3a3f996e7ea2\n",//
|
||||
call("a\n"));
|
||||
}
|
||||
|
||||
public void testDirCacheAltersId() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
|
||||
call("a\n"));
|
||||
|
||||
final DirCacheBuilder builder = DirCache.lock(repository).builder();
|
||||
builder.add(file("A"));
|
||||
assertTrue(builder.commit());
|
||||
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: If56597ea9759f23b070677ea6f064c60c38da631\n",//
|
||||
call("a\n"));
|
||||
}
|
||||
|
||||
public void testSingleLineMessages() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n",//
|
||||
call("a\n"));
|
||||
|
||||
assertEquals("fix: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I0f13d0e6c739ca3ae399a05a93792e80feb97f37\n",//
|
||||
call("fix: this thing\n"));
|
||||
assertEquals("fix-a-widget: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I1a1a0c751e4273d532e4046a501a612b9b8a775e\n",//
|
||||
call("fix-a-widget: this thing\n"));
|
||||
|
||||
assertEquals("FIX: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: If816d944c57d3893b60cf10c65931fead1290d97\n",//
|
||||
call("FIX: this thing\n"));
|
||||
assertEquals("Fix-A-Widget: this thing\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I3e18d00cbda2ba1f73aeb63ed8c7d57d7fd16c76\n",//
|
||||
call("Fix-A-Widget: this thing\n"));
|
||||
}
|
||||
|
||||
public void testMultiLineMessagesWithoutFooter() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"b\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: Id0b4f42d3d6fc1569595c9b97cb665e738486f5d\n",//
|
||||
call("a\n" + "\n" + "b\n"));
|
||||
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"b\nc\nd\ne\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7d237b20058a0f46cc3f5fabc4a0476877289d75\n",//
|
||||
call("a\n" + "\n" + "b\nc\nd\ne\n"));
|
||||
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"b\nc\nd\ne\n" + //
|
||||
"\n" + //
|
||||
"f\ng\nh\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n",//
|
||||
call("a\n" + "\n" + "b\nc\nd\ne\n" + "\n" + "f\ng\nh\n"));
|
||||
}
|
||||
|
||||
public void testSingleLineMessagesWithSignedOffBy() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
|
||||
SOB1,//
|
||||
call("a\n" + "\n" + SOB1));
|
||||
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
|
||||
SOB1 + //
|
||||
SOB2,//
|
||||
call("a\n" + "\n" + SOB1 + SOB2));
|
||||
}
|
||||
|
||||
public void testMultiLineMessagesWithSignedOffBy() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"b\nc\nd\ne\n" + //
|
||||
"\n" + //
|
||||
"f\ng\nh\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n" + //
|
||||
SOB1,//
|
||||
call("a\n" + "\n" + "b\nc\nd\ne\n" + "\n" + "f\ng\nh\n" + "\n" + SOB1));
|
||||
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"b\nc\nd\ne\n" + //
|
||||
"\n" + //
|
||||
"f\ng\nh\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I382e662f47bf164d6878b7fe61637873ab7fa4e8\n" + //
|
||||
SOB1 + //
|
||||
SOB2,//
|
||||
call("a\n" + //
|
||||
"\n" + //
|
||||
"b\nc\nd\ne\n" + //
|
||||
"\n" + //
|
||||
"f\ng\nh\n" + //
|
||||
"\n" + //
|
||||
SOB1 + //
|
||||
SOB2));
|
||||
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"b: not a footer\nc\nd\ne\n" + //
|
||||
"\n" + //
|
||||
"f\ng\nh\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I8869aabd44b3017cd55d2d7e0d546a03e3931ee2\n" + //
|
||||
SOB1 + //
|
||||
SOB2,//
|
||||
call("a\n" + //
|
||||
"\n" + //
|
||||
"b: not a footer\nc\nd\ne\n" + //
|
||||
"\n" + //
|
||||
"f\ng\nh\n" + //
|
||||
"\n" + //
|
||||
SOB1 + //
|
||||
SOB2));
|
||||
}
|
||||
|
||||
public void testNoteInMiddle() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"NOTE: This\n" + //
|
||||
"does not fix it.\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I988a127969a6ee5e58db546aab74fc46e66847f8\n", //
|
||||
call("a\n" + //
|
||||
"\n" + //
|
||||
"NOTE: This\n" + //
|
||||
"does not fix it.\n"));
|
||||
}
|
||||
|
||||
public void testKernelStyleFooter() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I1bd787f9e7590a2ac82b02c404c955ffb21877c4\n" + //
|
||||
SOB1 + //
|
||||
"[ja: Fixed\n" + //
|
||||
" the indentation]\n" + //
|
||||
SOB2, //
|
||||
call("a\n" + //
|
||||
"\n" + //
|
||||
SOB1 + //
|
||||
"[ja: Fixed\n" + //
|
||||
" the indentation]\n" + //
|
||||
SOB2));
|
||||
}
|
||||
|
||||
public void testChangeIdAfterBugOrIssue() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Bug: 42\n" + //
|
||||
"Change-Id: I8c0321227c4324e670b9ae8cf40eccc87af21b1b\n" + //
|
||||
SOB1,//
|
||||
call("a\n" + //
|
||||
"\n" + //
|
||||
"Bug: 42\n" + //
|
||||
SOB1));
|
||||
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Issue: 42\n" + //
|
||||
"Change-Id: Ie66e07d89ae5b114c0975b49cf326e90331dd822\n" + //
|
||||
SOB1,//
|
||||
call("a\n" + //
|
||||
"\n" + //
|
||||
"Issue: 42\n" + //
|
||||
SOB1));
|
||||
}
|
||||
|
||||
public void testCommitDashV() throws Exception {
|
||||
assertEquals("a\n" + //
|
||||
"\n" + //
|
||||
"Change-Id: I7fc3876fee63c766a2063df97fbe04a2dddd8d7c\n" + //
|
||||
SOB1 + //
|
||||
SOB2, //
|
||||
call("a\n" + //
|
||||
"\n" + //
|
||||
SOB1 + //
|
||||
SOB2 + //
|
||||
"\n" + //
|
||||
"# on branch master\n" + //
|
||||
"diff --git a/src b/src\n" + //
|
||||
"new file mode 100644\n" + //
|
||||
"index 0000000..c78b7f0\n"));
|
||||
}
|
||||
|
||||
private void hookDoesNotModify(final String in) throws Exception {
|
||||
assertEquals(in, call(in));
|
||||
}
|
||||
|
||||
private String call(final String body) throws Exception {
|
||||
final File tmp = write(body);
|
||||
try {
|
||||
final File hook = getHook("commit-msg");
|
||||
assertEquals(0, runHook(repository, hook, tmp.getAbsolutePath()));
|
||||
return read(tmp);
|
||||
} finally {
|
||||
tmp.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private DirCacheEntry file(final String name) throws IOException {
|
||||
final DirCacheEntry e = new DirCacheEntry(name);
|
||||
e.setFileMode(FileMode.REGULAR_FILE);
|
||||
e.setObjectId(writer().writeBlob(Constants.encode(name)));
|
||||
return e;
|
||||
}
|
||||
|
||||
private void setHEAD() throws Exception {
|
||||
final ObjectWriter ow = writer();
|
||||
final Commit commit = new Commit(repository);
|
||||
commit.setTreeId(DirCache.newInCore().writeTree(ow));
|
||||
commit.setAuthor(author);
|
||||
commit.setCommitter(committer);
|
||||
commit.setMessage("test\n");
|
||||
final ObjectId commitId = ow.writeCommit(commit);
|
||||
|
||||
final RefUpdate ref = repository.updateRef(Constants.HEAD);
|
||||
ref.setNewObjectId(commitId);
|
||||
switch (ref.forceUpdate()) {
|
||||
case NEW:
|
||||
case FAST_FORWARD:
|
||||
case FORCED:
|
||||
case NO_CHANGE:
|
||||
break;
|
||||
default:
|
||||
fail(Constants.HEAD + " did not change: " + ref.getResult());
|
||||
}
|
||||
}
|
||||
|
||||
private ObjectWriter writer() {
|
||||
return new ObjectWriter(repository);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright (C) 2009 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Portions related to finding the hook script to execute are:
|
||||
// Copyright (C) 2008, Imran M Yousuf <imyousuf@smartitengineering.com>
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or
|
||||
// without modification, are permitted provided that the following
|
||||
// conditions are met:
|
||||
//
|
||||
// - Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// - Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// - Neither the name of the Git Development Community nor the
|
||||
// names of its contributors may be used to endorse or promote
|
||||
// products derived from this software without specific prior
|
||||
// written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||
// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.gerrit.server.tools.hooks;
|
||||
|
||||
import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
public abstract class HookTestCase extends LocalDiskRepositoryTestCase {
|
||||
protected Repository repository;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
repository = createWorkRepository();
|
||||
}
|
||||
|
||||
protected File getHook(final String name) {
|
||||
final String scproot = "com/google/gerrit/server/tools/root";
|
||||
final String path = scproot + "/hooks/" + name;
|
||||
final URL url = cl().getResource(path);
|
||||
if (url == null) {
|
||||
fail("Cannot locate " + path + " in CLASSPATH");
|
||||
}
|
||||
|
||||
File hook;
|
||||
try {
|
||||
hook = new File(url.toURI());
|
||||
} catch (URISyntaxException e) {
|
||||
hook = new File(url.getPath());
|
||||
}
|
||||
if (!hook.isFile()) {
|
||||
fail("Cannot locate " + path + " in CLASSPATH");
|
||||
}
|
||||
|
||||
// The hook was copied out of our source control system into the
|
||||
// target area by Java tools. Its not executable in the source
|
||||
// are, nor did the copying Java program make it executable in the
|
||||
// destination area. So we must force it to be executable.
|
||||
//
|
||||
final long time = hook.lastModified();
|
||||
hook.setExecutable(true);
|
||||
hook.setLastModified(time);
|
||||
return hook;
|
||||
}
|
||||
|
||||
private ClassLoader cl() {
|
||||
return HookTestCase.class.getClassLoader();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user