Catalogs: allow migration to alternate IDs
This commit is contained in:
@@ -81,17 +81,27 @@ bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SGPropertyNode_ptr alternateForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
|
||||||
|
{
|
||||||
|
for (auto v : props->getChildren("alternate-version")) {
|
||||||
|
for (auto versionSpec : v->getChildren("version")) {
|
||||||
|
if (checkVersionString(aVersion, versionSpec->getStringValue())) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
|
std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props)
|
||||||
{
|
{
|
||||||
for (SGPropertyNode* v : props->getChildren("alternate-version")) {
|
auto node = alternateForVersion(aVersion, props);
|
||||||
std::string s(v->getStringValue("version"));
|
if (node)
|
||||||
if (checkVersionString(aVersion, s)) {
|
return node->getStringValue("url");;
|
||||||
return v->getStringValue("url");;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -139,19 +149,15 @@ protected:
|
|||||||
|
|
||||||
std::string ver(m_owner->root()->applicationVersion());
|
std::string ver(m_owner->root()->applicationVersion());
|
||||||
if (!checkVersion(ver, props)) {
|
if (!checkVersion(ver, props)) {
|
||||||
SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", but version required " << ver);
|
|
||||||
|
|
||||||
// check for a version redirect entry
|
// check for a version redirect entry
|
||||||
std::string url = redirectUrlForVersion(ver, props);
|
auto alt = alternateForVersion(ver, props);
|
||||||
if (!url.empty()) {
|
if (alt) {
|
||||||
SG_LOG(SG_GENERAL, SG_WARN, "redirecting from " << m_owner->url() <<
|
SG_LOG(SG_GENERAL, SG_WARN, "have alternate version of package at:"
|
||||||
" to \n\t" << url);
|
<< alt->getStringValue("url"));
|
||||||
|
m_owner->processAlternate(alt);
|
||||||
// update the URL and kick off a new request
|
|
||||||
m_owner->setUrl(url);
|
|
||||||
Downloader* dl = new Downloader(m_owner, url);
|
|
||||||
m_owner->root()->makeHTTPRequest(dl);
|
|
||||||
} else {
|
} else {
|
||||||
|
SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url()
|
||||||
|
<< ", but app version " << ver << " is not comaptible");
|
||||||
m_owner->refreshComplete(Delegate::FAIL_VERSION);
|
m_owner->refreshComplete(Delegate::FAIL_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +234,7 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath)
|
|||||||
|
|
||||||
bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props);
|
bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props);
|
||||||
if (!versionCheckOk) {
|
if (!versionCheckOk) {
|
||||||
SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need version: "
|
SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: app version: "
|
||||||
<< aRoot->applicationVersion());
|
<< aRoot->applicationVersion());
|
||||||
// keep the catalog but mark it as needing an update
|
// keep the catalog but mark it as needing an update
|
||||||
} else {
|
} else {
|
||||||
@@ -598,6 +604,50 @@ bool Catalog::isEnabled() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Catalog::processAlternate(SGPropertyNode_ptr alt)
|
||||||
|
{
|
||||||
|
std::string altId;
|
||||||
|
const auto idPtr = alt->getStringValue("id");
|
||||||
|
if (idPtr) {
|
||||||
|
altId = std::string(idPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string altUrl;
|
||||||
|
if (alt->getStringValue("url")) {
|
||||||
|
altUrl = std::string(alt->getStringValue("url"));
|
||||||
|
}
|
||||||
|
|
||||||
|
CatalogRef existing;
|
||||||
|
if (!altId.empty()) {
|
||||||
|
existing = root()->getCatalogById(altId);
|
||||||
|
} else {
|
||||||
|
existing = root()->getCatalogByUrl(altUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing && (existing != this)) {
|
||||||
|
// we already have the alternate, so just go quiet here
|
||||||
|
changeStatus(Delegate::FAIL_VERSION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have an alternate ID, and it's differnt from our ID, so let's
|
||||||
|
// define a new catalog
|
||||||
|
if (!altId.empty()) {
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO, "Adding new catalog:" << altId << " as version alternate for " << id());
|
||||||
|
// new catalog being added
|
||||||
|
createFromUrl(root(), altUrl);
|
||||||
|
|
||||||
|
// and we can go idle now
|
||||||
|
changeStatus(Delegate::FAIL_VERSION);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SG_LOG(SG_GENERAL, SG_INFO, "Migrating catalog " << id() << " to new URL:" << altUrl);
|
||||||
|
setUrl(altUrl);
|
||||||
|
Downloader* dl = new Downloader(this, altUrl);
|
||||||
|
root()->makeHTTPRequest(dl);
|
||||||
|
}
|
||||||
|
|
||||||
} // of namespace pkg
|
} // of namespace pkg
|
||||||
|
|
||||||
} // of namespace simgear
|
} // of namespace simgear
|
||||||
|
|||||||
@@ -179,6 +179,8 @@ private:
|
|||||||
|
|
||||||
void changeStatus(Delegate::StatusCode newStatus);
|
void changeStatus(Delegate::StatusCode newStatus);
|
||||||
|
|
||||||
|
void processAlternate(SGPropertyNode_ptr alt);
|
||||||
|
|
||||||
Root* m_root;
|
Root* m_root;
|
||||||
SGPropertyNode_ptr m_props;
|
SGPropertyNode_ptr m_props;
|
||||||
SGPath m_installRoot;
|
SGPath m_installRoot;
|
||||||
|
|||||||
@@ -741,6 +741,130 @@ void testVersionMigrate(HTTP::Client* cl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testVersionMigrateToId(HTTP::Client* cl)
|
||||||
|
{
|
||||||
|
global_catalogVersion = 2; // version which has migration info
|
||||||
|
SGPath rootPath(simgear::Dir::current().path());
|
||||||
|
rootPath.append("cat_migrate_version_id");
|
||||||
|
simgear::Dir pd(rootPath);
|
||||||
|
pd.removeChildren();
|
||||||
|
|
||||||
|
{
|
||||||
|
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
|
||||||
|
root->setHTTPClient(cl);
|
||||||
|
|
||||||
|
pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml");
|
||||||
|
waitForUpdateComplete(cl, root);
|
||||||
|
SG_VERIFY(c->isEnabled());
|
||||||
|
|
||||||
|
// install a package
|
||||||
|
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
|
||||||
|
SG_CHECK_EQUAL(p1->id(), "b737-NG");
|
||||||
|
pkg::InstallRef ins = p1->install();
|
||||||
|
SG_VERIFY(ins->isQueued());
|
||||||
|
waitForUpdateComplete(cl, root);
|
||||||
|
SG_VERIFY(p1->isInstalled());
|
||||||
|
}
|
||||||
|
|
||||||
|
// change version to an alternate one
|
||||||
|
{
|
||||||
|
pkg::RootRef root(new pkg::Root(rootPath, "7.5"));
|
||||||
|
root->setHTTPClient(cl);
|
||||||
|
|
||||||
|
// this should cause the alternate package to be loaded
|
||||||
|
root->refresh(true);
|
||||||
|
waitForUpdateComplete(cl, root);
|
||||||
|
|
||||||
|
pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1");
|
||||||
|
SG_VERIFY(!cat->isEnabled());
|
||||||
|
SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION);
|
||||||
|
SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1");
|
||||||
|
SG_CHECK_EQUAL(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml");
|
||||||
|
|
||||||
|
auto enabledCats = root->catalogs();
|
||||||
|
auto it = std::find(enabledCats.begin(), enabledCats.end(), cat);
|
||||||
|
SG_VERIFY(it == enabledCats.end());
|
||||||
|
|
||||||
|
// ensure existing package is still installed
|
||||||
|
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
|
||||||
|
SG_VERIFY(p1 != pkg::PackageRef());
|
||||||
|
SG_CHECK_EQUAL(p1->id(), "b737-NG");
|
||||||
|
|
||||||
|
// but not listed
|
||||||
|
auto packs = root->allPackages();
|
||||||
|
auto k = std::find(packs.begin(), packs.end(), p1);
|
||||||
|
SG_VERIFY(k == packs.end());
|
||||||
|
|
||||||
|
// check the new catalog
|
||||||
|
auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt");
|
||||||
|
SG_VERIFY(altCat->isEnabled());
|
||||||
|
SG_CHECK_EQUAL(altCat->status(), pkg::Delegate::STATUS_REFRESHED);
|
||||||
|
SG_CHECK_EQUAL(altCat->id(), "org.flightgear.test.catalog-alt");
|
||||||
|
SG_CHECK_EQUAL(altCat->url(), "http://localhost:2000/catalogTest1/catalog-alt.xml");
|
||||||
|
|
||||||
|
it = std::find(enabledCats.begin(), enabledCats.end(), altCat);
|
||||||
|
SG_VERIFY(it != enabledCats.end());
|
||||||
|
|
||||||
|
// install a parallel package from the new catalog
|
||||||
|
pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG");
|
||||||
|
SG_CHECK_EQUAL(p2->id(), "b737-NG");
|
||||||
|
pkg::InstallRef ins = p2->install();
|
||||||
|
SG_VERIFY(ins->isQueued());
|
||||||
|
waitForUpdateComplete(cl, root);
|
||||||
|
SG_VERIFY(p2->isInstalled());
|
||||||
|
|
||||||
|
// do a non-scoped lookup, we should get the new one
|
||||||
|
pkg::PackageRef p3 = root->getPackageById("b737-NG");
|
||||||
|
SG_CHECK_EQUAL(p2, p3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test that re-init-ing doesn't mirgate again
|
||||||
|
{
|
||||||
|
pkg::RootRef root(new pkg::Root(rootPath, "7.5"));
|
||||||
|
root->setHTTPClient(cl);
|
||||||
|
|
||||||
|
root->refresh(true);
|
||||||
|
waitForUpdateComplete(cl, root);
|
||||||
|
|
||||||
|
pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1");
|
||||||
|
SG_VERIFY(!cat->isEnabled());
|
||||||
|
SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION);
|
||||||
|
|
||||||
|
auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt");
|
||||||
|
SG_VERIFY(altCat->isEnabled());
|
||||||
|
|
||||||
|
auto packs = root->allPackages();
|
||||||
|
SG_CHECK_EQUAL(packs.size(), 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// and now switch back to the older version
|
||||||
|
{
|
||||||
|
pkg::RootRef root(new pkg::Root(rootPath, "8.1.0"));
|
||||||
|
root->setHTTPClient(cl);
|
||||||
|
root->refresh(true);
|
||||||
|
waitForUpdateComplete(cl, root);
|
||||||
|
|
||||||
|
pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1");
|
||||||
|
SG_VERIFY(cat->isEnabled());
|
||||||
|
SG_CHECK_EQUAL(cat->status(), pkg::Delegate::STATUS_REFRESHED);
|
||||||
|
|
||||||
|
auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt");
|
||||||
|
SG_VERIFY(!altCat->isEnabled());
|
||||||
|
|
||||||
|
// verify the original aircraft is still installed and available
|
||||||
|
pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG");
|
||||||
|
SG_VERIFY(p1 != pkg::PackageRef());
|
||||||
|
SG_CHECK_EQUAL(p1->id(), "b737-NG");
|
||||||
|
SG_VERIFY(p1->isInstalled());
|
||||||
|
|
||||||
|
// verify the alt package is still installed,
|
||||||
|
pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG");
|
||||||
|
SG_VERIFY(p2 != pkg::PackageRef());
|
||||||
|
SG_CHECK_EQUAL(p2->id(), "b737-NG");
|
||||||
|
SG_VERIFY(p2->isInstalled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void testOfflineMode(HTTP::Client* cl)
|
void testOfflineMode(HTTP::Client* cl)
|
||||||
{
|
{
|
||||||
global_catalogVersion = 0;
|
global_catalogVersion = 0;
|
||||||
@@ -990,6 +1114,8 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
removeInvalidCatalog(&cl);
|
removeInvalidCatalog(&cl);
|
||||||
|
|
||||||
|
testVersionMigrateToId(&cl);
|
||||||
|
|
||||||
SG_LOG(SG_GENERAL, SG_INFO, "Successfully passed all tests!");
|
SG_LOG(SG_GENERAL, SG_INFO, "Successfully passed all tests!");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -414,7 +414,7 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) :
|
|||||||
if (cat && cat->isEnabled()) {
|
if (cat && cat->isEnabled()) {
|
||||||
d->catalogs.insert({cat->id(), cat});
|
d->catalogs.insert({cat->id(), cat});
|
||||||
} else if (cat) {
|
} else if (cat) {
|
||||||
SG_LOG(SG_GENERAL, SG_WARN, "Package-Root init: catalog is disabled: " << cat->id());
|
SG_LOG(SG_GENERAL, SG_DEBUG, "Package-Root init: catalog is disabled: " << cat->id());
|
||||||
}
|
}
|
||||||
} // of child directories iteration
|
} // of child directories iteration
|
||||||
}
|
}
|
||||||
@@ -450,6 +450,17 @@ CatalogRef Root::getCatalogById(const std::string& aId) const
|
|||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CatalogRef Root::getCatalogByUrl(const std::string& aUrl) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(d->catalogs.begin(), d->catalogs.end(),
|
||||||
|
[aUrl](const CatalogDict::value_type& v)
|
||||||
|
{ return (v.second->url() == aUrl); });
|
||||||
|
if (it == d->catalogs.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
PackageRef Root::getPackageById(const std::string& aName) const
|
PackageRef Root::getPackageById(const std::string& aName) const
|
||||||
{
|
{
|
||||||
size_t lastDot = aName.rfind('.');
|
size_t lastDot = aName.rfind('.');
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ public:
|
|||||||
|
|
||||||
CatalogRef getCatalogById(const std::string& aId) const;
|
CatalogRef getCatalogById(const std::string& aId) const;
|
||||||
|
|
||||||
|
CatalogRef getCatalogByUrl(const std::string& aUrl) const;
|
||||||
|
|
||||||
void scheduleToUpdate(InstallRef aInstall);
|
void scheduleToUpdate(InstallRef aInstall);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
141
simgear/package/catalogTest1/catalog-alt.xml
Normal file
141
simgear/package/catalogTest1/catalog-alt.xml
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<PropertyList>
|
||||||
|
<id>org.flightgear.test.catalog-alt</id>
|
||||||
|
<description>Alternate test catalog</description>
|
||||||
|
<url>http://localhost:2000/catalogTest1/catalog-alt.xml</url>
|
||||||
|
<catalog-version>4</catalog-version>
|
||||||
|
|
||||||
|
<version>7.*</version>
|
||||||
|
|
||||||
|
|
||||||
|
<package>
|
||||||
|
<id>alpha</id>
|
||||||
|
<name>Alpha package</name>
|
||||||
|
<revision type="int">9</revision>
|
||||||
|
<file-size-bytes type="int">593</file-size-bytes>
|
||||||
|
|
||||||
|
<md5>a469c4b837f0521db48616cfe65ac1ea</md5>
|
||||||
|
<url>http://localhost:2000/catalogTest1/alpha.zip</url>
|
||||||
|
|
||||||
|
<dir>alpha</dir>
|
||||||
|
</package>
|
||||||
|
|
||||||
|
<package>
|
||||||
|
<id>c172p</id>
|
||||||
|
<name>Cessna 172-P</name>
|
||||||
|
<dir>c172p</dir>
|
||||||
|
<description>A plane made by Cessna</description>
|
||||||
|
<revision type="int">42</revision>
|
||||||
|
<file-size-bytes type="int">860</file-size-bytes>
|
||||||
|
|
||||||
|
<tag>cessna</tag>
|
||||||
|
<tag>ga</tag>
|
||||||
|
<tag>piston</tag>
|
||||||
|
<tag>ifr</tag>
|
||||||
|
|
||||||
|
<rating>
|
||||||
|
<FDM type="int">3</FDM>
|
||||||
|
<systems type="int">4</systems>
|
||||||
|
<model type="int">5</model>
|
||||||
|
<cockpit type="int">4</cockpit>
|
||||||
|
</rating>
|
||||||
|
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
<type>exterior</type>
|
||||||
|
<path>thumb-exterior.png</path>
|
||||||
|
<url>http://foo.bar.com/thumb-exterior.png</url>
|
||||||
|
</preview>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
<type>panel</type>
|
||||||
|
<path>thumb-panel.png</path>
|
||||||
|
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||||
|
</preview>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
<path>thumb-something.png</path>
|
||||||
|
<url>http://foo.bar.com/thumb-something.png</url>
|
||||||
|
</preview>
|
||||||
|
|
||||||
|
<variant>
|
||||||
|
<id>c172p-2d-panel</id>
|
||||||
|
<name>C172 with 2d panel only</name>
|
||||||
|
</variant>
|
||||||
|
|
||||||
|
<variant>
|
||||||
|
<id>c172p-floats</id>
|
||||||
|
<name>C172 with floats</name>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
<type>exterior</type>
|
||||||
|
<path>thumb-exterior-floats.png</path>
|
||||||
|
<url>http://foo.bar.com/thumb-exterior-floats.png</url>
|
||||||
|
</preview>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
<type>panel</type>
|
||||||
|
<path>thumb-panel.png</path>
|
||||||
|
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||||
|
</preview>
|
||||||
|
</variant>
|
||||||
|
|
||||||
|
<variant>
|
||||||
|
<id>c172p-skis</id>
|
||||||
|
<name>C172 with skis</name>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
<type>exterior</type>
|
||||||
|
<path>thumb-exterior-skis.png</path>
|
||||||
|
<url>http://foo.bar.com/thumb-exterior-skis.png</url>
|
||||||
|
</preview>
|
||||||
|
|
||||||
|
<preview>
|
||||||
|
<type>panel</type>
|
||||||
|
<path>thumb-panel.png</path>
|
||||||
|
<url>http://foo.bar.com/thumb-panel.png</url>
|
||||||
|
</preview>
|
||||||
|
</variant>
|
||||||
|
|
||||||
|
<md5>ec0e2ffdf98d6a5c05c77445e5447ff5</md5>
|
||||||
|
<url>http://localhost:2000/catalogTest1/c172p.zip</url>
|
||||||
|
|
||||||
|
</package>
|
||||||
|
|
||||||
|
<package>
|
||||||
|
<id>b737-NG</id>
|
||||||
|
<name>Boeing 737 NG</name>
|
||||||
|
<dir>b737NG</dir>
|
||||||
|
<description>A popular twin-engined narrow body jet</description>
|
||||||
|
<revision type="int">112</revision>
|
||||||
|
<file-size-bytes type="int">860</file-size-bytes>
|
||||||
|
|
||||||
|
<tag>boeing</tag>
|
||||||
|
<tag>jet</tag>
|
||||||
|
<tag>ifr</tag>
|
||||||
|
|
||||||
|
<rating>
|
||||||
|
<FDM type="int">5</FDM>
|
||||||
|
<systems type="int">5</systems>
|
||||||
|
<model type="int">4</model>
|
||||||
|
<cockpit type="int">4</cockpit>
|
||||||
|
</rating>
|
||||||
|
|
||||||
|
<md5>a94ca5704f305b90767f40617d194ed6</md5>
|
||||||
|
<url>http://localhost:2000/catalogTest1/b737.tar.gz</url>
|
||||||
|
|
||||||
|
</package>
|
||||||
|
|
||||||
|
<package>
|
||||||
|
<id>dc3</id>
|
||||||
|
<name>DC-3</name>
|
||||||
|
<revision type="int">9</revision>
|
||||||
|
<file-size-bytes type="int">593</file-size-bytes>
|
||||||
|
|
||||||
|
<md5>a469c4b837f0521db48616cfe65ac1ea</md5>
|
||||||
|
<url>http://localhost:2000/catalogTest1/alpha.zip</url>
|
||||||
|
|
||||||
|
<dir>dc3</dir>
|
||||||
|
</package>
|
||||||
|
</PropertyList>
|
||||||
@@ -15,6 +15,12 @@
|
|||||||
<url>http://localhost:2000/catalogTest1/catalog-v10.xml</url>
|
<url>http://localhost:2000/catalogTest1/catalog-v10.xml</url>
|
||||||
</alternate-version>
|
</alternate-version>
|
||||||
|
|
||||||
|
<alternate-version>
|
||||||
|
<version>7.*</version>
|
||||||
|
<id>org.flightgear.test.catalog-alt</id>
|
||||||
|
<url>http://localhost:2000/catalogTest1/catalog-alt.xml</url>
|
||||||
|
</alternate-version>
|
||||||
|
|
||||||
<package>
|
<package>
|
||||||
<id>alpha</id>
|
<id>alpha</id>
|
||||||
<name>Alpha package</name>
|
<name>Alpha package</name>
|
||||||
|
|||||||
Reference in New Issue
Block a user