Skip to content

Commit 51f413a

Browse files
committed
Add release scripts.
1 parent c5156b1 commit 51f413a

File tree

6 files changed

+262
-8
lines changed

6 files changed

+262
-8
lines changed

.github/workflows/release.yml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
permissions:
9+
contents: write # Required for creating releases and uploading assets
10+
11+
env:
12+
CARGO_TERM_COLOR: always
13+
RUST_BACKTRACE: 1
14+
15+
jobs:
16+
create-release:
17+
name: Create Release
18+
runs-on: ubuntu-latest
19+
outputs:
20+
upload_url: ${{ steps.release.outputs.upload_url }}
21+
release_id: ${{ steps.release.outputs.id }}
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Extract version from tag
26+
id: get_version
27+
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
28+
29+
- name: Create Release
30+
id: release
31+
uses: actions/create-release@v1
32+
env:
33+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34+
with:
35+
tag_name: ${{ steps.get_version.outputs.VERSION }}
36+
release_name: Ruby Butler ${{ steps.get_version.outputs.VERSION }}
37+
body: |
38+
🎩 **Ruby Butler ${{ steps.get_version.outputs.VERSION }}**
39+
40+
A sophisticated Ruby environment manager release with the refined precision of a proper gentleman's gentleman.
41+
42+
## Installation
43+
44+
Download the appropriate binary for your platform below and place it in your PATH.
45+
46+
## What's New
47+
48+
See the [CHANGELOG](https://github.com/RubyElders/ruby-butler/blob/main/CHANGELOG.md) for detailed changes.
49+
50+
## Binaries
51+
52+
- **Linux**: `rb` and `rb-debug`
53+
- **macOS**: `rb` and `rb-debug`
54+
- **Windows**: `rb.exe` and `rb-debug.exe`
55+
56+
---
57+
*At your distinguished service,*
58+
*RubyElders.com*
59+
draft: true
60+
prerelease: false
61+
62+
build-release:
63+
name: Build Release Binaries
64+
needs: create-release
65+
strategy:
66+
matrix:
67+
include:
68+
- os: ubuntu-latest
69+
target: x86_64-unknown-linux-gnu
70+
suffix: linux
71+
ext: ""
72+
- os: macos-latest
73+
target: x86_64-apple-darwin
74+
suffix: macos
75+
ext: ""
76+
- os: windows-latest
77+
target: x86_64-pc-windows-msvc
78+
suffix: windows
79+
ext: ".exe"
80+
runs-on: ${{ matrix.os }}
81+
steps:
82+
- uses: actions/checkout@v4
83+
with:
84+
fetch-depth: 0 # Need full history for git info
85+
86+
- name: Install Rust
87+
uses: dtolnay/rust-toolchain@stable
88+
with:
89+
toolchain: 1.90.0
90+
targets: ${{ matrix.target }}
91+
92+
- name: Cache cargo registry
93+
uses: actions/cache@v4
94+
with:
95+
path: |
96+
~/.cargo/registry
97+
~/.cargo/git
98+
target
99+
key: ${{ matrix.os }}-1.90.0-cargo-${{ hashFiles('**/Cargo.lock') }}
100+
restore-keys: |
101+
${{ matrix.os }}-1.90.0-cargo-
102+
${{ matrix.os }}-cargo-
103+
104+
- name: Build release binary
105+
run: cargo build --release --target ${{ matrix.target }}
106+
107+
- name: Build debug binary
108+
run: cargo build --target ${{ matrix.target }}
109+
110+
- name: Upload release binary
111+
uses: actions/upload-release-asset@v1
112+
env:
113+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114+
with:
115+
upload_url: ${{ needs.create-release.outputs.upload_url }}
116+
asset_path: target/${{ matrix.target }}/release/rb${{ matrix.ext }}
117+
asset_name: rb${{ matrix.ext }}
118+
asset_content_type: application/octet-stream
119+
120+
- name: Upload debug binary
121+
uses: actions/upload-release-asset@v1
122+
env:
123+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
124+
with:
125+
upload_url: ${{ needs.create-release.outputs.upload_url }}
126+
asset_path: target/${{ matrix.target }}/debug/rb${{ matrix.ext }}
127+
asset_name: rb-debug${{ matrix.ext }}
128+
asset_content_type: application/octet-stream

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Changelog
2+
3+
All notable changes to Ruby Butler will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
## [0.1.0] - Initial Release
11+
12+
### Added
13+
- Initial implementation of Ruby Butler
14+
15+
---
16+
17+
*Distinguished releases crafted with appropriate ceremony by RubyElders.com*

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,27 @@ Ruby Butler is built with Rust for cross-platform reliability and employs a **en
109109

110110
If you're curious about what Butler does under the hood, add `-v` or even better `-vv` to see the distinguished orchestration in action.
111111

112+
## Release Process
113+
114+
To create a new release with cross-platform binaries:
115+
116+
1. **Update version** in `crates/rb-cli/Cargo.toml`
117+
2. **Update CHANGELOG.md** with release notes
118+
3. **Create and push tag**:
119+
```bash
120+
git tag v1.0.0
121+
git push --tags
122+
```
123+
124+
The release workflow automatically:
125+
- Builds binaries for Linux, macOS, and Windows (both release and debug)
126+
- Creates GitHub release with binaries attached
127+
- Embeds git information in version output (`rb --version`)
128+
129+
Released binaries include version traceability:
130+
- **Tagged builds**: `Ruby Butler v1.0.0`
131+
- **Development builds**: `Ruby Butler v0.1.0 (c5156b1) [debug build] [modified]`
132+
112133
---
113134

114135
*Distinguished Ruby development deserves distinguished tooling.*

crates/rb-cli/build.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,63 @@
11
use std::env;
2+
use std::process::Command;
23

34
fn main() {
4-
// Only run this in release builds to avoid copying during development
5-
let profile = env::var("PROFILE").unwrap_or_default();
6-
if profile != "release" {
7-
return;
5+
// Capture git information for version embedding
6+
println!("cargo:rerun-if-changed=.git/HEAD");
7+
println!("cargo:rerun-if-changed=.git/refs/");
8+
9+
// Get git commit hash
10+
let git_hash = Command::new("git")
11+
.args(["rev-parse", "--short", "HEAD"])
12+
.output()
13+
.ok()
14+
.and_then(|output| {
15+
if output.status.success() {
16+
String::from_utf8(output.stdout).ok()
17+
} else {
18+
None
19+
}
20+
})
21+
.map(|s| s.trim().to_string())
22+
.unwrap_or_else(|| "unknown".to_string());
23+
24+
// Get git tag (if on a tagged commit)
25+
let git_tag = Command::new("git")
26+
.args(["describe", "--tags", "--exact-match", "HEAD"])
27+
.output()
28+
.ok()
29+
.and_then(|output| {
30+
if output.status.success() {
31+
String::from_utf8(output.stdout).ok()
32+
} else {
33+
None
34+
}
35+
})
36+
.map(|s| s.trim().to_string());
37+
38+
// Check if working directory is dirty
39+
let git_dirty = Command::new("git")
40+
.args(["diff", "--quiet", "HEAD"])
41+
.output()
42+
.map(|output| !output.status.success())
43+
.unwrap_or(false);
44+
45+
// Set environment variables for the binary
46+
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
47+
48+
if let Some(tag) = git_tag {
49+
println!("cargo:rustc-env=GIT_TAG={}", tag);
850
}
951

10-
println!("cargo:rerun-if-changed=src/");
52+
if git_dirty {
53+
println!("cargo:rustc-env=GIT_DIRTY=true");
54+
}
1155

12-
// This will run after the binary is built
13-
println!("cargo:warning=Build script executed for release build");
56+
// Build profile information
57+
let profile = env::var("PROFILE").unwrap_or_default();
58+
println!("cargo:rustc-env=BUILD_PROFILE={}", profile);
59+
60+
if profile == "release" {
61+
println!("cargo:warning=Build script executed for release build");
62+
}
1463
}

crates/rb-cli/src/bin/rb.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,46 @@ use rb_cli::{
55
};
66
use rb_core::butler::{ButlerError, ButlerRuntime};
77

8+
fn build_version_info() -> String {
9+
let version = env!("CARGO_PKG_VERSION");
10+
let git_hash = option_env!("GIT_HASH").unwrap_or("unknown");
11+
let profile = option_env!("BUILD_PROFILE").unwrap_or("unknown");
12+
13+
let mut parts = vec![format!("Ruby Butler v{}", version)];
14+
15+
// Add tag if available, otherwise add git hash
16+
if let Some(tag) = option_env!("GIT_TAG") {
17+
if !tag.is_empty() && tag != format!("v{}", version) {
18+
parts.push(format!("({})", tag));
19+
}
20+
} else if git_hash != "unknown" {
21+
parts.push(format!("({})", git_hash));
22+
}
23+
24+
// Add profile if debug
25+
if profile == "debug" {
26+
parts.push("[debug build]".to_string());
27+
}
28+
29+
// Add dirty flag if present
30+
if option_env!("GIT_DIRTY").is_some() {
31+
parts.push("[modified]".to_string());
32+
}
33+
34+
parts.push("\n\nA sophisticated Ruby environment manager with the refined precision".to_string());
35+
parts.push("of a proper gentleman's gentleman.\n".to_string());
36+
parts.push("At your distinguished service, RubyElders.com".to_string());
37+
38+
parts.join(" ")
39+
}
40+
841
fn main() {
42+
// Handle version request with custom formatting before parsing
43+
if std::env::args().any(|arg| arg == "--version" || arg == "-V") {
44+
println!("{}", build_version_info());
45+
return;
46+
}
47+
948
let cli = Cli::parse();
1049

1150
// Initialize logger with the effective log level (considering -v/-vv flags)

crates/rb-cli/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ impl From<LogLevel> for log::LevelFilter {
3838
long_about = "🎩 Ruby Butler\n\nA sophisticated Ruby environment manager that orchestrates your Ruby installations\nand gem collections with the refined precision of a proper gentleman's gentleman.\n\nNot merely a version switcher, but your devoted aide in curating Ruby environments\nwith the elegance and attention to detail befitting a distinguished developer.\n\n At your service,\n RubyElders.com"
3939
)]
4040
#[command(author = "RubyElders.com")]
41-
#[command(version = "0.1.0")]
41+
#[command(version)]
4242
#[command(propagate_version = true)]
4343
#[command(styles = STYLES)]
4444
pub struct Cli {

0 commit comments

Comments
 (0)