Implement essential subscriptions functions
* Look for projects with monitor enabled on plugin load
* Keep track of manifest repos that has subscription enabled
* Keep track of projects defined in manifests being monitored
* Update when project.config is changed
* Update when manifest changes
* Update on project changes (for projects in manifests being monitored)
* Repo where generated manifests are stored will not be monitored
Change-Id: I63c749487f52c58d20ce8004bdb892ae4b78afd6
diff --git a/README.md b/README.md
new file mode 120000
index 0000000..ac8bf0b
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+src/main/resources/Documentation/about.md
\ No newline at end of file
diff --git a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/ManifestSubscription.java b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/ManifestSubscription.java
new file mode 100644
index 0000000..0376815
--- /dev/null
+++ b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/ManifestSubscription.java
@@ -0,0 +1,447 @@
+// 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.*;
+import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.ProjectConfig;
+import com.google.gerrit.server.project.ProjectCache;
+import com.google.inject.Inject;
+import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.xml.bind.JAXBException;
+import java.io.IOException;
+import java.util.*;
+
+public class ManifestSubscription implements
+ GitReferenceUpdatedListener, LifecycleListener {
+ private static final Logger log =
+ LoggerFactory.getLogger(ManifestSubscription.class);
+
+ private static final String KEY_BRANCH = "branch";
+ private static final String KEY_STORE = "store";
+
+ private static final String STORE_BRANCH_PREFIX = "refs/heads/m/";
+
+ private final String pluginName;
+
+ private final MetaDataUpdate.Server metaDataUpdateFactory;
+ private final GitRepositoryManager gitRepoManager;
+ private final ProjectCache projectCache;
+
+ /**
+ * source project lookup
+ * manifest source project name, plugin config
+ **/
+ private Map<String, PluginProjectConfig> enabledManifestSource = Maps.newHashMap();
+
+ /**
+ * manifest store lookup
+ * repo name, branch (branchPath), Manifest
+ */
+ private Table<String, String, Manifest> manifestStores = HashBasedTable.create();
+
+ /**
+ * lookup
+ * subscribed project name and branch, manifest dest store, <manifest dest branch Project>
+ **/
+ private Table<ProjectBranchKey, String, Map<String, Set<
+ com.amd.gerrit.plugins.manifestsubscription.manifest.Project>>> subscribedRepos = HashBasedTable.create();
+
+ public Set<String> getEnabledManifestSource() {
+ return ImmutableSet.copyOf(enabledManifestSource.keySet());
+ }
+
+ public Set<ProjectBranchKey> getSubscribedProjects() {
+ return ImmutableSet.copyOf(subscribedRepos.rowKeySet());
+ }
+
+ @Override
+ public void start() {
+ ProjectConfig config;
+ for (Project.NameKey p : projectCache.all()) {
+ //TODO parallelize parsing/load up?
+ try {
+ config = ProjectConfig.read(metaDataUpdateFactory.create(p));
+ loadStoreFromProjectConfig(p.toString(), config);
+
+ } catch (IOException | ConfigInvalidException | JAXBException e) {
+ log.error(e.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void stop() {
+
+ }
+
+ @Inject
+ ManifestSubscription(MetaDataUpdate.Server metaDataUpdateFactory,
+ GitRepositoryManager gitRepoManager,
+ @PluginName String pluginName,
+ ProjectCache projectCache) {
+ this.metaDataUpdateFactory = metaDataUpdateFactory;
+ this.gitRepoManager = gitRepoManager;
+ this.pluginName = pluginName;
+ this.projectCache = projectCache;
+ }
+
+ @Override
+ public void onGitReferenceUpdated(Event event) {
+ String projectName = event.getProjectName();
+ String refName = event.getRefName();
+ String branchName = refName.startsWith("refs/heads/") ?
+ refName.substring(11) : "";
+ ProjectBranchKey pbKey = new ProjectBranchKey(projectName, branchName);
+
+ if ("refs/meta/config".equals(refName)) {
+ // possible change in enabled repos
+ processProjectConfigChange(event);
+ } else if (enabledManifestSource.containsKey(projectName) &&
+ enabledManifestSource.get(projectName)
+ .getBranches().contains(branchName)) {
+ processManifestChange(event, projectName, branchName);
+
+ } else if (subscribedRepos.containsRow(pbKey)) {
+ // Manifest store and branch
+ Map<String, Map<String, Set<
+ com.amd.gerrit.plugins.manifestsubscription.manifest.Project>>>
+ destinations = subscribedRepos.row(pbKey);
+
+ for (String store : destinations.keySet()) {
+ for (String storeBranch : destinations.get(store).keySet()) {
+ Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project> ps
+ = destinations.get(store).get(storeBranch);
+
+ Manifest manifest = manifestStores.get(store, storeBranch);
+ // these are project from the above manifest previously
+ // cached in the lookup table
+ for (com.amd.gerrit.plugins.manifestsubscription.manifest.Project
+ updateProject : ps) {
+ updateProject.setRevision(event.getNewObjectId());
+ }
+
+ try {
+ updateManifest(store, STORE_BRANCH_PREFIX + storeBranch, manifest);
+ } catch (JAXBException | IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+ //updates in subscribed repos
+ }
+
+
+ }
+
+ private void updateProjectRev(String projectName, String branch, String rev,
+ List<com.amd.gerrit.plugins.
+ manifestsubscription.manifest.Project> projects) {
+ //TODO optimize to not have to iterate through manifest?
+ for (com.amd.gerrit.plugins.manifestsubscription.manifest.Project project : projects) {
+ if (Objects.equals(projectName, project.getName()) &&
+ Objects.equals(branch, project.getUpstream())) {
+ project.setRevision(rev);
+ }
+
+ if (project.getProject().size() > 0) {
+ updateProjectRev(projectName, branch, rev, project.getProject());
+ }
+ }
+
+ }
+
+ private void processManifestChange(Event event,
+ String projectName, String branchName) {
+ try {
+ VersionedManifests versionedManifests = parseManifests(event);
+ processManifestChange(versionedManifests, projectName, branchName);
+ } catch (JAXBException | IOException | ConfigInvalidException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void processManifestChange(VersionedManifests versionedManifests,
+ String projectName, String branchName) {
+ //possible manifest update in subscribing repos
+ //TODO Fix, right now update all manifest every time
+ //TODO even when only one of the manifest has changed
+
+ try {
+ if (versionedManifests != null) {
+ CanonicalManifest cManifest = new CanonicalManifest(versionedManifests);
+ Set<String> manifests = versionedManifests.getManifestPaths();
+ Manifest manifest;
+ String store = enabledManifestSource.get(projectName).getStore();
+ Table<String, String, String> lookup = HashBasedTable.create();
+
+ // Remove old manifest from subscription if destination store and branch
+ // matches manifest source being updated
+ // TODO again, this assume 1-1 map between store and manifest store
+ Map<String,
+ Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>> branchPaths;
+ for (Table.Cell<ProjectBranchKey, String,
+ Map<String,
+ Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>>> cell :
+ subscribedRepos.cellSet()) {
+ if (store.equals(cell.getColumnKey())) {
+ branchPaths = cell.getValue();
+
+ Iterator<String> iter = branchPaths.keySet().iterator();
+ String branchPath;
+ while (iter.hasNext()) {
+ branchPath = iter.next();
+ if (branchPath.startsWith(branchName)) {
+ iter.remove();
+ manifestStores.remove(store, branchPath);
+ }
+ }
+ }
+ }
+
+ //TODO need to make sure remote is pointing to this server?
+ //TODO this may be impossible
+ //TODO only monitor projects without 'remote' attribute / only using default?
+
+ for (String path : manifests) {
+ String bp = branchName + "/" + path;
+ try {
+ manifest = cManifest.getCanonicalManifest(path);
+
+ VersionedManifests.affixManifest(gitRepoManager, manifest, lookup);
+
+ watchCanonicalManifest(manifest, store, bp);
+ //save manifest
+ //TODO added the m/ to the ref to to work around LOCK_FAILURE error of creating master/bla/bla
+ //TODO (because default master ref already exists) better solution?
+ updateManifest(store, STORE_BRANCH_PREFIX + bp, manifest);
+
+ } catch (ManifestReadException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+ } catch (JAXBException | IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private void watchCanonicalManifest(Manifest manifest, String store,
+ String branchPath) {
+ String defaultBranch;
+ if (manifest.getDefault() != null &&
+ manifest.getDefault().getRevision() != null) {
+ defaultBranch = manifest.getDefault().getRevision();
+ } else {
+ defaultBranch = "";
+ }
+
+ manifestStores.put(store, branchPath, manifest);
+
+ List<com.amd.gerrit.plugins.manifestsubscription.manifest.Project> projects
+ = manifest.getProject();
+ watchProjectsInCanonicalManifest(store, branchPath, defaultBranch, projects);
+ }
+
+ private void watchProjectsInCanonicalManifest(String store, String branchPath,
+ String defaultBranch,
+ List<com.amd.gerrit.plugins.manifestsubscription.manifest.Project> projects) {
+ ProjectBranchKey pbKey;
+ for (com.amd.gerrit.plugins.manifestsubscription.manifest.Project project : projects) {
+ if (manifestStores.containsRow(project.getName()) &&
+ !manifestStores.row(project.getName()).isEmpty()) {
+ // Skip if it's one of the repo for storing
+ // manifest to avoid infinite loop
+ // This is a bit too general, but it's done to avoid the complexity
+ // of actually tracing out the loop
+ // i.e. manifest1->store2 --> manifest2->store1
+ continue;
+ }
+
+ // Make sure revision is branch ref w/o refs/heads
+ String branch = project.getRevision() == null ?
+ defaultBranch : (project.getUpstream() != null ?
+ project.getUpstream() : project.getRevision());
+ pbKey = new ProjectBranchKey(project.getName(),
+ Repository.shortenRefName(branch));
+
+
+ //TODO only update manifests that changed
+ if (!subscribedRepos.contains(pbKey, store)) {
+ subscribedRepos.put(pbKey, store,
+ Maps.<String, Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>>newHashMap());
+ }
+
+
+ Map<String,
+ Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>> ps;
+ ps = subscribedRepos.get(pbKey, store);
+ if (!ps.containsKey(branchPath)) {
+ ps.put(branchPath,
+ Sets.<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>newHashSet());
+ }
+ ps.get(branchPath).add(project);
+
+ if (project.getProject().size() > 0) {
+ watchProjectsInCanonicalManifest(store, branchPath, defaultBranch,
+ project.getProject());
+ }
+ }
+ }
+
+ private void processProjectConfigChange(Event event) {
+ Project.NameKey p = new Project.NameKey(event.getProjectName());
+
+ //TODO test two separate project configured to the same store
+ try {
+ ProjectConfig oldCfg = parseConfig(p, event.getOldObjectId());
+ ProjectConfig newCfg = parseConfig(p, event.getNewObjectId());
+
+ //TODO selectively update changes instead of complete reset
+ if (oldCfg != null) {
+ String oldStore =
+ oldCfg.getPluginConfig(pluginName).getString(KEY_STORE);
+
+ if (oldStore != null && !oldStore.isEmpty()) {
+ //TODO FIX assume unique store for each manifest source (1-1 map)
+ manifestStores.row(oldStore).clear();
+ enabledManifestSource.remove(event.getProjectName());
+
+ Iterator<Table.Cell<ProjectBranchKey, String, Map<String,
+ Set<com.amd.gerrit.plugins.manifestsubscription.manifest.Project>>>> iter =
+ subscribedRepos.cellSet().iterator();
+ while (iter.hasNext()) {
+ if (oldStore.equals(iter.next().getColumnKey())) {
+ iter.remove();
+ }
+ }
+ }
+ }
+
+ if (newCfg != null) {
+ loadStoreFromProjectConfig(event.getProjectName(), newCfg);
+
+ }
+ } catch (IOException | ConfigInvalidException | JAXBException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void loadStoreFromProjectConfig(String projectName,
+ ProjectConfig config)
+ throws JAXBException, IOException, ConfigInvalidException {
+ String newStore =
+ config.getPluginConfig(pluginName).getString(KEY_STORE);
+
+ if (newStore != null) {
+ newStore = newStore.trim();
+ if (!newStore.isEmpty()) {
+ Set<String> branches = Sets.newHashSet(
+ config.getPluginConfig(pluginName)
+ .getStringList(KEY_BRANCH));
+
+ if (branches.size() > 0) {
+ PluginProjectConfig ppc = new PluginProjectConfig(newStore, branches);
+
+ enabledManifestSource.put(projectName, ppc);
+ Project.NameKey nameKey = new Project.NameKey(projectName);
+ VersionedManifests versionedManifests;
+ for (String branch : branches) {
+ versionedManifests = parseManifests(nameKey, branch);
+ processManifestChange(versionedManifests, projectName, branch);
+ }
+ }
+ }
+ }
+ }
+
+ private ProjectConfig parseConfig(Project.NameKey p, String idStr)
+ throws IOException, ConfigInvalidException {
+ ObjectId id = ObjectId.fromString(idStr);
+ if (ObjectId.zeroId().equals(id)) {
+ return null;
+ }
+ return ProjectConfig.read(metaDataUpdateFactory.create(p), id);
+ }
+
+ private VersionedManifests parseManifests(Event event)
+ throws JAXBException, IOException, ConfigInvalidException {
+ Project.NameKey p = new Project.NameKey(event.getProjectName());
+ return parseManifests(p, event.getRefName());
+ }
+
+ private VersionedManifests parseManifests(Project.NameKey p, String refName)
+ throws IOException, JAXBException, ConfigInvalidException {
+
+ MetaDataUpdate update = metaDataUpdateFactory.create(p);
+ VersionedManifests vManifests = new VersionedManifests(refName);
+ vManifests.load(update);
+
+ return vManifests;
+ }
+
+ private void updateManifest(String projectName, String refName,
+ Manifest manifest)
+ throws JAXBException, IOException {
+ Project.NameKey p = new Project.NameKey(projectName);
+ MetaDataUpdate update = metaDataUpdateFactory.create(p);
+ VersionedManifests vManifests = new VersionedManifests(refName);
+
+ //TODO find a better way to detect no branch
+ boolean refExists = true;
+ try {
+ vManifests.load(update);
+ } catch (Exception e) {
+ refExists = false;
+ }
+
+ if (refExists) {
+ Map<String, Manifest> entry = Maps.newHashMapWithExpectedSize(1);
+ entry.put("default.xml", manifest);
+ vManifests.setManifests(entry);
+ vManifests.commit(update);
+ } else {
+ vManifests = new VersionedManifests("master");
+ try {
+ vManifests.load(update);
+ } catch (ConfigInvalidException e) {
+ e.printStackTrace();
+ }
+ Map<String, Manifest> entry = Maps.newHashMapWithExpectedSize(1);
+ entry.put("default.xml", manifest);
+ vManifests.setManifests(entry);
+ vManifests.commitToNewRef(update, refName);
+ }
+ }
+
+}
diff --git a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/Module.java b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/Module.java
index e73bdf2..ff8c3b0 100644
--- a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/Module.java
+++ b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/Module.java
@@ -14,11 +14,22 @@
package com.amd.gerrit.plugins.manifestsubscription;
+import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.inject.AbstractModule;
+import com.google.inject.Scopes;
class Module extends AbstractModule {
+
@Override
protected void configure() {
- // TODO
+
+ bind(ManifestSubscription.class).in(Scopes.SINGLETON);
+
+ DynamicSet.bind(binder(), LifecycleListener.class)
+ .to(ManifestSubscription.class);
+ DynamicSet.bind(binder(), GitReferenceUpdatedListener.class)
+ .to(ManifestSubscription.class);
}
}
diff --git a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/PluginProjectConfig.java b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/PluginProjectConfig.java
new file mode 100644
index 0000000..bad5797
--- /dev/null
+++ b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/PluginProjectConfig.java
@@ -0,0 +1,43 @@
+// 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 java.util.Set;
+
+public class PluginProjectConfig {
+ private String store;
+ private Set<String> branches;
+
+ public PluginProjectConfig(String store, Set<String> branches) {
+ this.store = store;
+ this.branches = branches;
+ }
+
+ public String getStore() {
+ return store;
+ }
+
+ public void setStore(String store) {
+ this.store = store;
+ }
+
+ public Set<String> getBranches() {
+ return branches;
+ }
+
+ public void setBranches(Set<String> branches) {
+ this.branches = branches;
+ }
+}
diff --git a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/ProjectBranchKey.java b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/ProjectBranchKey.java
new file mode 100644
index 0000000..13734c6
--- /dev/null
+++ b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/ProjectBranchKey.java
@@ -0,0 +1,49 @@
+// 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 java.util.Objects;
+
+public class ProjectBranchKey {
+ private String project;
+ private String branch;
+
+ public ProjectBranchKey(String project, String branch) {
+ this.project = project;
+ this.branch = branch;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ProjectBranchKey that = (ProjectBranchKey) o;
+ return Objects.equals(project, that.project) &&
+ Objects.equals(branch, that.branch);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(project, branch);
+ }
+
+ @Override
+ public String toString() {
+ return com.google.common.base.Objects.toStringHelper(this)
+ .add("project", project)
+ .add("branch", branch)
+ .toString();
+ }
+}
diff --git a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifests.java b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifests.java
index 5cf3466..b7ea26e 100644
--- a/src/main/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifests.java
+++ b/src/main/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifests.java
@@ -39,6 +39,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
public class VersionedManifests extends VersionedMetaData implements ManifestProvider {
private String refName;
@@ -50,6 +51,10 @@
this.manifests = manifests;
}
+ public Set<String> getManifestPaths() {
+ return manifests.keySet();
+ }
+
public Map<String, Manifest> getManifests() {
return Collections.unmodifiableMap(manifests);
}
@@ -88,6 +93,11 @@
Manifest manifest;
RevWalk rw = new RevWalk(reader);
+
+ // This happens when someone configured a invalid branch name
+ if (getRevision() == null) {
+ throw new ConfigInvalidException(refName);
+ }
RevCommit r = rw.parseCommit(getRevision());
TreeWalk treewalk = new TreeWalk(reader);
treewalk.addTree(r.getTree());
diff --git a/src/main/resources/Documentation/about.md b/src/main/resources/Documentation/about.md
new file mode 100644
index 0000000..ac28edd
--- /dev/null
+++ b/src/main/resources/Documentation/about.md
@@ -0,0 +1,65 @@
+This plugin allows users to monitor git-repo manifests in manifest repositories
+and generate rev-specific manifests (similar to "repo manifest -o") and store
+them to a separate git repository (configurable.)
+
+> **Warning: this plugin is currently under development and is targeting 2.9.1**
+
+The rev-specific manifest of each source manifest is stored in its own branch
+with the following naming convention:
+m/\<source manifest branch\>/\<source manifest path\>
+
+The name of the rev-specific manifest is default.xml
+
+For example, if the source manifests are
+branch: master
+file: default.xml
+file: dev.xml
+file: upstream-mirror.xml
+
+There will be three *branches* in the destination repository with these names:
+m/master/default.xml
+m/master/dev.xml
+m/master/upstream-mirror.xml
+
+If a tag is placed at the start of one of these branches, "git describe" can be
+used to provide system-level version metadata. One can also use "git bisect" on
+the branches to identify system-level regression.
+
+* Enabled by project.config in the manifest project to be monitored
+* Manifest class is generated from 'repo help manifest' DTD using JAXB
+* \<include\>s are expanded similar to "repo manifest -o" (only works for
+manifests in the same source manifest repository reference by relative path)
+* Supports \<remove-project\>
+* Circular update loop is prevented by not monitoring any of the rev-specific
+manifest repositories
+* Projects in the manifests are assumed to be on the server. (There is no check
+currently so it's a potential source of error.)
+
+
+```
+xjc -dtd -d gen -p com.amd.gerrit.plugins.manifestsubscription.manifest manifest.dtd
+```
+
+Manifest represent raw XML
+CanonicalManifest resolve <include> and <remove-project>
+
+* TODO: keep parsed manifest in memory for quick lookup
+* TODO: monitor all manifest branch if no branch is specified
+* TODO: ssh command to check what is being monitored
+* TODO: stright mode, only monitor projects using default (no remote)
+* TODO: split mode (store all manifests in same branch strcuture as source instead of flattening the underlying file strcuture)
+* TODO: not monitor non-local projects
+
+* TODO: make sure no circular dependencies (project with manifest subscription is not in the
+manifest being monitored.)
+* TODO: support changes in manifest
+
+* resolve relative path in include
+* supports include tag in manifest
+
+* TODO: check project.config on newkly created project
+
+* TODO: include an external manifest DTD or XML schema/XSD
+* generates/xjc classes from DTD/XSD at build time
+* TODO add test verify include manifest have original project
+* TODO sub dir include manifest have same level include working
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index bde3084..78c3ce4 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -1 +1,11 @@
-TODO: config documentation
+In the manifest project's project.config in refs/meta/config, set the following:
+
+```
+[plugin "manifest-subscription"]
+ store = "repo/name/on/the/server"
+ branch = "branch-being-monitored-in-this-repo"
+ branch = "another-branch-being-monitored-in-this-repo"
+ branch = "master"
+```
+
+There should be only one value for store. More than one value for branch.
\ No newline at end of file
diff --git a/src/test/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifestsTest.java b/src/test/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifestsTest.java
index dff6e8d..2799435 100644
--- a/src/test/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifestsTest.java
+++ b/src/test/java/com/amd/gerrit/plugins/manifestsubscription/VersionedManifestsTest.java
@@ -46,6 +46,19 @@
db = createBareRepository();
util = new TestRepository<>(db);
}
+ @Test
+ public void testVersionedManifestsReadFromNonManifestGit() throws Exception {
+ RevCommit rev = util.commit(util.tree(
+ util.file("nonxml.txt",
+ util.blob(IOUtils.toByteArray(
+ getClass().getResourceAsStream("/nonxml.txt"))))
+ ));
+
+ VersionedManifests versionedManifests =
+ new VersionedManifests("master");
+
+ versionedManifests.load(db, rev);
+ }
@Test
public void testVersionedManifestsReadFromGit() throws Exception {