From f2018f4a878e8eb2974d4bb392935c444a551e20 Mon Sep 17 00:00:00 2001 From: Ahmed Hesham Abdelkader <23265119+ahmedhesham6@users.noreply.github.com> Date: Thu, 15 Jan 2026 13:45:50 +0100 Subject: [PATCH] fix: sync remote tags before listing for publish Fetch tags from origin before listing available tags during publish. This ensures version bump suggestions are based on the latest GitHub tags, preventing conflicts when tags are created from other machines or directly on GitHub. --- apps/cli/src/commands/core/git.rs | 6 ++++++ apps/cli/src/commands/publish.rs | 24 +++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/apps/cli/src/commands/core/git.rs b/apps/cli/src/commands/core/git.rs index 617f5f4..9873670 100644 --- a/apps/cli/src/commands/core/git.rs +++ b/apps/cli/src/commands/core/git.rs @@ -47,6 +47,12 @@ pub fn tag_exists(path: &Path, tag: &str) -> bool { .unwrap_or(false) } +/// Fetch tags from remote to sync local tags with remote +pub fn fetch_tags(path: &Path, remote: &str) -> Result<()> { + git_cmd(&["fetch", remote, "--tags", "--force"], path)?; + Ok(()) +} + /// List all tags sorted by version (newest first) pub fn list_tags(path: &Path) -> Result> { let output = git_cmd(&["tag", "-l", "--sort=-v:refname"], path)?; diff --git a/apps/cli/src/commands/publish.rs b/apps/cli/src/commands/publish.rs index c2db84f..93266a1 100644 --- a/apps/cli/src/commands/publish.rs +++ b/apps/cli/src/commands/publish.rs @@ -64,6 +64,11 @@ fn filter_semver_tags(tags: &[String]) -> Vec { .collect() } +/// Get the latest semver version from a list of tags +fn get_latest_version(tags: &[String]) -> Option { + filter_semver_tags(tags).into_iter().next() +} + /// Prompt user to select version bump type, existing tag, or enter custom version fn prompt_tag_selection(existing_tags: &[String], current_version: &str) -> Result { // Filter to only semver tags @@ -255,8 +260,19 @@ pub async fn run(args: PublishArgs) -> Result<()> { } // Step 4: Determine which tag to use + // First, sync local tags with remote to ensure we have the latest state from GitHub + print!(" Syncing tags from origin... "); + match git::fetch_tags(&skill_path, remote) { + Ok(()) => println!("✓"), + Err(e) => println!("⚠ ({})", e), + } + + // Now list local tags (which are synced with remote) let existing_tags = git::list_tags(&skill_path)?; + // Get the latest version from tags for bump calculations + let latest_version = get_latest_version(&existing_tags); + let (tag, needs_create) = if let Some(explicit_tag) = args.tag.clone() { // User explicitly provided a tag via --tag flag - validate it's semver let tag_to_check = if explicit_tag.starts_with('v') { @@ -271,8 +287,9 @@ pub async fn run(args: PublishArgs) -> Result<()> { } (tag_to_check, false) } else if args.yes { - // Non-interactive mode: create patch bump - let (major, minor, patch) = parse_version(current_version)?; + // Non-interactive mode: create patch bump based on latest tag version + let base_version = latest_version.as_deref().unwrap_or(current_version); + let (major, minor, patch) = parse_version(base_version)?; let new_tag = format!("v{}.{}.{}", major, minor, patch + 1); if git::tag_exists(&skill_path, &new_tag) { bail!("Tag {} already exists.", new_tag); @@ -281,7 +298,8 @@ pub async fn run(args: PublishArgs) -> Result<()> { } else { // Interactive mode: let user choose bump type or existing tag println!(); - match prompt_tag_selection(&existing_tags, current_version)? { + let base_version = latest_version.as_deref().unwrap_or(current_version); + match prompt_tag_selection(&existing_tags, base_version)? { TagSelection::New(tag) => { if git::tag_exists(&skill_path, &tag) { bail!("Tag {} already exists.", tag);