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
64 changes: 14 additions & 50 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,87 +2,51 @@ name: CI

on:
push:
branches: [main]
branches: [main, develop]
pull_request:
branches: [main]
branches: [develop]

env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1

jobs:
test:
name: Test (${{ matrix.rust }})
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.rust == 'nightly' }}
ci:
name: CI (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
rust: [stable, nightly]
os: [ubuntu-latest, macos-latest]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
- name: Install Rust toolchain (stable)
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.rust }}
components: clippy, rustfmt

- name: Cache cargo registry & build
- name: Cache cargo registry
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ matrix.rust }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-${{ matrix.rust }}-
${{ runner.os }}-cargo-

- name: Check formatting
if: matrix.rust == 'stable'
run: cargo fmt --all -- --check

- name: Run clippy
if: matrix.rust == 'stable'
run: cargo clippy --all-targets --all-features -- -D warnings
run: cargo clippy --all-targets -- -D warnings

- name: Run tests
run: cargo test --all-features --verbose
run: cargo test

- name: Build release
run: cargo build --release --verbose

lint:
name: Lint & Format (stable)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain (stable)
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt

- name: Check formatting
run: cargo fmt --all -- --check

- name: Run clippy
run: cargo clippy --all-targets --all-features -- -D warnings

msrv:
name: Check MSRV
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain (MSRV)
uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.75.0"

- name: Build
run: cargo build
run: cargo build --release
84 changes: 70 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,10 @@ jobs:
runner: ubuntu-latest
artifact_name: cora
asset_name: cora-linux-aarch64
- target: x86_64-unknown-linux-gnu
runner: ubuntu-latest
artifact_name: cora
asset_name: cora-linux-x86_64
- target: aarch64-apple-darwin
runner: macos-latest
artifact_name: cora
asset_name: cora-macos-aarch64
- target: x86_64-apple-darwin
runner: macos-latest
artifact_name: cora
asset_name: cora-macos-x86_64
- target: x86_64-pc-windows-msvc
runner: windows-latest
artifact_name: cora.exe
Expand Down Expand Up @@ -79,6 +71,9 @@ jobs:
needs: build-release
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Download all artifacts
uses: actions/download-artifact@v4
with:
Expand All @@ -92,16 +87,77 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

publish-crates-io:
name: Publish to crates.io
update-homebrew-formula:
name: Update Homebrew Formula
needs: build-release
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Extract version and download info
id: info
run: |
VERSION="${GITHUB_REF_NAME}"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

# Get the macOS ARM64 asset download URL (will be available after release)
# We use the GitHub API to get the release asset URL
REPO="${GITHUB_REPOSITORY}"
TAG="${GITHUB_REF_NAME}"

- name: Publish to crates.io
run: cargo publish --token ${{ secrets.CRATES_TOKEN }}
# Wait briefly for release to be fully published
sleep 5

