Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/openshell-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ const DOCTOR_HELP: &str = "\x1b[1mALIAS\x1b[0m
/// `OpenShell` CLI - agent execution and management.
#[derive(Parser, Debug)]
#[command(name = "openshell")]
#[command(author, version, about, long_about = None)]
#[command(author, version = openshell_core::VERSION, about, long_about = None)]
#[command(propagate_version = true)]
#[command(help_template = HELP_TEMPLATE)]
#[command(disable_help_subcommand = true)]
Expand Down
58 changes: 58 additions & 0 deletions crates/openshell-core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
use std::env;

fn main() -> Result<(), Box<dyn std::error::Error>> {
// --- Git-derived version ---
// Compute a version from `git describe` for local builds. In Docker/CI
// builds where .git is absent, this silently does nothing and the binary
// falls back to CARGO_PKG_VERSION (which is already sed-patched by the
// build pipeline).
println!("cargo:rerun-if-changed=../../.git/HEAD");
println!("cargo:rerun-if-changed=../../.git/refs/tags");

if let Some(version) = git_version() {
println!("cargo:rustc-env=OPENSHELL_GIT_VERSION={version}");
}

// --- Protobuf compilation ---
// Use bundled protoc from protobuf-src
// SAFETY: This is run at build time in a single-threaded build script context.
// No other threads are reading environment variables concurrently.
Expand Down Expand Up @@ -33,3 +46,48 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

Ok(())
}

/// Derive a version string from `git describe --tags`.
///
/// Implements the "guess-next-dev" convention used by the release pipeline
/// (`setuptools-scm`): when there are commits past the last tag, the patch
/// version is bumped and `-dev.<N>+g<sha>` is appended.
///
/// Examples:
/// on tag v0.0.3 → "0.0.3"
/// 3 commits past v0.0.3 → "0.0.4-dev.3+g2bf9969"
///
/// Returns `None` when git is unavailable or the repo has no matching tags.
fn git_version() -> Option<String> {
let output = std::process::Command::new("git")
.args(["describe", "--tags", "--long", "--match", "v*"])
.output()
.ok()?;

if !output.status.success() {
return None;
}

let desc = String::from_utf8(output.stdout).ok()?;
let desc = desc.trim();
let desc = desc.strip_prefix('v').unwrap_or(desc);

// `git describe --long` format: <tag>-<N>-g<sha>
// Split from the right to handle tags that contain hyphens.
let (rest, sha) = desc.rsplit_once('-')?;
let (tag, commits_str) = rest.rsplit_once('-')?;
let commits: u32 = commits_str.parse().ok()?;

if commits == 0 {
// Exactly on a tag — use the tag version as-is.
return Some(tag.to_string());
}

// Bump patch version (guess-next-dev scheme).
let mut parts = tag.splitn(3, '.');
let major = parts.next()?;
let minor = parts.next()?;
let patch: u32 = parts.next()?.parse().ok()?;

Some(format!("{major}.{minor}.{}-dev.{commits}+{sha}", patch + 1))
}
12 changes: 12 additions & 0 deletions crates/openshell-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! - Protocol buffer definitions and generated code
//! - Configuration management
//! - Common error types
//! - Build version metadata

pub mod config;
pub mod error;
Expand All @@ -16,3 +17,14 @@ pub mod proto;

pub use config::{Config, TlsConfig};
pub use error::{Error, Result};

