diff options
| author | Stefan Boberg <[email protected]> | 2026-05-04 12:48:50 +0200 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-05-04 12:48:50 +0200 |
| commit | ac8711e10eb19eada27c1d8eb58487dfb3199af9 (patch) | |
| tree | dce7ca871ad4fcc44880b18cecd7a5436ba4e27b | |
| parent | Oplog commands -> oplog subcommands (#1025) (diff) | |
| download | archived-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.md | 1 | ||||
| -rw-r--r-- | src/zen/cmds/cache_cmd.cpp | 17 | ||||
| -rw-r--r-- | src/zen/cmds/cache_cmd.h | 7 | ||||
| -rw-r--r-- | src/zen/cmds/projectstore_cmd.cpp | 217 | ||||
| -rw-r--r-- | src/zen/cmds/projectstore_cmd.h | 205 | ||||
| -rw-r--r-- | src/zen/zen.cpp | 2 |
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}, |