The project has three main release-related scripts with overlapping functionality:
gem-publish.sh (700+ lines) - Comprehensive release automationrelease.sh (290+ lines) - Alternative release workflowbuild.sh (180+ lines) - Gem building utilityProblem: Three scripts with unclear responsibilities
gem-publish.sh does everything (version bump, changelog, build, publish, GitHub release)release.sh duplicates most of this functionalitybuild.sh is a subset of bothImpact: Contributors don’t know which script to use
Problem: Single 700-line script trying to do too much
Example: Changelog generation alone is ~200 lines embedded in the main script
Problem: Different validation patterns across scripts
# gem-publish.sh
if [[ ! -f "$file" ]]; then
error "Required file not found: $file"
fi
# release.sh
if [[ ! -f "jekyll-theme-zer0.gemspec" ]]; then
error "Must be run from the repository root directory"
fi
Problem: Same operations implemented multiple times
Replace the three scripts with a modular approach:
scripts/
├── lib/
│ ├── version.sh # Version operations library
│ ├── changelog.sh # Changelog generation library
│ ├── validation.sh # Environment validation library
│ ├── git.sh # Git operations library
│ └── gem.sh # Gem build/publish library
├── release # Main release command (simplified)
├── build # Build-only command
└── analyze-commits.sh # Keep as-is (already focused)
version.sh - Version management
#!/bin/bash
# Single responsibility: version operations
get_current_version() { ... }
calculate_new_version() { ... }
update_version_files() { ... }
validate_version_format() { ... }
changelog.sh - Changelog generation
#!/bin/bash
# Single responsibility: changelog operations
generate_changelog() { ... }
extract_release_notes() { ... }
categorize_commit() { ... }
validation.sh - Environment validation
#!/bin/bash
# Single responsibility: validation checks
validate_git_repo() { ... }
validate_clean_working_dir() { ... }
validate_dependencies() { ... }
validate_rubygems_auth() { ... }
git.sh - Git operations
#!/bin/bash
# Single responsibility: git operations
get_last_version_tag() { ... }
commit_release() { ... }
create_tag() { ... }
push_changes() { ... }
gem.sh - Gem operations
#!/bin/bash
# Single responsibility: gem build/publish
build_gem() { ... }
validate_gemspec() { ... }
publish_to_rubygems() { ... }
create_github_release() { ... }
release - Main release command (100 lines max)
#!/bin/bash
# Main release orchestrator
source "$(dirname "$0")/lib/validation.sh"
source "$(dirname "$0")/lib/version.sh"
source "$(dirname "$0")/lib/changelog.sh"
source "$(dirname "$0")/lib/gem.sh"
source "$(dirname "$0")/lib/git.sh"
main() {
# Parse simple arguments
local version_type="${1:-patch}"
local dry_run="${2:-false}"
# Validate
validate_environment
# Execute workflow
local current_version=$(get_current_version)
local new_version=$(calculate_new_version "$current_version" "$version_type")
generate_changelog "$new_version"
update_version_files "$new_version"
build_gem "$new_version"
commit_and_tag "$new_version"
publish_gem "$new_version"
create_github_release "$new_version"
push_changes
}
main "$@"
build - Build-only command (50 lines max)
#!/bin/bash
# Simple gem builder
source "$(dirname "$0")/lib/validation.sh"
source "$(dirname "$0")/lib/gem.sh"
main() {
validate_gemspec
build_gem "$(get_current_version)"
}
main "$@"
scripts/lib/ directorylib/version.shlib/validation.shscripts/release using libraries--dry-run*.legacy.shgem-publish.shrelease.shbuild.shBefore (gem-publish.sh):
./scripts/gem-publish.sh patch --dry-run --skip-tests --skip-changelog \
--skip-publish --no-github-release --non-interactive \
--automated-release --auto-commit-range=HEAD~5..HEAD
After (release):
# Most common case (patch release)
./scripts/release
# With options
./scripts/release minor --dry-run
# Advanced (still supported through env vars)
SKIP_TESTS=true ./scripts/release patch
scripts/test/lib/
├── test_version.sh
├── test_changelog.sh
├── test_validation.sh
├── test_git.sh
└── test_gem.sh
test/
├── test_release_workflow.sh
├── test_build_workflow.sh
└── test_dry_run.sh
Example deprecation wrapper:
#!/bin/bash
# gem-publish.sh (deprecated)
echo "⚠️ WARNING: gem-publish.sh is deprecated"
echo " Use: ./scripts/release $*"
echo ""
echo " This wrapper will be removed in v0.3.0"
echo ""
exec "$(dirname "$0")/release" "$@"
scripts/lib/ directory structureversion.shchangelog.shvalidation.shgit.shgem.shrelease commandbuild commandAfter refactoring, we should see:
Rakefile approach instead?
semantic-release?
Proceed with shell script refactoring because:
Next Steps: Review this proposal, get feedback, then start Phase 1 (library extraction) in a feature branch.