aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-05-04 12:48:50 +0200
committerGitHub Enterprise <[email protected]>2026-05-04 12:48:50 +0200
commitac8711e10eb19eada27c1d8eb58487dfb3199af9 (patch)
treedce7ca871ad4fcc44880b18cecd7a5436ba4e27b
parentOplog commands -> oplog subcommands (#1025) (diff)
downloadarchived-zen-ac8711e10eb19eada27c1d8eb58487dfb3199af9.tar.xz
archived-zen-ac8711e10eb19eada27c1d8eb58487dfb3199af9.zip
zen CLI: project-* commands → 'project <sub>' subcommands (#1026)
- Refactors the five `project-*` top-level commands into a `project <sub>` subcommand structure, mirroring the existing `cache <sub>` pattern. New surface: `project create | drop | info | op-details | stats`. - Legacy `project-create`, `project-drop`, `project-info`, `project-op-details`, `project-stats` remain functional as hidden deprecated shims that forward through `project_legacy_shim::RunAs`, so existing scripts (e.g. `scripts/test_scripts/oplog-import-export-test.py`) keep working unchanged.
-rw-r--r--CHANGELOG.md1
-rw-r--r--src/zen/cmds/cache_cmd.cpp17
-rw-r--r--src/zen/cmds/cache_cmd.h7
-rw-r--r--src/zen/cmds/projectstore_cmd.cpp217
-rw-r--r--src/zen/cmds/projectstore_cmd.h205
-rw-r--r--src/zen/zen.cpp2
6 files changed, 258 insertions, 191 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 479f6c2d2..9caed4afd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,5 @@
##
+- Improvement: `zen` consolidates the `project-*` commands into a single `zen project <sub>` command tree (`project create`, `project drop`, `project info`, `project op-details`, `project stats`). The legacy `project-create`/`project-drop`/etc. names remain as hidden deprecated aliases that forward to the new dispatcher, so existing scripts keep working.
- Improvement: `zen` consolidates the `oplog-*` commands into a single `zen oplog <sub>` command tree (`oplog create`, `oplog export`, `oplog import`, `oplog snapshot`, `oplog mirror`, `oplog validate`, `oplog download`). The legacy `oplog-create`/`oplog-export`/etc. names remain as hidden deprecated aliases that forward to the new dispatcher, so existing scripts keep working.
- Bugfix: `zen builds download` no longer injects the `default` build part when `--download-spec` is given; the spec drives part selection
diff --git a/src/zen/cmds/cache_cmd.cpp b/src/zen/cmds/cache_cmd.cpp
index f93a5318c..5deebeb4b 100644
--- a/src/zen/cmds/cache_cmd.cpp
+++ b/src/zen/cmds/cache_cmd.cpp
@@ -98,16 +98,6 @@ CacheSubCmdBase::CacheSubCmdBase(std::string_view Name, std::string_view Descrip
m_SubOptions.add_option("", "u", "hosturl", ZenCmdBase::kHostUrlHelp, cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}
-void
-CacheSubCmdBase::ResolveHost()
-{
- m_HostName = ZenCmdBase::ResolveTargetHostSpec(m_HostName);
- if (m_HostName.empty())
- {
- throw OptionParseException("Unable to resolve server specification", m_SubOptions.help());
- }
-}
-
////////////////////////////////////////////////////////////////////////////////
// Legacy shim dispatcher
@@ -170,7 +160,6 @@ CacheDropSubCmd::CacheDropSubCmd() : CacheSubCmdBase("drop", "Drop cache namespa
void
CacheDropSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
- ResolveHost();
ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "drop"});
HttpClient& Http = Service.Http();
@@ -225,7 +214,6 @@ CacheInfoSubCmd::CacheInfoSubCmd() : CacheSubCmdBase("info", "Info on cache, nam
void
CacheInfoSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
- ResolveHost();
ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "info"});
HttpClient& Http = Service.Http();
@@ -296,7 +284,6 @@ CacheStatsSubCmd::CacheStatsSubCmd() : CacheSubCmdBase("stats", "Stats on cache"
void
CacheStatsSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
- ResolveHost();
ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "stats"});
HttpClient& Http = Service.Http();
@@ -334,7 +321,6 @@ CacheDetailsSubCmd::CacheDetailsSubCmd() : CacheSubCmdBase("details", "Details o
void
CacheDetailsSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
- ResolveHost();
ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "details"});
HttpClient& Http = Service.Http();
@@ -433,7 +419,6 @@ CacheGenSubCmd::CacheGenSubCmd() : CacheSubCmdBase("gen", "Generates cache value
void
CacheGenSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
- ResolveHost();
ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "gen"});
HttpClient& Http = Service.Http();
@@ -670,7 +655,6 @@ CacheGetSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
using namespace std::literals;
- ResolveHost();
ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "get"});
HttpClient& Http = Service.Http();
@@ -796,7 +780,6 @@ CacheRecordSubCmd::CacheRecordSubCmd()
void
CacheRecordSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
- ResolveHost();
ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "record"});
HttpClient& Http = Service.Http();
diff --git a/src/zen/cmds/cache_cmd.h b/src/zen/cmds/cache_cmd.h
index a2834f73d..e871d9f47 100644
--- a/src/zen/cmds/cache_cmd.h
+++ b/src/zen/cmds/cache_cmd.h
@@ -8,17 +8,14 @@
namespace zen {
-// Base for `cache` subcommands. Registers the shared --hosturl option and
-// exposes ResolveHost() which subcommands must call before issuing HTTP
-// requests (it normalises m_HostName and throws if no host could be resolved).
+// Base for `cache` subcommands. Registers the shared --hosturl option;
+// host resolution is handled by ZenServiceClient at construction.
class CacheSubCmdBase : public ZenSubCmdBase
{
public:
CacheSubCmdBase(std::string_view Name, std::string_view Description);
protected:
- void ResolveHost();
-
std::string m_HostName;
};
diff --git a/src/zen/cmds/projectstore_cmd.cpp b/src/zen/cmds/projectstore_cmd.cpp
index b18637d10..2d025ae3a 100644
--- a/src/zen/cmds/projectstore_cmd.cpp
+++ b/src/zen/cmds/projectstore_cmd.cpp
@@ -590,33 +590,73 @@ namespace oplog_legacy_shim {
///////////////////////////////////////
-DropProjectCommand::DropProjectCommand()
+////////////////////////////////////////////////////////////////////////////////
+// ProjectCommand
+
+ProjectCommand::ProjectCommand()
{
m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", kHostUrlHelp, cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
- m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
- m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
- m_Options.add_option("", "", "dryrun", "Dry run - resolve arguments but do not drop", cxxopts::value(m_DryRun), "<dryrun>");
- m_Options.parse_positional({"project", "oplog"});
- m_Options.positional_help("[<projectid> [<oplogid>]]");
+
+ AddSubCommand(m_CreateSubCmd);
+ AddSubCommand(m_DropSubCmd);
+ AddSubCommand(m_InfoSubCmd);
+ AddSubCommand(m_OpDetailsSubCmd);
+ AddSubCommand(m_StatsSubCmd);
}
-DropProjectCommand::~DropProjectCommand()
+ProjectCommand::~ProjectCommand() = default;
+
+////////////////////////////////////////////////////////////////////////////////
+// ProjectSubCmdBase
+
+ProjectSubCmdBase::ProjectSubCmdBase(std::string_view Name, std::string_view Description) : ZenSubCmdBase(Name, Description)
{
+ m_SubOptions.add_option("", "u", "hosturl", ZenCmdBase::kHostUrlHelp, cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
}
-void
-DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
-{
- using namespace projectstore_impl;
- ZEN_UNUSED(GlobalOptions);
+////////////////////////////////////////////////////////////////////////////////
+// Legacy shim dispatcher
- if (!ParseOptions(argc, argv))
+namespace project_legacy_shim {
+ void RunAs(const char* SubCommandName, const ZenCliOptions& GlobalOptions, int argc, char** argv)
{
- return;
+ // cxxopts treats argv as writable char** in the style of C main(argv).
+ // Stage the injected token in writable std::string storage so we never
+ // hand out pointers to string literals.
+ std::string Token(SubCommandName);
+
+ std::vector<char*> NewArgv;
+ NewArgv.reserve(static_cast<size_t>(argc) + 1);
+ NewArgv.push_back(argv[0]);
+ NewArgv.push_back(Token.data());
+ for (int i = 1; i < argc; ++i)
+ {
+ NewArgv.push_back(argv[i]);
+ }
+
+ ProjectCommand Impl;
+ Impl.Run(GlobalOptions, static_cast<int>(NewArgv.size()), NewArgv.data());
}
+} // namespace project_legacy_shim
+
+////////////////////////////////////////////////////////////////////////////////
+// ProjectDropSubCmd
+
+ProjectDropSubCmd::ProjectDropSubCmd() : ProjectSubCmdBase("drop", "Drop project or project oplog")
+{
+ m_SubOptions.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
+ m_SubOptions.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
+ m_SubOptions.add_option("", "", "dryrun", "Dry run - resolve arguments but do not drop", cxxopts::value(m_DryRun), "<dryrun>");
+ m_SubOptions.parse_positional({"project", "oplog"});
+ m_SubOptions.positional_help("[<projectid> [<oplogid>]]");
+}
+
+void
+ProjectDropSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
+{
+ using namespace projectstore_impl;
- ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name});
+ ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "drop"});
HttpClient& Http = Service.Http();
m_ProjectName = ResolveProject(Http, m_ProjectName);
@@ -629,11 +669,13 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
{
if (m_DryRun)
{
- ZEN_CONSOLE("Would drop project '{}' from '{}'. Use --dryrun=false to execute the drop operation.", m_ProjectName, m_HostName);
+ ZEN_CONSOLE("Would drop project '{}' from '{}'. Use --dryrun=false to execute the drop operation.",
+ m_ProjectName,
+ Service.HostSpec());
}
else
{
- ZEN_CONSOLE("Dropping project '{}' from '{}'", m_ProjectName, m_HostName);
+ ZEN_CONSOLE("Dropping project '{}' from '{}'", m_ProjectName, Service.HostSpec());
if (HttpClient::Response Result = Http.Delete(fmt::format("/prj/{}", m_ProjectName)))
{
ZEN_CONSOLE("{}", Result);
@@ -656,11 +698,11 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
ZEN_CONSOLE("Would drop oplog '{}/{}' from '{}'. Add --dryrun=false to execute the drop operation.",
m_ProjectName,
m_OplogName,
- m_HostName);
+ Service.HostSpec());
}
else
{
- ZEN_CONSOLE("Dropping oplog '{}/{}' from '{}'", m_ProjectName, m_OplogName, m_HostName);
+ ZEN_CONSOLE("Dropping oplog '{}/{}' from '{}'", m_ProjectName, m_OplogName, Service.HostSpec());
if (HttpClient::Response Result = Http.Delete(fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName)))
{
ZEN_CONSOLE("{}", Result);
@@ -673,39 +715,28 @@ DropProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
}
}
-///////////////////////////////////////
-
-ProjectInfoCommand::ProjectInfoCommand()
-{
- m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", kHostUrlHelp, cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
- m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
- m_Options.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
- m_Options.parse_positional({"project", "oplog"});
- m_Options.positional_help("[<projectid> [<oplogid>]]");
-}
+////////////////////////////////////////////////////////////////////////////////
+// ProjectInfoSubCmd
-ProjectInfoCommand::~ProjectInfoCommand()
+ProjectInfoSubCmd::ProjectInfoSubCmd() : ProjectSubCmdBase("info", "Info on project or project oplog")
{
+ m_SubOptions.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectName), "<projectid>");
+ m_SubOptions.add_option("", "o", "oplog", "Oplog name", cxxopts::value(m_OplogName), "<oplogid>");
+ m_SubOptions.parse_positional({"project", "oplog"});
+ m_SubOptions.positional_help("[<projectid> [<oplogid>]]");
}
void
-ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
+ProjectInfoSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
using namespace projectstore_impl;
- ZEN_UNUSED(GlobalOptions);
-
- if (!ParseOptions(argc, argv))
- {
- return;
- }
if (!m_OplogName.empty() && m_ProjectName.empty())
{
- throw OptionParseException("'--project' is required", m_Options.help());
+ throw OptionParseException("'--project' is required", m_SubOptions.help());
}
- ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name});
+ ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "info"});
HttpClient& Http = Service.Http();
std::string Url;
@@ -723,7 +754,7 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
}
Url = fmt::format("/prj/{}", m_ProjectName);
- ZEN_CONSOLE("Info on project '{}' from '{}{}'", m_ProjectName, m_HostName, Url);
+ ZEN_CONSOLE("Info on project '{}' from '{}{}'", m_ProjectName, Service.HostSpec(), Url);
}
else
{
@@ -740,7 +771,7 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
}
Url = fmt::format("/prj/{}/oplog/{}", m_ProjectName, m_OplogName);
- ZEN_CONSOLE("Info on oplog '{}/{}' from '{}{}'", m_ProjectName, m_OplogName, m_HostName, Url);
+ ZEN_CONSOLE("Info on oplog '{}/{}' from '{}{}'", m_ProjectName, m_OplogName, Service.HostSpec(), Url);
}
if (HttpClient::Response Result = Http.Get(Url, HttpClient::Accept(ZenContentType::kJSON)))
@@ -753,42 +784,32 @@ ProjectInfoCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg
}
}
-///////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// ProjectCreateSubCmd
-CreateProjectCommand::CreateProjectCommand()
+ProjectCreateSubCmd::ProjectCreateSubCmd() : ProjectSubCmdBase("create", "Create a project")
{
- m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", kHostUrlHelp, cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
- m_Options.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectId), "<projectid>");
- m_Options.add_option("", "", "rootdir", "Absolute path to root directory", cxxopts::value(m_RootDir), "<root>");
- m_Options.add_option("", "", "enginedir", "Absolute path to engine root directory", cxxopts::value(m_EngineRootDir), "<engineroot>");
- m_Options.add_option("", "", "projectdir", "Absolute path to project directory", cxxopts::value(m_ProjectRootDir), "<projectroot>");
- m_Options.add_option("", "", "projectfile", "Absolute path to .uproject file", cxxopts::value(m_ProjectFile), "<projectfile>");
- m_Options.add_option("", "f", "force-update", "Force update of existing project", cxxopts::value(m_ForceUpdate), "<force-update>");
- m_Options.parse_positional({"project", "rootdir", "enginedir", "projectdir", "projectfile"});
+ m_SubOptions.add_option("", "p", "project", "Project name", cxxopts::value(m_ProjectId), "<projectid>");
+ m_SubOptions.add_option("", "", "rootdir", "Absolute path to root directory", cxxopts::value(m_RootDir), "<root>");
+ m_SubOptions.add_option("", "", "enginedir", "Absolute path to engine root directory", cxxopts::value(m_EngineRootDir), "<engineroot>");
+ m_SubOptions.add_option("", "", "projectdir", "Absolute path to project directory", cxxopts::value(m_ProjectRootDir), "<projectroot>");
+ m_SubOptions.add_option("", "", "projectfile", "Absolute path to .uproject file", cxxopts::value(m_ProjectFile), "<projectfile>");
+ m_SubOptions.add_option("", "f", "force-update", "Force update of existing project", cxxopts::value(m_ForceUpdate), "<force-update>");
+ m_SubOptions.parse_positional({"project", "rootdir", "enginedir", "projectdir", "projectfile"});
}
-CreateProjectCommand::~CreateProjectCommand() = default;
-
void
-CreateProjectCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
+ProjectCreateSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
using namespace projectstore_impl;
- ZEN_UNUSED(GlobalOptions);
-
using namespace std::literals;
- if (!ParseOptions(argc, argv))
- {
- return;
- }
-
if (m_ProjectId.empty())
{
- throw OptionParseException("'--project' is required", m_Options.help());
+ throw OptionParseException("'--project' is required", m_SubOptions.help());
}
- ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name});
+ ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "create"});
HttpClient& Http = Service.Http();
std::string Url = fmt::format("/prj/{}", m_ProjectId);
@@ -1857,28 +1878,19 @@ OplogSnapshotSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
////////////////////////////
-ProjectStatsCommand::ProjectStatsCommand()
-{
- m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", kHostUrlHelp, cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
-}
+////////////////////////////////////////////////////////////////////////////////
+// ProjectStatsSubCmd
-ProjectStatsCommand::~ProjectStatsCommand()
+ProjectStatsSubCmd::ProjectStatsSubCmd() : ProjectSubCmdBase("stats", "Stats on project store")
{
}
void
-ProjectStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
+ProjectStatsSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
using namespace projectstore_impl;
- ZEN_UNUSED(GlobalOptions);
-
- if (!ParseOptions(argc, argv))
- {
- return;
- }
- ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name});
+ ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "stats"});
HttpClient& Http = Service.Http();
if (HttpClient::Response Result = Http.Get("/stats/prj", HttpClient::Accept(ZenContentType::kJSON)))
{
@@ -1890,42 +1902,31 @@ ProjectStatsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** ar
}
}
-////////////////////////////
-
-ProjectOpDetailsCommand::ProjectOpDetailsCommand()
-{
- m_Options.add_options()("h,help", "Print help");
- m_Options.add_option("", "u", "hosturl", kHostUrlHelp, cxxopts::value(m_HostName)->default_value(""), "<hosturl>");
- m_Options.add_option("", "c", "csv", "Output in CSV format (default is JSon)", cxxopts::value(m_CSV), "<csv>");
- m_Options.add_option("", "d", "details", "Detailed info on oplog", cxxopts::value(m_Details), "<details>");
- m_Options.add_option("", "o", "opdetails", "Details info on oplog body", cxxopts::value(m_OpDetails), "<opdetails>");
- m_Options.add_option("", "p", "project", "Project name to get info from", cxxopts::value(m_ProjectName), "<projectid>");
- m_Options.add_option("", "l", "oplog", "Oplog name to get info from", cxxopts::value(m_OplogName), "<oplogid>");
- m_Options.add_option("", "i", "opid", "Oid of a specific op info for", cxxopts::value(m_OpId), "<opid>");
- m_Options.add_option("",
- "a",
- "attachmentdetails",
- "Get detailed information about attachments",
- cxxopts::value(m_AttachmentDetails),
- "<attachmentdetails>");
-}
+////////////////////////////////////////////////////////////////////////////////
+// ProjectOpDetailsSubCmd
-ProjectOpDetailsCommand::~ProjectOpDetailsCommand()
+ProjectOpDetailsSubCmd::ProjectOpDetailsSubCmd() : ProjectSubCmdBase("op-details", "Detail info on ops inside a project store oplog")
{
+ m_SubOptions.add_option("", "c", "csv", "Output in CSV format (default is JSon)", cxxopts::value(m_CSV), "<csv>");
+ m_SubOptions.add_option("", "d", "details", "Detailed info on oplog", cxxopts::value(m_Details), "<details>");
+ m_SubOptions.add_option("", "o", "opdetails", "Details info on oplog body", cxxopts::value(m_OpDetails), "<opdetails>");
+ m_SubOptions.add_option("", "p", "project", "Project name to get info from", cxxopts::value(m_ProjectName), "<projectid>");
+ m_SubOptions.add_option("", "l", "oplog", "Oplog name to get info from", cxxopts::value(m_OplogName), "<oplogid>");
+ m_SubOptions.add_option("", "i", "opid", "Oid of a specific op info for", cxxopts::value(m_OpId), "<opid>");
+ m_SubOptions.add_option("",
+ "a",
+ "attachmentdetails",
+ "Get detailed information about attachments",
+ cxxopts::value(m_AttachmentDetails),
+ "<attachmentdetails>");
}
void
-ProjectOpDetailsCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** argv)
+ProjectOpDetailsSubCmd::Run(const ZenCliOptions& /*GlobalOptions*/)
{
using namespace projectstore_impl;
- ZEN_UNUSED(GlobalOptions);
-
- if (!ParseOptions(argc, argv))
- {
- return;
- }
- ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = Name});
+ ZenServiceClient Service({.HostSpec = m_HostName, .CommandName = "op-details"});
HttpClient& Http = Service.Http();
m_ProjectName = ResolveProject(Http, m_ProjectName);
diff --git a/src/zen/cmds/projectstore_cmd.h b/src/zen/cmds/projectstore_cmd.h
index b43452551..fc019c312 100644
--- a/src/zen/cmds/projectstore_cmd.h
+++ b/src/zen/cmds/projectstore_cmd.h
@@ -13,104 +13,193 @@ class ProjectStoreCommand : public ZenCmdBase
virtual ZenCmdCategory& CommandCategory() const override { return g_ProjectStoreCategory; }
};
-class DropProjectCommand : public ProjectStoreCommand
+// Base for `project` subcommands. Registers the shared --hosturl option;
+// host resolution is handled by ZenServiceClient at construction.
+class ProjectSubCmdBase : public ZenSubCmdBase
{
public:
- static constexpr char Name[] = "project-drop";
- static constexpr char Description[] = "Drop project or project oplog";
+ ProjectSubCmdBase(std::string_view Name, std::string_view Description);
+
+protected:
+ std::string m_HostName;
+};
+
+class ProjectCreateSubCmd : public ProjectSubCmdBase
+{
+public:
+ ProjectCreateSubCmd();
+ void Run(const ZenCliOptions& GlobalOptions) override;
+
+private:
+ std::string m_ProjectId;
+ std::string m_RootDir;
+ std::string m_EngineRootDir;
+ std::string m_ProjectRootDir;
+ std::string m_ProjectFile;
+ bool m_ForceUpdate = false;
+};
+
+class ProjectDropSubCmd : public ProjectSubCmdBase
+{
+public:
+ ProjectDropSubCmd();
+ void Run(const ZenCliOptions& GlobalOptions) override;
+
+private:
+ std::string m_ProjectName;
+ std::string m_OplogName;
+ bool m_DryRun = true;
+};
+
+class ProjectInfoSubCmd : public ProjectSubCmdBase
+{
+public:
+ ProjectInfoSubCmd();
+ void Run(const ZenCliOptions& GlobalOptions) override;
- DropProjectCommand();
- ~DropProjectCommand();
+private:
+ std::string m_ProjectName;
+ std::string m_OplogName;
+};
- virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override;
- virtual cxxopts::Options& Options() override { return m_Options; }
+class ProjectStatsSubCmd : public ProjectSubCmdBase
+{
+public:
+ ProjectStatsSubCmd();
+ void Run(const ZenCliOptions& GlobalOptions) override;
+};
+
+class ProjectOpDetailsSubCmd : public ProjectSubCmdBase
+{
+public:
+ ProjectOpDetailsSubCmd();
+ void Run(const ZenCliOptions& GlobalOptions) override;
+
+private:
+ bool m_Details = false;
+ bool m_OpDetails = false;
+ bool m_AttachmentDetails = false;
+ bool m_CSV = false;
+ std::string m_ProjectName;
+ std::string m_OplogName;
+ std::string m_OpId;
+};
+
+class ProjectCommand : public ProjectStoreCmdWithSubCommands
+{
+public:
+ static constexpr char Name[] = "project";
+ static constexpr char Description[] = "Manage projects - create, drop, info, stats, op-details";
+
+ ProjectCommand();
+ ~ProjectCommand();
+
+ cxxopts::Options& Options() override { return m_Options; }
private:
cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
- std::string m_ProjectName;
- std::string m_OplogName;
- bool m_DryRun = true;
+
+ ProjectCreateSubCmd m_CreateSubCmd;
+ ProjectDropSubCmd m_DropSubCmd;
+ ProjectInfoSubCmd m_InfoSubCmd;
+ ProjectStatsSubCmd m_StatsSubCmd;
+ ProjectOpDetailsSubCmd m_OpDetailsSubCmd;
+};
+
+// ---------------------------------------------------------------------------
+// Deprecated legacy top-level commands. These forward to the corresponding
+// 'project <sub>' subcommand so existing scripts keep working. They are
+// hidden from the top-level `zen --help` listing; `zen project --help` is the
+// canonical discovery surface now.
+
+namespace project_legacy_shim {
+ void RunAs(const char* SubCommandName, const ZenCliOptions& GlobalOptions, int argc, char** argv);
+}
+
+class DeprecatedProjectStoreCommand : public ProjectStoreCommand
+{
+public:
+ bool IsHidden() const override { return true; }
};
-class ProjectInfoCommand : public ProjectStoreCommand
+class DropProjectCommand : public DeprecatedProjectStoreCommand
+{
+public:
+ static constexpr char Name[] = "project-drop";
+ static constexpr char Description[] = "(deprecated, use 'project drop') Drop project or project oplog";
+
+ void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override
+ {
+ project_legacy_shim::RunAs("drop", GlobalOptions, argc, argv);
+ }
+ cxxopts::Options& Options() override { return m_Options; }
+
+private:
+ cxxopts::Options m_Options{Name, Description};
+};
+
+class ProjectInfoCommand : public DeprecatedProjectStoreCommand
{
public:
static constexpr char Name[] = "project-info";
- static constexpr char Description[] = "Info on project or project oplog";
+ static constexpr char Description[] = "(deprecated, use 'project info') Info on project or project oplog";
- ProjectInfoCommand();
- ~ProjectInfoCommand();
- virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override;
- virtual cxxopts::Options& Options() override { return m_Options; }
+ void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override
+ {
+ project_legacy_shim::RunAs("info", GlobalOptions, argc, argv);
+ }
+ cxxopts::Options& Options() override { return m_Options; }
private:
cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
- std::string m_ProjectName;
- std::string m_OplogName;
};
-class CreateProjectCommand : public ProjectStoreCommand
+class CreateProjectCommand : public DeprecatedProjectStoreCommand
{
public:
static constexpr char Name[] = "project-create";
- static constexpr char Description[] = "Create a project";
-
- CreateProjectCommand();
- ~CreateProjectCommand();
+ static constexpr char Description[] = "(deprecated, use 'project create') Create a project";
- virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override;
- virtual cxxopts::Options& Options() override { return m_Options; }
+ void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override
+ {
+ project_legacy_shim::RunAs("create", GlobalOptions, argc, argv);
+ }
+ cxxopts::Options& Options() override { return m_Options; }
private:
cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
- std::string m_ProjectId;
- std::string m_RootDir;
- std::string m_EngineRootDir;
- std::string m_ProjectRootDir;
- std::string m_ProjectFile;
- bool m_ForceUpdate = false;
};
-class ProjectStatsCommand : public ProjectStoreCommand
+class ProjectStatsCommand : public DeprecatedProjectStoreCommand
{
public:
static constexpr char Name[] = "project-stats";
- static constexpr char Description[] = "Stats on project store";
+ static constexpr char Description[] = "(deprecated, use 'project stats') Stats on project store";
- ProjectStatsCommand();
- ~ProjectStatsCommand();
- virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override;
- virtual cxxopts::Options& Options() override { return m_Options; }
+ void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override
+ {
+ project_legacy_shim::RunAs("stats", GlobalOptions, argc, argv);
+ }
+ cxxopts::Options& Options() override { return m_Options; }
private:
cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
};
-class ProjectOpDetailsCommand : public ProjectStoreCommand
+class ProjectOpDetailsCommand : public DeprecatedProjectStoreCommand
{
public:
static constexpr char Name[] = "project-op-details";
- static constexpr char Description[] = "Detail info on ops inside a project store oplog";
+ static constexpr char Description[] = "(deprecated, use 'project op-details') Detail info on ops inside a project store oplog";
- ProjectOpDetailsCommand();
- ~ProjectOpDetailsCommand();
- virtual void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override;
- virtual cxxopts::Options& Options() override { return m_Options; }
+ void Run(const ZenCliOptions& GlobalOptions, int argc, char** argv) override
+ {
+ project_legacy_shim::RunAs("op-details", GlobalOptions, argc, argv);
+ }
+ cxxopts::Options& Options() override { return m_Options; }
private:
cxxopts::Options m_Options{Name, Description};
- std::string m_HostName;
- bool m_Details = false;
- bool m_OpDetails = false;
- bool m_AttachmentDetails = false;
- bool m_CSV = false;
- std::string m_ProjectName;
- std::string m_OplogName;
- std::string m_OpId;
};
// ---------------------------------------------------------------------------
@@ -357,12 +446,6 @@ namespace oplog_legacy_shim {
void RunAs(const char* SubCommandName, const ZenCliOptions& GlobalOptions, int argc, char** argv);
}
-class DeprecatedProjectStoreCommand : public ProjectStoreCommand
-{
-public:
- bool IsHidden() const override { return true; }
-};
-
class CreateOplogCommand : public DeprecatedProjectStoreCommand
{
public:
diff --git a/src/zen/zen.cpp b/src/zen/zen.cpp
index 202e31382..7f5c50b2f 100644
--- a/src/zen/zen.cpp
+++ b/src/zen/zen.cpp
@@ -677,6 +677,7 @@ main(int argc, char** argv)
OplogValidateCommand OplogValidateCmd;
PrintCommand PrintCmd;
PrintPackageCommand PrintPkgCmd;
+ ProjectCommand ProjectCmd;
ProjectOpDetailsCommand ProjectOpDetailsCmd;
ProjectInfoCommand ProjectInfoCmd;
ProjectStatsCommand ProjectStatsCmd;
@@ -741,6 +742,7 @@ main(int argc, char** argv)
{OplogValidateCommand::Name, &OplogValidateCmd, OplogValidateCommand::Description},
{PrintCommand::Name, &PrintCmd, PrintCommand::Description},
{PrintPackageCommand::Name, &PrintPkgCmd, PrintPackageCommand::Description},
+ {ProjectCommand::Name, &ProjectCmd, ProjectCommand::Description},
{CreateProjectCommand::Name, &CreateProjectCmd, CreateProjectCommand::Description},
{ProjectOpDetailsCommand::Name, &ProjectOpDetailsCmd, ProjectOpDetailsCommand::Description},
{DropProjectCommand::Name, &ProjectDropCmd, DropProjectCommand::Description},