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
33 changes: 32 additions & 1 deletion .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ jobs:
${{ runner.os }}-cargo-

- name: Build release binary
run: cargo build --release
# Build without network feature so init uses placeholder binaries (no releases exist yet)
run: cargo build --release --no-default-features --features parallel

- name: Copy binary to test location
shell: bash
Expand Down Expand Up @@ -232,6 +233,7 @@ jobs:
run: |
INIT_DIR=$(mktemp -d)
cd "$INIT_DIR"
git init
$GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --current-platform-only

# Verify files were created
Expand Down Expand Up @@ -271,6 +273,7 @@ jobs:
run: |
INIT_DIR=$(mktemp -d)
cd "$INIT_DIR"
git init
$GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --platforms linux-amd64,macos-arm64,windows-amd64

# Verify config has the right platforms
Expand Down Expand Up @@ -309,6 +312,7 @@ jobs:
run: |
INIT_DIR=$(mktemp -d)
cd "$INIT_DIR"
git init

# First init with one platform
$GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --platforms linux-amd64
Expand Down Expand Up @@ -345,6 +349,7 @@ jobs:
run: |
INIT_DIR=$(mktemp -d)
cd "$INIT_DIR"
git init
$GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --platforms linux-amd64,windows-amd64

OUTPUT=$($GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --show-platforms 2>&1)
Expand All @@ -365,6 +370,7 @@ jobs:
run: |
INIT_DIR=$(mktemp -d)
cd "$INIT_DIR"
git init
$GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --platforms linux-amd64

# Try to remove the only platform - should fail
Expand All @@ -376,6 +382,31 @@ jobs:
echo "✅ init correctly refuses to remove last platform"
rm -rf "$INIT_DIR"

- name: "Test: init --force skips git repo check"
shell: bash
run: |
INIT_DIR=$(mktemp -d)
cd "$INIT_DIR"
# Do NOT run git init - this is intentionally not a git repo

# Without --force, should fail
if $GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --current-platform-only 2>&1; then
echo "ERROR: init should fail without --force in non-git directory"
exit 1
fi

# With --force, should succeed
$GITHUB_WORKSPACE/tests/${{ matrix.binary }} init --current-platform-only --force

# Verify files were created
if [ ! -f ".rnr/config.yaml" ]; then
echo "ERROR: .rnr/config.yaml not created with --force"
exit 1
fi

echo "✅ init --force correctly skips git repo check"
rm -rf "$INIT_DIR"

# ==================== Error Cases ====================

- name: "Test: nonexistent task (should fail)"
Expand Down
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ dialoguer = "0.11"
console = "0.15"

# HTTP client for init/upgrade
reqwest = { version = "0.12", features = ["blocking"], default-features = false, optional = true }
reqwest = { version = "0.12", features = ["blocking", "rustls-tls", "json"], default-features = false, optional = true }

# JSON parsing for GitHub API
serde_json = "1"

# Async runtime for parallel execution
tokio = { version = "1", features = ["rt-multi-thread", "process", "sync"], optional = true }
Expand Down
4 changes: 4 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,8 @@ pub struct InitArgs {
/// Show currently configured platforms
#[arg(long)]
pub show_platforms: bool,

/// Skip git repository root check
#[arg(long)]
pub force: bool,
}
66 changes: 44 additions & 22 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ pub fn run(args: &InitArgs) -> Result<()> {
return Ok(());
}

// Error if not at git repo root (unless --force is used)
if !args.force && !is_git_repo_root()? {
bail!(
"This directory does not appear to be a git repository root.\n\
rnr is typically initialized at the root of a git repository.\n\
Use --force to initialize anyway, or run from the directory containing your .git folder."
);
}

// Determine platforms to install
let platforms = select_platforms(args)?;

Expand All @@ -49,6 +58,13 @@ pub fn run(args: &InitArgs) -> Result<()> {
initialize(&platforms)
}

/// Check if the current directory is a git repository root
fn is_git_repo_root() -> Result<bool> {
let current_dir = std::env::current_dir().context("Failed to get current directory")?;
let git_dir = current_dir.join(".git");
Ok(git_dir.exists())
}

/// Select platforms based on args or interactively
fn select_platforms(args: &InitArgs) -> Result<Vec<Platform>> {
// --all-platforms
Expand Down Expand Up @@ -191,28 +207,42 @@ fn download_binaries(platforms: &[Platform], bin_directory: &Path) -> Result<()>
Ok(())
}

/// GitHub repository for releases
const GITHUB_REPO: &str = "CodingWithCalvin/rnr.cli";

/// Download a single binary from GitHub releases
#[cfg(feature = "network")]
fn download_binary(platform: Platform, dest: &Path) -> Result<()> {
use std::io::Write;

// TODO: Replace with actual release URL once we have releases
// For now, use GitHub releases URL pattern
let url = format!(
"https://github.com/CodingWithCalvin/rnr.cli/releases/latest/download/{}",
"https://github.com/{}/releases/latest/download/{}",
GITHUB_REPO,
platform.binary_name()
);

// For now, create a placeholder since we don't have releases yet
// In production, this would download from the URL
let placeholder = format!(
"#!/bin/sh\necho 'Placeholder binary for {}. Replace with actual binary from releases.'\n",
platform.id()
);
let client = reqwest::blocking::Client::builder()
.user_agent("rnr-cli")
.build()
.context("Failed to create HTTP client")?;

let mut file =
fs::File::create(dest).with_context(|| format!("Failed to create {}", dest.display()))?;
file.write_all(placeholder.as_bytes())?;
let response = client
.get(&url)
.send()
.with_context(|| format!("Failed to download {}", platform.binary_name()))?;

if !response.status().is_success() {
anyhow::bail!(
"Failed to download {}: HTTP {}",
platform.binary_name(),
response.status().as_u16()
);
}

let bytes = response
.bytes()
.with_context(|| format!("Failed to read response for {}", platform.binary_name()))?;

// Write to file
fs::write(dest, &bytes).with_context(|| format!("Failed to write {}", dest.display()))?;

// Make executable on Unix
#[cfg(unix)]
Expand All @@ -223,14 +253,6 @@ fn download_binary(platform: Platform, dest: &Path) -> Result<()> {
fs::set_permissions(dest, perms)?;
}

// TODO: Actual download implementation:
// let response = reqwest::blocking::get(&url)
// .with_context(|| format!("Failed to download {}", url))?;
// let bytes = response.bytes()?;
// fs::write(dest, bytes)?;

let _ = url; // Suppress unused warning for now

Ok(())
}

Expand Down
Loading