792 lines
25 KiB
Plaintext
792 lines
25 KiB
Plaintext
#!/usr/bin/env nu
|
|
|
|
# Release creation tool - creates GitHub releases with automated changelog
|
|
#
|
|
# Creates:
|
|
# - Git tags with proper versioning
|
|
# - GitHub releases with generated changelogs
|
|
# - Release notes from commit history
|
|
# - Asset uploads and management
|
|
# - Version bumping automation
|
|
|
|
use std log
|
|
|
|
def main [
|
|
--version: string # Release version (e.g., 2.1.0, auto-increment if empty)
|
|
--release-type: string = "patch" # Release type: major, minor, patch, pre-release
|
|
--pre-release: bool = false # Mark as pre-release
|
|
--draft: bool = false # Create as draft release
|
|
--generate-changelog: bool = true # Generate changelog from commits
|
|
--asset-dir: string = "packages" # Directory containing release assets
|
|
--dry-run: bool = false # Show what would be created without doing it
|
|
--push-tag: bool = true # Push git tag to remote
|
|
--auto-upload: bool = true # Automatically upload assets
|
|
--verbose: bool = false # Enable verbose logging
|
|
] -> record {
|
|
|
|
let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
|
|
|
|
# Determine release version
|
|
let target_version = if $version == "" {
|
|
determine_next_version $repo_root $release_type
|
|
} else {
|
|
$version
|
|
}
|
|
|
|
let release_config = {
|
|
version: $target_version
|
|
release_type: $release_type
|
|
pre_release: $pre_release
|
|
draft: $draft
|
|
generate_changelog: $generate_changelog
|
|
asset_dir: ($asset_dir | path expand)
|
|
dry_run: $dry_run
|
|
push_tag: $push_tag
|
|
auto_upload: $auto_upload
|
|
verbose: $verbose
|
|
repo_root: $repo_root
|
|
}
|
|
|
|
log info $"Starting release creation with config: ($release_config)"
|
|
|
|
# Validate repository state
|
|
let repo_validation = validate_repository_state $repo_root $release_config
|
|
|
|
if $repo_validation.status != "ready" {
|
|
log error $"Repository not ready for release: ($repo_validation.reason)"
|
|
exit 1
|
|
}
|
|
|
|
let release_results = []
|
|
|
|
try {
|
|
# Generate changelog if requested
|
|
let changelog_result = if $release_config.generate_changelog {
|
|
generate_changelog $repo_root $release_config
|
|
} else {
|
|
{ status: "skipped", content: "", commits: [] }
|
|
}
|
|
|
|
let release_results = ($release_results | append { step: "changelog", result: $changelog_result })
|
|
|
|
# Create git tag
|
|
let tag_result = create_git_tag $repo_root $release_config
|
|
|
|
let release_results = ($release_results | append { step: "git_tag", result: $tag_result })
|
|
|
|
if $tag_result.status != "success" and not $release_config.dry_run {
|
|
log error $"Failed to create git tag: ($tag_result.reason)"
|
|
exit 1
|
|
}
|
|
|
|
# Push tag if requested
|
|
let push_result = if $release_config.push_tag and not $release_config.dry_run {
|
|
push_git_tag $repo_root $release_config
|
|
} else {
|
|
{ status: "skipped", reason: "push disabled or dry run" }
|
|
}
|
|
|
|
let release_results = ($release_results | append { step: "push_tag", result: $push_result })
|
|
|
|
# Create GitHub release
|
|
let github_result = create_github_release $repo_root $release_config $changelog_result
|
|
|
|
let release_results = ($release_results | append { step: "github_release", result: $github_result })
|
|
|
|
if $github_result.status != "success" and not $release_config.dry_run {
|
|
log error $"Failed to create GitHub release: ($github_result.reason)"
|
|
exit 1
|
|
}
|
|
|
|
# Upload assets if requested
|
|
let upload_result = if $release_config.auto_upload and not $release_config.dry_run {
|
|
upload_release_assets $repo_root $release_config $github_result
|
|
} else {
|
|
{ status: "skipped", reason: "auto-upload disabled or dry run", assets: [] }
|
|
}
|
|
|
|
let release_results = ($release_results | append { step: "upload_assets", result: $upload_result })
|
|
|
|
# Update version in project files
|
|
let version_update_result = update_project_version $repo_root $release_config
|
|
|
|
let release_results = ($release_results | append { step: "version_update", result: $version_update_result })
|
|
|
|
let summary = {
|
|
version: $release_config.version
|
|
release_type: $release_config.release_type
|
|
success: true
|
|
github_url: $github_result.release_url
|
|
tag_name: $"v($release_config.version)"
|
|
changelog_commits: ($changelog_result.commits | length)
|
|
uploaded_assets: ($upload_result.assets | length)
|
|
release_config: $release_config
|
|
steps: $release_results
|
|
}
|
|
|
|
if $release_config.dry_run {
|
|
log info $"Dry run completed - would create release v($release_config.version)"
|
|
} else {
|
|
log info $"Release v($release_config.version) created successfully: ($github_result.release_url)"
|
|
}
|
|
|
|
return $summary
|
|
|
|
} catch {|err|
|
|
log error $"Release creation failed: ($err.msg)"
|
|
|
|
# Cleanup on failure if not dry run
|
|
if not $release_config.dry_run {
|
|
cleanup_failed_release $repo_root $release_config
|
|
}
|
|
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Determine next version based on current version and release type
|
|
def determine_next_version [repo_root: string, release_type: string] -> string {
|
|
cd $repo_root
|
|
|
|
# Get latest tag
|
|
let latest_tag = try {
|
|
git describe --tags --abbrev=0 2>/dev/null | str trim
|
|
} catch {
|
|
"v0.0.0"
|
|
}
|
|
|
|
# Parse version from tag (remove 'v' prefix if present)
|
|
let current_version = ($latest_tag | str replace "^v" "")
|
|
let version_parts = ($current_version | split row ".")
|
|
|
|
if ($version_parts | length) < 3 {
|
|
log warning $"Invalid version format: ($current_version), using 0.0.0"
|
|
return "0.1.0"
|
|
}
|
|
|
|
let major = ($version_parts | get 0 | into int)
|
|
let minor = ($version_parts | get 1 | into int)
|
|
let patch = ($version_parts | get 2 | into int)
|
|
|
|
match $release_type {
|
|
"major" => { $"($major + 1).0.0" }
|
|
"minor" => { $"($major).($minor + 1).0" }
|
|
"patch" => { $"($major).($minor).($patch + 1)" }
|
|
"pre-release" => { $"($major).($minor).($patch + 1)-rc1" }
|
|
_ => {
|
|
log warning $"Unknown release type: ($release_type), using patch"
|
|
$"($major).($minor).($patch + 1)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Validate repository state before creating release
|
|
def validate_repository_state [repo_root: string, release_config: record] -> record {
|
|
cd $repo_root
|
|
|
|
# Check if we're in a git repository
|
|
if not (".git" | path exists) {
|
|
return { status: "failed", reason: "not in a git repository" }
|
|
}
|
|
|
|
# Check for uncommitted changes
|
|
let status_result = (git status --porcelain | complete)
|
|
if $status_result.exit_code == 0 and ($status_result.stdout | str trim) != "" {
|
|
return { status: "failed", reason: "uncommitted changes in working directory" }
|
|
}
|
|
|
|
# Check if current branch is main/master
|
|
let current_branch = try {
|
|
git branch --show-current | str trim
|
|
} catch {
|
|
"unknown"
|
|
}
|
|
|
|
if not ($current_branch in ["main", "master"]) {
|
|
return {
|
|
status: "warning",
|
|
reason: $"not on main branch (current: ($current_branch))",
|
|
current_branch: $current_branch
|
|
}
|
|
}
|
|
|
|
# Check if we can push to remote
|
|
let remote_check = try {
|
|
git remote -v | complete
|
|
} catch {
|
|
{ exit_code: 1, stdout: "" }
|
|
}
|
|
|
|
if $remote_check.exit_code != 0 or ($remote_check.stdout | str trim) == "" {
|
|
return { status: "warning", reason: "no remote repository configured" }
|
|
}
|
|
|
|
# Check if GitHub CLI is available (for GitHub releases)
|
|
let gh_check = try {
|
|
gh --version | complete
|
|
} catch {
|
|
{ exit_code: 1 }
|
|
}
|
|
|
|
if $gh_check.exit_code != 0 {
|
|
return { status: "warning", reason: "GitHub CLI (gh) not available - manual release creation required" }
|
|
}
|
|
|
|
return { status: "ready", current_branch: $current_branch }
|
|
}
|
|
|
|
# Generate changelog from commit history
|
|
def generate_changelog [repo_root: string, release_config: record] -> record {
|
|
log info "Generating changelog from commit history..."
|
|
|
|
cd $repo_root
|
|
|
|
try {
|
|
# Get last release tag
|
|
let last_tag = try {
|
|
git describe --tags --abbrev=0 2>/dev/null | str trim
|
|
} catch {
|
|
# If no tags, get all commits
|
|
""
|
|
}
|
|
|
|
# Get commits since last release
|
|
let git_log_cmd = if $last_tag != "" {
|
|
$"git log ($last_tag)..HEAD --pretty=format:\"%h|%s|%an|%ad\" --date=short"
|
|
} else {
|
|
$"git log --pretty=format:\"%h|%s|%an|%ad\" --date=short"
|
|
}
|
|
|
|
let commit_lines = (bash -c $git_log_cmd | lines | where $it != "")
|
|
|
|
# Parse commits
|
|
let commits = $commit_lines | each {|line|
|
|
let parts = ($line | split column "|" hash subject author date)
|
|
if ($parts | length) >= 4 {
|
|
{
|
|
hash: $parts.hash
|
|
subject: $parts.subject
|
|
author: $parts.author
|
|
date: $parts.date
|
|
type: (classify_commit_type $parts.subject)
|
|
}
|
|
} else {
|
|
null
|
|
}
|
|
} | where $it != null
|
|
|
|
# Group commits by type
|
|
let grouped_commits = ($commits | group-by type)
|
|
|
|
# Generate changelog content
|
|
let changelog_content = format_changelog $grouped_commits $release_config
|
|
|
|
{
|
|
status: "success"
|
|
content: $changelog_content
|
|
commits: $commits
|
|
commit_count: ($commits | length)
|
|
since_tag: $last_tag
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
content: ""
|
|
commits: []
|
|
}
|
|
}
|
|
}
|
|
|
|
# Classify commit type based on conventional commits
|
|
def classify_commit_type [subject: string] -> string {
|
|
if ($subject =~ "^feat(\\(.+\\))?:") {
|
|
"features"
|
|
} else if ($subject =~ "^fix(\\(.+\\))?:") {
|
|
"bug_fixes"
|
|
} else if ($subject =~ "^docs(\\(.+\\))?:") {
|
|
"documentation"
|
|
} else if ($subject =~ "^refactor(\\(.+\\))?:") {
|
|
"refactoring"
|
|
} else if ($subject =~ "^test(\\(.+\\))?:") {
|
|
"testing"
|
|
} else if ($subject =~ "^chore(\\(.+\\))?:") {
|
|
"maintenance"
|
|
} else if ($subject =~ "^perf(\\(.+\\))?:") {
|
|
"performance"
|
|
} else if ($subject =~ "^ci(\\(.+\\))?:") {
|
|
"ci_cd"
|
|
} else {
|
|
"other"
|
|
}
|
|
}
|
|
|
|
# Format changelog content
|
|
def format_changelog [grouped_commits: record, release_config: record] -> string {
|
|
let mut changelog = []
|
|
|
|
$changelog = ($changelog | append $"# Release v($release_config.version)")
|
|
$changelog = ($changelog | append "")
|
|
$changelog = ($changelog | append $"**Release Date:** (date now | format date '%Y-%m-%d')")
|
|
$changelog = ($changelog | append "")
|
|
|
|
# Features
|
|
if "features" in ($grouped_commits | columns) {
|
|
$changelog = ($changelog | append "## 🚀 New Features")
|
|
$changelog = ($changelog | append "")
|
|
for commit in ($grouped_commits.features) {
|
|
$changelog = ($changelog | append $"- ($commit.subject) (($commit.hash))")
|
|
}
|
|
$changelog = ($changelog | append "")
|
|
}
|
|
|
|
# Bug Fixes
|
|
if "bug_fixes" in ($grouped_commits | columns) {
|
|
$changelog = ($changelog | append "## 🐛 Bug Fixes")
|
|
$changelog = ($changelog | append "")
|
|
for commit in ($grouped_commits.bug_fixes) {
|
|
$changelog = ($changelog | append $"- ($commit.subject) (($commit.hash))")
|
|
}
|
|
$changelog = ($changelog | append "")
|
|
}
|
|
|
|
# Performance Improvements
|
|
if "performance" in ($grouped_commits | columns) {
|
|
$changelog = ($changelog | append "## ⚡ Performance Improvements")
|
|
$changelog = ($changelog | append "")
|
|
for commit in ($grouped_commits.performance) {
|
|
$changelog = ($changelog | append $"- ($commit.subject) (($commit.hash))")
|
|
}
|
|
$changelog = ($changelog | append "")
|
|
}
|
|
|
|
# Refactoring
|
|
if "refactoring" in ($grouped_commits | columns) {
|
|
$changelog = ($changelog | append "## 🔧 Refactoring")
|
|
$changelog = ($changelog | append "")
|
|
for commit in ($grouped_commits.refactoring) {
|
|
$changelog = ($changelog | append $"- ($commit.subject) (($commit.hash))")
|
|
}
|
|
$changelog = ($changelog | append "")
|
|
}
|
|
|
|
# Documentation
|
|
if "documentation" in ($grouped_commits | columns) {
|
|
$changelog = ($changelog | append "## 📚 Documentation")
|
|
$changelog = ($changelog | append "")
|
|
for commit in ($grouped_commits.documentation) {
|
|
$changelog = ($changelog | append $"- ($commit.subject) (($commit.hash))")
|
|
}
|
|
$changelog = ($changelog | append "")
|
|
}
|
|
|
|
# Maintenance
|
|
if "maintenance" in ($grouped_commits | columns) {
|
|
$changelog = ($changelog | append "## 🧹 Maintenance")
|
|
$changelog = ($changelog | append "")
|
|
for commit in ($grouped_commits.maintenance) {
|
|
$changelog = ($changelog | append $"- ($commit.subject) (($commit.hash))")
|
|
}
|
|
$changelog = ($changelog | append "")
|
|
}
|
|
|
|
# Other changes
|
|
if "other" in ($grouped_commits | columns) {
|
|
$changelog = ($changelog | append "## 📝 Other Changes")
|
|
$changelog = ($changelog | append "")
|
|
for commit in ($grouped_commits.other) {
|
|
$changelog = ($changelog | append $"- ($commit.subject) (($commit.hash))")
|
|
}
|
|
$changelog = ($changelog | append "")
|
|
}
|
|
|
|
return ($changelog | str join "\n")
|
|
}
|
|
|
|
# Create git tag
|
|
def create_git_tag [repo_root: string, release_config: record] -> record {
|
|
log info $"Creating git tag: v($release_config.version)"
|
|
|
|
cd $repo_root
|
|
|
|
if $release_config.dry_run {
|
|
return {
|
|
status: "success"
|
|
tag_name: $"v($release_config.version)"
|
|
dry_run: true
|
|
}
|
|
}
|
|
|
|
try {
|
|
let tag_name = $"v($release_config.version)"
|
|
|
|
# Check if tag already exists
|
|
let tag_exists = try {
|
|
git tag -l $tag_name | complete
|
|
} catch {
|
|
{ exit_code: 1, stdout: "" }
|
|
}
|
|
|
|
if $tag_exists.exit_code == 0 and ($tag_exists.stdout | str trim) != "" {
|
|
return {
|
|
status: "failed"
|
|
reason: $"tag ($tag_name) already exists"
|
|
tag_name: $tag_name
|
|
}
|
|
}
|
|
|
|
# Create annotated tag
|
|
let tag_message = $"Release v($release_config.version)"
|
|
git tag -a $tag_name -m $tag_message
|
|
|
|
{
|
|
status: "success"
|
|
tag_name: $tag_name
|
|
message: $tag_message
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
tag_name: $"v($release_config.version)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Push git tag to remote
|
|
def push_git_tag [repo_root: string, release_config: record] -> record {
|
|
log info $"Pushing git tag: v($release_config.version)"
|
|
|
|
cd $repo_root
|
|
|
|
try {
|
|
let tag_name = $"v($release_config.version)"
|
|
git push origin $tag_name
|
|
|
|
{
|
|
status: "success"
|
|
tag_name: $tag_name
|
|
pushed_to: "origin"
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
tag_name: $"v($release_config.version)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Create GitHub release
|
|
def create_github_release [
|
|
repo_root: string
|
|
release_config: record
|
|
changelog_result: record
|
|
] -> record {
|
|
log info $"Creating GitHub release: v($release_config.version)"
|
|
|
|
cd $repo_root
|
|
|
|
if $release_config.dry_run {
|
|
return {
|
|
status: "success"
|
|
release_url: $"https://github.com/owner/repo/releases/tag/v($release_config.version)"
|
|
dry_run: true
|
|
}
|
|
}
|
|
|
|
try {
|
|
let tag_name = $"v($release_config.version)"
|
|
let release_title = $"Release v($release_config.version)"
|
|
|
|
# Prepare release notes
|
|
let release_notes = if $changelog_result.status == "success" {
|
|
$changelog_result.content
|
|
} else {
|
|
$"Release v($release_config.version)\n\nChanges in this release:\n- Bug fixes and improvements"
|
|
}
|
|
|
|
# Save release notes to temporary file
|
|
let notes_file = ($repo_root | path join "tmp-release-notes.md")
|
|
$release_notes | save $notes_file
|
|
|
|
# Build gh release create command
|
|
let mut gh_cmd = ["gh", "release", "create", $tag_name, "--title", $release_title, "--notes-file", $notes_file]
|
|
|
|
if $release_config.pre_release {
|
|
$gh_cmd = ($gh_cmd | append "--prerelease")
|
|
}
|
|
|
|
if $release_config.draft {
|
|
$gh_cmd = ($gh_cmd | append "--draft")
|
|
}
|
|
|
|
# Create release
|
|
let release_result = (run-external --redirect-combine $gh_cmd.0 ...$gh_cmd.1.. | complete)
|
|
|
|
# Clean up temporary file
|
|
rm $notes_file
|
|
|
|
if $release_result.exit_code == 0 {
|
|
# Get release URL
|
|
let release_url = ($release_result.stdout | str trim)
|
|
|
|
{
|
|
status: "success"
|
|
release_url: $release_url
|
|
tag_name: $tag_name
|
|
title: $release_title
|
|
pre_release: $release_config.pre_release
|
|
draft: $release_config.draft
|
|
}
|
|
} else {
|
|
{
|
|
status: "failed"
|
|
reason: $release_result.stderr
|
|
tag_name: $tag_name
|
|
}
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
tag_name: $"v($release_config.version)"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Upload release assets
|
|
def upload_release_assets [
|
|
repo_root: string
|
|
release_config: record
|
|
github_result: record
|
|
] -> record {
|
|
log info "Uploading release assets..."
|
|
|
|
if not ($release_config.asset_dir | path exists) {
|
|
return {
|
|
status: "skipped"
|
|
reason: "asset directory does not exist"
|
|
assets: []
|
|
}
|
|
}
|
|
|
|
cd $repo_root
|
|
|
|
try {
|
|
# Find assets to upload
|
|
let asset_patterns = ["*.tar.gz", "*.zip", "*.deb", "*.rpm", "*.msi", "*.dmg", "checksums.txt", "manifest.json"]
|
|
let mut assets = []
|
|
|
|
for pattern in $asset_patterns {
|
|
let found_assets = (find $release_config.asset_dir -name $pattern -type f)
|
|
$assets = ($assets | append $found_assets)
|
|
}
|
|
|
|
$assets = ($assets | uniq)
|
|
|
|
if ($assets | length) == 0 {
|
|
return {
|
|
status: "skipped"
|
|
reason: "no assets found to upload"
|
|
assets: []
|
|
}
|
|
}
|
|
|
|
let tag_name = $"v($release_config.version)"
|
|
let mut upload_results = []
|
|
|
|
# Upload each asset
|
|
for asset in $assets {
|
|
try {
|
|
log info $"Uploading asset: ($asset | path basename)"
|
|
let upload_result = (gh release upload $tag_name $asset | complete)
|
|
|
|
if $upload_result.exit_code == 0 {
|
|
$upload_results = ($upload_results | append {
|
|
asset: ($asset | path basename)
|
|
status: "success"
|
|
size: (ls $asset | get 0.size)
|
|
})
|
|
} else {
|
|
$upload_results = ($upload_results | append {
|
|
asset: ($asset | path basename)
|
|
status: "failed"
|
|
reason: $upload_result.stderr
|
|
})
|
|
}
|
|
|
|
} catch {|err|
|
|
$upload_results = ($upload_results | append {
|
|
asset: ($asset | path basename)
|
|
status: "failed"
|
|
reason: $err.msg
|
|
})
|
|
}
|
|
}
|
|
|
|
let successful_uploads = ($upload_results | where status == "success" | length)
|
|
let total_assets = ($upload_results | length)
|
|
|
|
{
|
|
status: (if $successful_uploads == $total_assets { "success" } else { "partial" })
|
|
total_assets: $total_assets
|
|
successful_uploads: $successful_uploads
|
|
failed_uploads: ($total_assets - $successful_uploads)
|
|
assets: $upload_results
|
|
}
|
|
|
|
} catch {|err|
|
|
{
|
|
status: "failed"
|
|
reason: $err.msg
|
|
assets: []
|
|
}
|
|
}
|
|
}
|
|
|
|
# Update project version in files
|
|
def update_project_version [repo_root: string, release_config: record] -> record {
|
|
log info "Updating project version files..."
|
|
|
|
if $release_config.dry_run {
|
|
return { status: "skipped", reason: "dry run", updated_files: [] }
|
|
}
|
|
|
|
cd $repo_root
|
|
|
|
let mut updated_files = []
|
|
|
|
# Update Cargo.toml files
|
|
let cargo_files = (find . -name "Cargo.toml" -type f)
|
|
for cargo_file in $cargo_files {
|
|
try {
|
|
let content = (open $cargo_file --raw)
|
|
let updated_content = ($content | str replace 'version = "[^"]*"' $'version = "($release_config.version)"')
|
|
|
|
if $content != $updated_content {
|
|
$updated_content | save $cargo_file
|
|
$updated_files = ($updated_files | append $cargo_file)
|
|
log info $"Updated version in ($cargo_file)"
|
|
}
|
|
} catch {
|
|
log warning $"Failed to update version in ($cargo_file)"
|
|
}
|
|
}
|
|
|
|
# Update package.json files
|
|
let package_files = (find . -name "package.json" -type f)
|
|
for package_file in $package_files {
|
|
try {
|
|
let package_data = (open $package_file)
|
|
let updated_data = ($package_data | upsert version $release_config.version)
|
|
$updated_data | to json | save $package_file
|
|
$updated_files = ($updated_files | append $package_file)
|
|
log info $"Updated version in ($package_file)"
|
|
} catch {
|
|
log warning $"Failed to update version in ($package_file)"
|
|
}
|
|
}
|
|
|
|
{
|
|
status: "success"
|
|
updated_files: $updated_files
|
|
files_updated: ($updated_files | length)
|
|
}
|
|
}
|
|
|
|
# Cleanup failed release
|
|
def cleanup_failed_release [repo_root: string, release_config: record] {
|
|
log info "Cleaning up failed release..."
|
|
|
|
cd $repo_root
|
|
|
|
# Delete local tag if it was created
|
|
let tag_name = $"v($release_config.version)"
|
|
try {
|
|
git tag -d $tag_name
|
|
log info $"Deleted local tag: ($tag_name)"
|
|
} catch {
|
|
# Tag might not have been created
|
|
}
|
|
|
|
# Delete remote tag if it was pushed
|
|
if $release_config.push_tag {
|
|
try {
|
|
git push --delete origin $tag_name
|
|
log info $"Deleted remote tag: ($tag_name)"
|
|
} catch {
|
|
# Tag might not have been pushed
|
|
}
|
|
}
|
|
}
|
|
|
|
# Show release information
|
|
def "main info" [] {
|
|
let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
|
|
cd $repo_root
|
|
|
|
let latest_tag = try {
|
|
git describe --tags --abbrev=0 2>/dev/null | str trim
|
|
} catch {
|
|
"none"
|
|
}
|
|
|
|
let current_branch = try {
|
|
git branch --show-current | str trim
|
|
} catch {
|
|
"unknown"
|
|
}
|
|
|
|
let commits_since_tag = if $latest_tag != "none" {
|
|
try {
|
|
git rev-list $"($latest_tag)..HEAD" --count | into int
|
|
} catch {
|
|
0
|
|
}
|
|
} else {
|
|
try {
|
|
git rev-list HEAD --count | into int
|
|
} catch {
|
|
0
|
|
}
|
|
}
|
|
|
|
{
|
|
repository: $repo_root
|
|
current_branch: $current_branch
|
|
latest_tag: $latest_tag
|
|
commits_since_tag: $commits_since_tag
|
|
next_versions: {
|
|
patch: (determine_next_version $repo_root "patch")
|
|
minor: (determine_next_version $repo_root "minor")
|
|
major: (determine_next_version $repo_root "major")
|
|
}
|
|
github_cli_available: (try { gh --version | complete } catch { { exit_code: 1 } }).exit_code == 0
|
|
}
|
|
}
|
|
|
|
# Preview changelog for upcoming release
|
|
def "main preview" [
|
|
--version: string = "" # Version for preview
|
|
--release-type: string = "patch" # Release type for version calculation
|
|
] {
|
|
let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
|
|
|
|
let target_version = if $version == "" {
|
|
determine_next_version $repo_root $release_type
|
|
} else {
|
|
$version
|
|
}
|
|
|
|
let preview_config = { version: $target_version, generate_changelog: true }
|
|
let changelog_result = generate_changelog $repo_root $preview_config
|
|
|
|
print $"Preview of changelog for v($target_version):"
|
|
print "=" * 50
|
|
print $changelog_result.content
|
|
} |