Full C++23 framework tree: runtime, observability, security, data, app, realtime, supervisor, clients, CLI, example plus enterprise tenancy, audit, operability, governance, supplychain. Postgres via docker compose for tests.
144 lines
4.1 KiB
C++
144 lines
4.1 KiB
C++
#include "Workflow.hpp"
|
|
|
|
#include <cstdio>
|
|
|
|
#include "Caps.hpp"
|
|
|
|
namespace molo::example::fw {
|
|
|
|
bool parse_commit(const std::string& subject, const std::string& body,
|
|
CommitInfo& out, std::string& error) {
|
|
std::string s = subject;
|
|
if (s.size() > (std::size_t)caps::kMaxCommitSubject) {
|
|
error = "subject too long";
|
|
return false;
|
|
}
|
|
std::size_t colon = s.find(':');
|
|
if (colon == std::string::npos) {
|
|
error = "missing colon";
|
|
return false;
|
|
}
|
|
std::string head = s.substr(0, colon);
|
|
out.description = s.substr(colon + 1);
|
|
while (!out.description.empty() && out.description.front() == ' ')
|
|
out.description.erase(out.description.begin());
|
|
if (!head.empty() && head.back() == '!') {
|
|
out.breaking = true;
|
|
head.pop_back();
|
|
}
|
|
std::size_t paren = head.find('(');
|
|
if (paren == std::string::npos) {
|
|
out.type = head;
|
|
} else {
|
|
out.type = head.substr(0, paren);
|
|
std::size_t close = head.find(')', paren);
|
|
if (close == std::string::npos) {
|
|
error = "unclosed scope";
|
|
return false;
|
|
}
|
|
out.scope = head.substr(paren + 1, close - paren - 1);
|
|
}
|
|
const char* allowed[] = {"feat", "fix", "docs", "style", "refactor",
|
|
"perf", "test", "build", "ci", "chore"};
|
|
bool ok = false;
|
|
for (const char* t : allowed) {
|
|
if (out.type == t) {
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!ok) {
|
|
error = "unknown type: " + out.type;
|
|
return false;
|
|
}
|
|
if (body.find("BREAKING CHANGE") != std::string::npos) out.breaking = true;
|
|
return true;
|
|
}
|
|
|
|
std::string bump_name(Bump bump) {
|
|
switch (bump) {
|
|
case Bump::None: return "none";
|
|
case Bump::Patch: return "patch";
|
|
case Bump::Minor: return "minor";
|
|
case Bump::Major: return "major";
|
|
}
|
|
return "none";
|
|
}
|
|
|
|
Bump ReleasePlanner::decide(const std::vector<CommitInfo>& commits) const {
|
|
bool feat = false, fix = false;
|
|
for (const auto& c : commits) {
|
|
if (c.breaking) return Bump::Major;
|
|
if (c.type == "feat") feat = true;
|
|
if (c.type == "fix") fix = true;
|
|
}
|
|
if (feat) return Bump::Minor;
|
|
if (fix) return Bump::Patch;
|
|
return Bump::None;
|
|
}
|
|
|
|
std::string ReleasePlanner::next_version(
|
|
const std::string& current,
|
|
const std::vector<CommitInfo>& commits) const {
|
|
int major = 0, minor = 0, patch = 0;
|
|
std::sscanf(current.c_str(), "%d.%d.%d", &major, &minor, &patch);
|
|
switch (decide(commits)) {
|
|
case Bump::Major: return std::to_string(major + 1) + ".0.0";
|
|
case Bump::Minor:
|
|
return std::to_string(major) + "." + std::to_string(minor + 1) + ".0";
|
|
case Bump::Patch:
|
|
return std::to_string(major) + "." + std::to_string(minor) + "." +
|
|
std::to_string(patch + 1);
|
|
case Bump::None: return current;
|
|
}
|
|
return current;
|
|
}
|
|
|
|
std::string ChangelogWriter::entry(
|
|
const std::string& version, const std::string& date,
|
|
const std::vector<CommitInfo>& commits) const {
|
|
std::string added, changed, fixed, removed;
|
|
for (const auto& c : commits) {
|
|
std::string line =
|
|
"- " + (c.scope.empty() ? "" : "(" + c.scope + ") ") + c.description +
|
|
"\n";
|
|
if (c.breaking)
|
|
changed += line;
|
|
else if (c.type == "feat")
|
|
added += line;
|
|
else if (c.type == "fix")
|
|
fixed += line;
|
|
else
|
|
changed += line;
|
|
}
|
|
std::string out = "## [" + version + "] - " + date + "\n";
|
|
if (!added.empty()) out += "### Added\n" + added;
|
|
if (!changed.empty()) out += "### Changed\n" + changed;
|
|
if (!fixed.empty()) out += "### Fixed\n" + fixed;
|
|
if (!removed.empty()) out += "### Removed\n" + removed;
|
|
int lines = 0;
|
|
for (char c : out) {
|
|
if (c == '\n') ++lines;
|
|
}
|
|
if (lines > caps::kMaxChangelogLines)
|
|
out += "(truncated to cap)\n";
|
|
return out;
|
|
}
|
|
|
|
void ReleaseVerifier::add_check(std::string name, bool passed,
|
|
std::string detail) {
|
|
checks_.push_back({std::move(name), passed, std::move(detail)});
|
|
}
|
|
|
|
bool ReleaseVerifier::green() const {
|
|
if (checks_.empty()) return false;
|
|
for (const auto& c : checks_) {
|
|
if (!c.passed) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<VerifyCheck> ReleaseVerifier::checks() const { return checks_; }
|
|
|
|
} // namespace molo::example::fw
|