/// Build version string derived from git metadata.
///
/// For local builds this is computed by `build.rs` via `git describe` using
/// the guess-next-dev scheme (e.g. `0.0.4-dev.6+g2bf9969`). In Docker/CI
/// builds where `.git` is absent, falls back to `CARGO_PKG_VERSION` which
/// is already set correctly by the build pipeline's sed patch.
pub const VERSION: &str = match option_env!("OPENSHELL_GIT_VERSION") {
Some(v) => v,
None => env!("CARGO_PKG_VERSION"),
};
1 change: 1 addition & 0 deletions crates/openshell-sandbox/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use openshell_sandbox::run_sandbox;
/// OpenShell Sandbox - process isolation and monitoring.
#[derive(Parser, Debug)]
#[command(name = "openshell-sandbox")]
#[command(version = openshell_core::VERSION)]
#[command(about = "Process sandbox and monitor", long_about = None)]
struct Args {
/// Command to execute in the sandbox.
Expand Down
2 changes: 1 addition & 1 deletion crates/openshell-server/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ fn render_connect_page(
.replace('<', "\\x3c")
.replace('>', "\\x3e");

let version = env!("CARGO_PKG_VERSION");
let version = openshell_core::VERSION;

format!(
r#"<!DOCTYPE html>
Expand Down
2 changes: 1 addition & 1 deletion crates/openshell-server/src/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl OpenShell for OpenShellService {
) -> Result<Response<HealthResponse>, Status> {
Ok(Response::new(HealthResponse {
status: ServiceStatus::Healthy.into(),
version: env!("CARGO_PKG_VERSION").to_string(),
version: openshell_core::VERSION.to_string(),
}))
}

Expand Down
2 changes: 1 addition & 1 deletion crates/openshell-server/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ async fn healthz() -> impl IntoResponse {
async fn readyz() -> impl IntoResponse {
let response = HealthResponse {
status: "healthy",
version: env!("CARGO_PKG_VERSION"),
version: openshell_core::VERSION,
};

(StatusCode::OK, Json(response))
Expand Down
1 change: 1 addition & 0 deletions crates/openshell-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use openshell_server::{run_server, tracing_bus::TracingLogBus};
/// `OpenShell` Server - gRPC and HTTP server with protocol multiplexing.
#[derive(Parser, Debug)]
#[command(name = "openshell-server")]
#[command(version = openshell_core::VERSION)]
#[command(about = "OpenShell gRPC/HTTP server", long_about = None)]
struct Args {
/// Port to bind the server to (all interfaces).
Expand Down
37 changes: 15 additions & 22 deletions crates/openshell-tui/src/ui/splash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ const ART_WIDTH: u16 = 40;
/// Total content lines: 6 (OPEN) + 6 (SHELL) + 1 (blank) + 1 (tagline) = 14.
const CONTENT_LINES: u16 = 14;

// Border (2) + top/bottom inner padding (2) + content + blank before footer (1) + footer (1).
const MODAL_HEIGHT: u16 = CONTENT_LINES + 6;
// Border (2) + top/bottom inner padding (2) + content + blank before footer (1) + footer (2).
const MODAL_HEIGHT: u16 = CONTENT_LINES + 7;

// Art width + left/right padding (3+3) + borders (2).
const MODAL_WIDTH: u16 = ART_WIDTH + 8;
Expand All @@ -73,13 +73,13 @@ pub fn draw(frame: &mut Frame<'_>, area: Rect, theme: &crate::theme::Theme) {
let inner = block.inner(popup);
frame.render_widget(block, popup);

// Split inner area: art content + spacer + footer.
// Split inner area: art content + spacer + footer (2 lines).
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(CONTENT_LINES), // OPEN + SHELL + blank + tagline
Constraint::Min(0), // spacer
Constraint::Length(1), // footer
Constraint::Length(2), // footer (version + prompt)
])
.split(inner);

Expand All @@ -102,27 +102,20 @@ pub fn draw(frame: &mut Frame<'_>, area: Rect, theme: &crate::theme::Theme) {

frame.render_widget(Paragraph::new(content_lines), chunks[0]);

// -- Footer: version + ALPHA badge (left) + prompt (right) --
let version = format!("v{}", env!("CARGO_PKG_VERSION"));
let spacer = " ";
// -- Footer: version + ALPHA badge on line 1, prompt on line 2 --
let version = format!("v{}", openshell_core::VERSION);
let alpha_badge = "ALPHA";
let prompt_text = "press any key";

// Pad between left group and prompt to fill the line.
let used = version.len() + spacer.len() + alpha_badge.len() + prompt_text.len() + 2; // +2 for ░ and space
let footer_width = chunks[2].width as usize;
let gap = footer_width.saturating_sub(used);

let footer = Line::from(vec![
Span::styled(version, t.accent),
Span::styled(spacer, t.muted),
Span::styled(alpha_badge, t.title_bar),
Span::styled(" ".repeat(gap), t.muted),
Span::styled(prompt_text, t.muted),
Span::styled(" ░", t.muted),

let footer = Paragraph::new(vec![
Line::from(vec![
Span::styled(version, t.accent),
Span::styled(" ", t.muted),
Span::styled(alpha_badge, t.title_bar),
]),
Line::from(Span::styled("press any key ░", t.muted)),
]);

frame.render_widget(Paragraph::new(footer), chunks[2]);
frame.render_widget(footer, chunks[2]);
}

fn centered_rect(width: u16, height: u16, area: Rect) -> Rect {
Expand Down
4 changes: 4 additions & 0 deletions deploy/docker/Dockerfile.cluster
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certifi
FROM --platform=$BUILDPLATFORM rust:1.88-slim AS supervisor-builder
ARG TARGETARCH
ARG BUILDARCH
ARG OPENSHELL_CARGO_VERSION
ARG CARGO_TARGET_CACHE_SCOPE=default
ARG SCCACHE_MEMCACHED_ENDPOINT

Expand Down Expand Up @@ -135,6 +136,9 @@ RUN --mount=type=cache,id=cargo-registry-supervisor-${TARGETARCH},sharing=locked
--mount=type=cache,id=cargo-target-supervisor-${TARGETARCH}-${CARGO_TARGET_CACHE_SCOPE},sharing=locked,target=/build/target \
--mount=type=cache,id=sccache-supervisor-${TARGETARCH},sharing=locked,target=/tmp/sccache \
. cross-build.sh && \
if [ -n "${OPENSHELL_CARGO_VERSION:-}" ]; then \
sed -i -E '/^\[workspace\.package\]/,/^\[/{s/^version[[:space:]]*=[[:space:]]*".*"/version = "'"${OPENSHELL_CARGO_VERSION}"'"/}' Cargo.toml; \
fi && \
cargo_cross_build --release -p openshell-sandbox && \
mkdir -p /build/out && \
cp "$(cross_output_dir release)/openshell-sandbox" /build/out/
Expand Down
12 changes: 12 additions & 0 deletions tasks/scripts/cluster-deploy-fast.sh
Original file line number Diff line number Diff line change
Expand Up @@ -321,11 +321,23 @@ if [[ "${build_supervisor}" == "1" ]]; then
SUPERVISOR_BUILD_DIR=$(mktemp -d)
trap 'rm -rf "${SUPERVISOR_BUILD_DIR}"' EXIT

# Compute cargo version from git tags for the supervisor binary.
SUPERVISOR_VERSION_ARGS=()
if [[ -n "${OPENSHELL_CARGO_VERSION:-}" ]]; then
SUPERVISOR_VERSION_ARGS=(--build-arg "OPENSHELL_CARGO_VERSION=${OPENSHELL_CARGO_VERSION}")
else
_cargo_version=$(uv run python tasks/scripts/release.py get-version --cargo 2>/dev/null || true)
if [[ -n "${_cargo_version}" ]]; then
SUPERVISOR_VERSION_ARGS=(--build-arg "OPENSHELL_CARGO_VERSION=${_cargo_version}")
fi
fi

docker buildx build \
--file deploy/docker/Dockerfile.cluster \
--target supervisor-builder \
--build-arg "BUILDARCH=$(docker version --format '{{.Server.Arch}}')" \
--build-arg "TARGETARCH=${CLUSTER_ARCH}" \
${SUPERVISOR_VERSION_ARGS[@]+"${SUPERVISOR_VERSION_ARGS[@]}"} \
--output "type=local,dest=${SUPERVISOR_BUILD_DIR}" \
--platform "linux/${CLUSTER_ARCH}" \
.
Expand Down
12 changes: 12 additions & 0 deletions tasks/scripts/docker-build-cluster.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,22 @@ elif [[ "${DOCKER_PLATFORM:-}" == *","* ]]; then
OUTPUT_FLAG="--push"
fi

# Compute cargo version from git tags (same scheme as docker-build-component.sh).
VERSION_ARGS=()
if [[ -n "${OPENSHELL_CARGO_VERSION:-}" ]]; then
VERSION_ARGS=(--build-arg "OPENSHELL_CARGO_VERSION=${OPENSHELL_CARGO_VERSION}")
else
CARGO_VERSION=$(uv run python tasks/scripts/release.py get-version --cargo 2>/dev/null || true)
if [[ -n "${CARGO_VERSION}" ]]; then
VERSION_ARGS=(--build-arg "OPENSHELL_CARGO_VERSION=${CARGO_VERSION}")
fi
fi

docker buildx build \
${BUILDER_ARGS[@]+"${BUILDER_ARGS[@]}"} \
${DOCKER_PLATFORM:+--platform ${DOCKER_PLATFORM}} \
${CACHE_ARGS[@]+"${CACHE_ARGS[@]}"} \
${VERSION_ARGS[@]+"${VERSION_ARGS[@]}"} \
-f deploy/docker/Dockerfile.cluster \
-t ${IMAGE_NAME}:${IMAGE_TAG} \
${K3S_VERSION:+--build-arg K3S_VERSION=${K3S_VERSION}} \
Expand Down
Loading