Implemented semver command.
h3rald h3rald@h3rald.com
Fri, 12 Jun 2026 13:37:00 +0200
7 files changed,
65 insertions(+),
2 deletions(-)
M
README.md
→
README.md
@@ -21,5 +21,6 @@ init: Initialize the project for ConVer usage.
draft: Draft a new release by specifying score and metadata. status: Display the current Draft and Release versions, if any. release: Move the current draft version to release. + semver: Generate SemVer version number based on history. history: Print the full version history of the project. ```
M
src/conver.c
→
src/conver.c
@@ -10,6 +10,8 @@ "A version number compliant with Semantic Versioning can be derived from the project history, "
"and will still be used if required by package managers or similar software. \n\n" "For more information, see the [Convergent Versioning specification](https://h3rald.com/conver).\n"; +conver_semver_t semver; + #if defined(_WIN32) || defined(_WIN64) # define FS_WINDOWS # ifndef WIN32_LEAN_AND_MEAN@@ -373,6 +375,7 @@ printf(" init: Initialize the project for ConVer usage.\n");
printf(" draft: Draft a new release by specifying score and metadata.\n"); printf(" status: Display the current Draft and Release versions, if any.\n"); printf(" release: Move the current draft version to release.\n"); + printf(" semver: Generate SemVer version number based on history.\n"); printf(" history: Print the full version history of the project.\n"); }@@ -410,6 +413,11 @@ if (write_all || !fs_file_exists(CONVER_HISTORY_FILE))
{ printf("Creating %s...\n", CONVER_HISTORY_FILE); result += print_result(fs_file_create(CONVER_HISTORY_FILE)); + } + if (write_all || !fs_file_exists(CONVER_SEMVER_FILE)) + { + printf("Creating %s...\n", CONVER_SEMVER_FILE); + result += print_result(fs_file_create(CONVER_SEMVER_FILE)); } if (!fs_file_exists(CONVER_VERSIONING_FILE)) {@@ -663,6 +671,49 @@ print_release(history[i]);
} return CONVER_OK; } + +int command_semver() +{ + int result = parse_history(); + if (result != CONVER_OK){ + return result; + } + if (history_size == 0) { + printf("No project history available.\n"); + return CONVER_OK; + } + char *sv; + size_t sv_len = 0; + fs_read(CONVER_LEGACY_FILE, &sv, &sv_len); + sscanf(sv, "%hu.%hu.%hu", &semver.major, &semver.minor, &semver.patch); + for (int i=0; i<history_size; i++) { + conver_release_t r = history[i]; + uint16_t v = version(r); + if (compatibility(v) == CONVER_COMPAT_BREAK) { + if (stage(v) > CONVER_STAGE_PROTOTYPE) { + semver.major += 1; + semver.minor = 0; + semver.patch = 0; + } else { + // 0.x.y release + semver.minor += 1; + semver.patch = 0; + } + } else { + if (purpose(v) == CONVER_PURPOSE_ENHANCE) { + semver.minor += 1; + semver.patch = 0; + } else { + semver.patch += 1; + } + } + } + char semver_string[16]; + snprintf(semver_string, sizeof(semver_string), "%hu.%hu.%hu\n", semver.major, semver.minor, semver.patch); + printf("%s\n", semver_string); + return fs_write(CONVER_SEMVER_FILE, semver_string); + return CONVER_OK; +} //// Main@@ -703,6 +754,10 @@ }
if ((strcmp(arg, "history") == 0)) { return command_history(); + } + if ((strcmp(arg, "semver") == 0)) + { + return command_semver(); } } }
M
src/conver.h
→
src/conver.h
@@ -8,6 +8,7 @@ #define CONVER_DRAFT_FILE ".conver/draft.txt"
#define CONVER_RELEASE_FILE ".conver/release.txt" #define CONVER_LEGACY_FILE ".conver/legacy.txt" #define CONVER_HISTORY_FILE ".conver/history.txt" +#define CONVER_SEMVER_FILE ".conver/semver.txt" #define CONVER_VERSIONING_FILE "VERSIONING.md" #include <stddef.h>@@ -108,12 +109,16 @@ "Maintenance",
"Enhancement", }; -typedef struct { +typedef struct conver_release_t { struct tm date; bool widthdrawn; uint16_t score; uint8_t metadata; char comment[256]; } conver_release_t; + +typedef struct conver_semver_t { + uint16_t major, minor, patch; +} conver_semver_t; #endif // CONVER_H