# Get the download URL for the macOS ARM64 tarball
ASSET_URL=$(curl -s \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${REPO}/releases/tags/${TAG}" \
| jq -r '.assets[] | select(.name | contains("macos-aarch64")) | .url')

# Get SHA256 of the macOS ARM64 tarball
MACOS_SHA256=$(curl -sL \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/octet-stream" \
"${ASSET_URL}" | sha256sum | awk '{print $1}')

echo "macos_sha256=${MACOS_SHA256}" >> "$GITHUB_OUTPUT"

- name: Generate Homebrew formula
run: |
cat > cora.rb << FORMULA
class Cora < Formula
desc "CLI-first AI code review — BYOK, diff/scan/branch, pre-commit hooks"
homepage "https://github.com/ajianaz/cora-cli"
version "${{ steps.info.outputs.version }}"
license "MIT"

on_macos do
on_arm do
url "https://github.com/ajianaz/cora-cli/releases/download/${{ steps.info.outputs.version }}/cora-macos-aarch64.tar.gz"
sha256 "${{ steps.info.outputs.macos_sha256 }}"
end
end

def install
bin.install "cora"
end

test do
shell_output("#{bin}/cora --version")
end
end
FORMULA

- name: Push formula to homebrew-cora repo
env:
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
run: |
git clone https://x-access-token:${{ secrets.HOMEBREW_TAP_TOKEN }}@github.com/ajianaz/homebrew-cora.git /tmp/homebrew-cora
cp cora.rb /tmp/homebrew-cora/Formula/cora.rb
cd /tmp/homebrew-cora
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/cora.rb
git commit -m "Update cora to ${{ steps.info.outputs.version }}" || echo "No changes to commit"
git push
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rust-version = "1.85"
[dependencies]
# CLI
clap = { version = "4", features = ["derive", "env"] }
clap_complete = "4"

# Async runtime
tokio = { version = "1", features = ["full"] }
Expand Down Expand Up @@ -52,6 +53,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Misc
chrono = "0.4"
regex = "1"
futures-util = "0.3"

[dev-dependencies]
assert_cmd = "2"
Expand Down
25 changes: 6 additions & 19 deletions src/commands/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ pub fn execute_auth_login() -> Result<()> {

loader::save_api_key(&key)?;
println!(
"{} API key saved to {}",
"✅".green().bold(),
"~/.cora/config.toml"
"{} API key saved to ~/.cora/config.toml",
"✅".green().bold()
);
println!(
"{}",
Expand All @@ -56,16 +55,10 @@ pub fn execute_auth_status() -> Result<()> {
let status = loader::auth_status()?;

if status.has_key {
println!(
"{} API key is configured.",
"✅".green().bold()
);
println!("{} API key is configured.", "✅".green().bold());
println!(" Source: {}", status.source);
} else {
println!(
"{} No API key configured.",
"❌".red().bold()
);
println!("{} No API key configured.", "❌".red().bold());
println!(" Set it via:");
println!(" • CORA_API_KEY environment variable");
println!(" • `cora auth login` command");
Expand All @@ -79,18 +72,12 @@ pub fn execute_auth_status() -> Result<()> {
pub fn execute_auth_remove() -> Result<()> {
let status = loader::auth_status()?;
if !status.has_key && std::env::var("CORA_API_KEY").is_err() {
println!(
"{}",
"No API key found to remove.".yellow()
);
println!("{}", "No API key found to remove.".yellow());
return Ok(());
}

loader::remove_api_key()?;
println!(
"{} API key removed from local config.",
"✅".green().bold()
);
println!("{} API key removed from local config.", "✅".green().bold());
println!(
"{}",
" If you set CORA_API_KEY in your shell, remove it there too.".dimmed()
Expand Down
16 changes: 16 additions & 0 deletions src/commands/completion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use anyhow::Result;
use clap::CommandFactory;
use clap_complete::aot::{Shell, generate};

use crate::Cli;

pub fn execute_completion(shell: &str) -> Result<i32> {
let shell: Shell = shell.parse().map_err(|_| {
anyhow::anyhow!("Invalid shell: {shell}. Supported shells: bash, zsh, fish, powershell")
})?;

let mut cmd = Cli::command();
generate(shell, &mut cmd, "cora", &mut std::io::stdout());

Ok(0)
}
10 changes: 2 additions & 8 deletions src/commands/hook_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ pub fn execute_hook_install() -> Result<()> {
"{}",
" The hook will run `cora review --staged --format compact` before each commit.".dimmed()
);
println!(
"{}",
" Use `cora hook uninstall` to remove.".dimmed()
);
println!("{}", " Use `cora hook uninstall` to remove.".dimmed());

Ok(())
}
Expand All @@ -28,10 +25,7 @@ pub fn execute_hook_install() -> Result<()> {
pub fn execute_hook_uninstall() -> Result<()> {
hook::uninstall_hook()?;

println!(
"{} Pre-commit hook removed.",
"✅".green().bold()
);
println!("{} Pre-commit hook removed.", "✅".green().bold());

Ok(())
}
4 changes: 3 additions & 1 deletion src/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pub mod auth;
pub mod completion;
pub mod hook_cmd;
pub mod init;
pub mod providers;
pub mod review;
pub mod scan;

pub mod upload;
Loading
Loading