Add VersionedManifests VersionedManifests extends VersionedMetaData so that manifests in a git commit can be retrieved and saved. org.eclipse.jgit.junit is added at a version that is supported in Gerrit 2.9.1 so that a mock git repo/commit can be created for tests. Change-Id: I15cc5bea49e32aa99169dfdffab663b9f21c52be
diff --git a/pom.xml b/pom.xml index 60fc0dc..26de210 100644 --- a/pom.xml +++ b/pom.xml
@@ -141,6 +141,13 @@ </dependency> <dependency> + <groupId>org.eclipse.jgit</groupId> + <artifactId>org.eclipse.jgit.junit</artifactId> + <version>3.7.1.201504261725-r</version> + <scope>test</scope> + </dependency> + + <dependency> <groupId>org.jvnet.jaxb2_commons</groupId> <artifactId>jaxb2-basics-runtime</artifactId> <version>${jaxb2-basics-runtime.version}</version>
diff --git a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifests.java b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifests.java new file mode 100644 index 0000000..5cf3466 --- /dev/null +++ b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifests.java
@@ -0,0 +1,235 @@ +// Copyright (C) 2015 Advanced Micro Devices, Inc. All rights reserved. +// +// 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.amd.gerrit.plugins.manifestsubscription; + +import com.amd.gerrit.plugins.manifestsubscription.manifest.Manifest; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Maps; +import com.google.common.collect.Table; +import com.google.gerrit.reviewdb.client.Project; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.VersionedMetaData; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathSuffixFilter; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class VersionedManifests extends VersionedMetaData implements ManifestProvider { + private String refName; + private Unmarshaller manifestUnmarshaller; + private Marshaller manifestMarshaller; + private Map<String, Manifest> manifests; + + public void setManifests(Map<String, Manifest> manifests) { + this.manifests = manifests; + } + + public Map<String, Manifest> getManifests() { + return Collections.unmodifiableMap(manifests); + } + + private VersionedManifests() throws JAXBException { + JAXBContext jaxbctx = JAXBContext.newInstance(Manifest.class); + this.manifestUnmarshaller = jaxbctx.createUnmarshaller(); + this.manifestMarshaller = jaxbctx.createMarshaller(); + this.manifestMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + } + + public VersionedManifests(String refName) + throws JAXBException { + this(); + this.refName = refName; + + } + + public VersionedManifests(String refName, + Map<String, Manifest> manifests) throws JAXBException { + this(refName); + this.manifests = manifests; + + } + + @Override + protected String getRefName() { + return refName; + } + + @Override + protected void onLoad() throws IOException, ConfigInvalidException { + manifests = Maps.newHashMap(); + + String path; + Manifest manifest; + + RevWalk rw = new RevWalk(reader); + RevCommit r = rw.parseCommit(getRevision()); + TreeWalk treewalk = new TreeWalk(reader); + treewalk.addTree(r.getTree()); + treewalk.setRecursive(false); + treewalk.setFilter(PathSuffixFilter.create(".xml")); + while (treewalk.next()) { + if (treewalk.isSubtree()) { + treewalk.enterSubtree(); + } else { + path = treewalk.getPathString(); + try { + //TODO: Should this be done more lazily? + //TODO: difficult to do when reader is not available outside of onLoad? + ByteArrayInputStream input = new ByteArrayInputStream(readFile(path)); + manifest = (Manifest) manifestUnmarshaller.unmarshal(input); + manifests.put(path, manifest); + } catch (JAXBException e) { + e.printStackTrace(); + } + } + } + + treewalk.release(); + + //TODO load changed manifest +// DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE); + } + + @Override + protected boolean onSave(CommitBuilder commit) throws IOException { + StringBuilder commitMsg = new StringBuilder(); + commitMsg.append("Updated manifest\n\n"); + + String path; + Manifest manifest; + for (Map.Entry<String, Manifest> entry : manifests.entrySet()) { + path = entry.getKey(); + manifest = entry.getValue(); + + try { + saveManifest(path, manifest); + } catch (JAXBException e) { + throw new IOException(e); + } + } + + if (commit.getMessage() == null || "".equals(commit.getMessage())) { + commit.setMessage(commitMsg.toString()); + } + + return true; + } + + @Override + public Manifest readManifest(String path) throws ManifestReadException { + if (manifests.containsKey(path)) { + return manifests.get(path); + } + + throw new ManifestReadException(path); + } + + /** + * Must be called inside onSave + * + * @param path + * @param manifest + * @throws JAXBException + * @throws IOException + */ + private void saveManifest(String path, Manifest manifest) + throws JAXBException, IOException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + manifestMarshaller.marshal(manifest, output); + saveFile(path, output.toByteArray()); + } + + /** + * Pass in a {@link com.google.common.collect.Table} if you want to reuse + * the lookup cache + * + * @param gitRepoManager + * @param manifest + * @param lookup + */ + static void affixManifest(GitRepositoryManager gitRepoManager, + Manifest manifest, Table<String, String, String> lookup) { + if (lookup == null) { + // project, branch, hash + lookup = HashBasedTable.create(); + } + + String defaultRef = null; + + if (manifest.getDefault() != null) { + defaultRef = manifest.getDefault().getRevision(); + } + + affixManifest(gitRepoManager, manifest.getProject(), defaultRef, lookup); + } + + private static void affixManifest(GitRepositoryManager gitRepoManager, + List<com.amd.gerrit.plugins.manifestsubscription.manifest.Project> projects, + String defaultRef, + Table<String, String, String> lookup) { + + String ref; + String hash; + String projectName; + Project.NameKey p; + for (com.amd.gerrit.plugins.manifestsubscription.manifest.Project project : projects) { + projectName = project.getName(); + ref = project.getRevision(); + + ref = (ref == null) ? defaultRef : ref; + + if (ref != null) { + hash = lookup.get(projectName, ref); + + if (hash == null) { + p = new Project.NameKey(projectName); + try { + Repository db = gitRepoManager.openRepository(p); + + hash = db.resolve(ref).getName(); + db.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (hash != null) { + lookup.put(projectName, ref, hash); + project.setRevision(hash); + project.setUpstream(ref); + } + } + + if (project.getProject().size() > 0) { + affixManifest(gitRepoManager, project.getProject(), defaultRef, lookup); + } + } + } + +}
diff --git a/src/test/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifestsTest.java b/src/test/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifestsTest.java new file mode 100644 index 0000000..dff6e8d --- /dev/null +++ b/src/test/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifestsTest.java
@@ -0,0 +1,112 @@ +// Copyright (C) 2015 Advanced Micro Devices, Inc. All rights reserved. +// +// 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.amd.gerrit.plugins.manifestsubscription; + +import com.amd.gerrit.plugins.manifestsubscription.manifest.Manifest; +import com.amd.gerrit.plugins.manifestsubscription.manifest.ManifestTest; +import org.apache.commons.compress.utils.IOUtils; +import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.junit.*; +import org.junit.rules.ExpectedException; + +import static com.google.common.truth.Truth.assertThat; + +public class VersionedManifestsTest extends LocalDiskRepositoryTestCase { + + private Repository db; + private TestRepository<Repository> util; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @BeforeClass + public static void setUpBeforeClass() { + + } + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + db = createBareRepository(); + util = new TestRepository<>(db); + } + + @Test + public void testVersionedManifestsReadFromGit() throws Exception { + RevCommit rev = util.commit(util.tree( + util.file("aosp.xml", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/aosp.xml")))), + util.file("aospinclude.xml", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/aospinclude.xml")))), + util.file("aospincludereplace.xml", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/aospincludereplace.xml")))), + util.file("multipleincludes.xml", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/multipleincludes.xml")))), + util.file("subdir/aospincludereplace.xml", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/subdir/aospincludereplace.xml")))), + util.file("subdir/testonly1.xml", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/subdir/testonly1.xml")))), + util.file("nonxml.txt", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/nonxml.txt")))), + util.file("testonly.xml", + util.blob(IOUtils.toByteArray( + getClass().getResourceAsStream("/testonly.xml")))) + )); + + VersionedManifests versionedManifests = + new VersionedManifests("master"); + + versionedManifests.load(db, rev); + + Manifest manifest; + + manifest = versionedManifests.readManifest("aosp.xml"); + ManifestTest.checkAOSPcontent(manifest); + + manifest = versionedManifests.readManifest("aospinclude.xml"); + assertThat(manifest.getInclude()).isNotEmpty(); + manifest = versionedManifests.readManifest("aospincludereplace.xml"); + assertThat(manifest.getInclude()).isNotEmpty(); + manifest = versionedManifests.readManifest("multipleincludes.xml"); + assertThat(manifest.getInclude()).isNotEmpty(); + + manifest = versionedManifests.readManifest("testonly.xml"); + ManifestTest.checkTestOnlyContent(manifest); + + manifest = versionedManifests.readManifest("subdir/aospincludereplace.xml"); + assertThat(manifest.getInclude()).isNotEmpty(); + assertThat(manifest.getInclude().get(0).getName()).isEqualTo("../aospinclude.xml"); + assertThat(manifest.getInclude().get(1).getName()).isEqualTo("testonly1.xml"); + + thrown.expect(ManifestReadException.class); + manifest = versionedManifests.readManifest("nonxml.txt"); + } + + @After + public void tearDown() throws Exception { + + } +} \ No newline at end of file