From 4aa8779f030abbfd4fa5a8797bb30ed11af41548 Mon Sep 17 00:00:00 2001 From: Maximilian Noller Date: Fri, 16 Jan 2026 18:16:04 +0100 Subject: [PATCH] ci: add release automation and easy installation --- .github/workflows/build-release.yml | 59 +++++++++++ .github/workflows/ci.yml | 44 +++++++++ .github/workflows/commitlint.yml | 26 +++++ .github/workflows/release-please.yml | 18 ++++ .release-please-manifest.json | 3 + Cargo.toml | 4 + README.md | 29 ++++-- commitlint.config.js | 24 +++++ install.sh | 141 +++++++++++++++++++++++++++ release-please-config.json | 10 ++ 10 files changed, 352 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/build-release.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/commitlint.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .release-please-manifest.json create mode 100644 commitlint.config.js create mode 100644 install.sh create mode 100644 release-please-config.json diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml new file mode 100644 index 0000000..62905aa --- /dev/null +++ b/.github/workflows/build-release.yml @@ -0,0 +1,59 @@ +name: Build Release + +on: + release: + types: [created] + +permissions: + contents: write + +jobs: + build: + strategy: + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + name: nanocode-linux-x86_64 + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + name: nanocode-linux-aarch64 + - target: aarch64-apple-darwin + os: macos-latest + name: nanocode-darwin-aarch64 + + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Install dependencies (Linux) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y libssl-dev pkg-config + if [ "${{ matrix.target }}" = "aarch64-unknown-linux-gnu" ]; then + sudo apt-get install -y gcc-aarch64-linux-gnu + fi + + - name: Build + run: | + if [ "${{ matrix.target }}" = "aarch64-unknown-linux-gnu" ]; then + export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc + fi + cargo build --release --target ${{ matrix.target }} + + - name: Package + run: | + cd target/${{ matrix.target }}/release + tar -czvf ../../../${{ matrix.name }}.tar.gz nanocode + cd ../../.. + + - name: Upload Release Asset + uses: softprops/action-gh-release@v1 + with: + files: ${{ matrix.name }}.tar.gz diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e65cb3b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Cache cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Check formatting + run: cargo fmt -- --check + + - name: Clippy + run: cargo clippy -- -D warnings + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 0000000..70b4ea4 --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,26 @@ +name: Commitlint + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +jobs: + commitlint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install commitlint + run: npm install --save-dev @commitlint/cli @commitlint/config-conventional + + - name: Validate PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: echo "$PR_TITLE" | npx commitlint diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..1147603 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,18 @@ +name: Release Please + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + steps: + - uses: googleapis/release-please-action@v4 + with: + release-type: rust diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 0000000..466df71 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.1.0" +} diff --git a/Cargo.toml b/Cargo.toml index 1b8a03f..680e5f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,9 @@ name = "nanocode" version = "0.1.0" edition = "2021" +description = "A minimal AI-powered coding CLI assistant" +license = "MIT" +repository = "https://github.com/HybridAIOne/nano" [dependencies] colored = "2.1.0" @@ -10,3 +13,4 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" glob = "0.3" regex = "1.10" +openssl = { version = "0.10", features = ["vendored"] } diff --git a/README.md b/README.md index 83b4345..f7b1ea4 100644 --- a/README.md +++ b/README.md @@ -16,22 +16,39 @@ A minimal AI-powered coding CLI assistant written in Rust. Nanocode lets you int - **Color-Coded Output** - Easy-to-read terminal interface - **Safety Guards** - Limits tool iterations to prevent runaway executions +## Supported Platforms + +- Linux x86_64 +- Linux aarch64 +- macOS aarch64 (Apple Silicon) + +Intel Mac users: the installer will build from source automatically. + ## Prerequisites -- Rust and Cargo (install from [rustup.rs](https://rustup.rs)) - An OpenRouter API key or a HybridAI API key (HybridAI takes precedence if both are set) ## Installation -1. Clone this repository: +### Quick Install (Linux/macOS) + +```bash +curl -fsSL https://raw.githubusercontent.com/HybridAIOne/nano/main/install.sh | bash +``` + +This downloads a pre-built binary or builds from source if needed. Installs to `~/.local/bin/` by default. + +### Using Cargo + ```bash -git clone -cd nanocode +cargo install --git https://github.com/HybridAIOne/nano ``` -2. Build the project: +### From Source + ```bash -cargo build --release +git clone https://github.com/HybridAIOne/nano && cd nano +cargo install --path . ``` ## Configuration diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..3580460 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,24 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + 'type-enum': [ + 2, + 'always', + [ + 'feat', + 'fix', + 'docs', + 'style', + 'refactor', + 'perf', + 'test', + 'build', + 'ci', + 'chore', + 'revert', + ], + ], + 'subject-case': [2, 'always', 'lower-case'], + 'header-max-length': [2, 'always', 100], + }, +}; diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..580bac0 --- /dev/null +++ b/install.sh @@ -0,0 +1,141 @@ +#!/bin/bash +set -e + +# Nanocode Installer +# Usage: curl -fsSL https://raw.githubusercontent.com/HybridAIOne/nano/main/install.sh | bash + +REPO="HybridAIOne/nano" +BINARY_NAME="nanocode" +INSTALL_DIR="${NANOCODE_INSTALL_DIR:-$HOME/.local/bin}" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +info() { echo -e "${GREEN}[INFO]${NC} $1"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } + +# Detect OS and architecture +detect_platform() { + local os arch + + case "$(uname -s)" in + Linux*) os="linux" ;; + Darwin*) os="darwin" ;; + *) error "Unsupported OS: $(uname -s)" ;; + esac + + case "$(uname -m)" in + x86_64|amd64) arch="x86_64" ;; + aarch64|arm64) arch="aarch64" ;; + *) error "Unsupported architecture: $(uname -m)" ;; + esac + + echo "${os}-${arch}" +} + +# Get latest release version +get_latest_version() { + curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" | \ + grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/' +} + +# Download and install binary +install_binary() { + local version="$1" + local platform="$2" + local asset_name="${BINARY_NAME}-${platform}.tar.gz" + local download_url="https://github.com/${REPO}/releases/download/${version}/${asset_name}" + + info "Downloading ${BINARY_NAME} ${version} for ${platform}..." + + local tmp_dir=$(mktemp -d) + trap "rm -rf $tmp_dir" EXIT + + if ! curl -fsSL "$download_url" -o "$tmp_dir/${BINARY_NAME}.tar.gz" 2>/dev/null; then + return 1 + fi + + tar -xzf "$tmp_dir/${BINARY_NAME}.tar.gz" -C "$tmp_dir" + + mkdir -p "$INSTALL_DIR" + mv "$tmp_dir/${BINARY_NAME}" "$INSTALL_DIR/${BINARY_NAME}" + chmod +x "$INSTALL_DIR/${BINARY_NAME}" + + return 0 +} + +# Build from source +build_from_source() { + info "Building from source..." + + if ! command -v cargo &>/dev/null; then + error "Rust/Cargo not found. Install from https://rustup.rs" + fi + + local tmp_dir=$(mktemp -d) + trap "rm -rf $tmp_dir" EXIT + + git clone --depth 1 "https://github.com/${REPO}.git" "$tmp_dir/${BINARY_NAME}" + cd "$tmp_dir/${BINARY_NAME}" + + cargo build --release + + mkdir -p "$INSTALL_DIR" + mv target/release/${BINARY_NAME} "$INSTALL_DIR/${BINARY_NAME}" + chmod +x "$INSTALL_DIR/${BINARY_NAME}" +} + +# Check if install dir is in PATH +check_path() { + if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then + warn "Add $INSTALL_DIR to your PATH:" + echo "" + echo " export PATH=\"\$PATH:$INSTALL_DIR\"" + echo "" + echo "Add this line to your ~/.bashrc, ~/.zshrc, or shell config." + fi +} + +main() { + info "Installing ${BINARY_NAME}..." + + local platform=$(detect_platform) + info "Detected platform: $platform" + + local version=$(get_latest_version) + + if [ -n "$version" ]; then + info "Latest version: $version" + + if install_binary "$version" "$platform"; then + info "Successfully installed ${BINARY_NAME} to $INSTALL_DIR/${BINARY_NAME}" + else + warn "Pre-built binary not available for $platform" + build_from_source + info "Successfully built and installed ${BINARY_NAME} to $INSTALL_DIR/${BINARY_NAME}" + fi + else + warn "Could not determine latest version, building from source" + build_from_source + info "Successfully built and installed ${BINARY_NAME} to $INSTALL_DIR/${BINARY_NAME}" + fi + + check_path + + echo "" + info "Installation complete!" + echo "" + echo "Don't forget to set your API key:" + echo " export OPENROUTER_API_KEY=\"your-api-key\"" + echo " # or" + echo " export HYBRIDAI_API_KEY=\"your-api-key\"" + echo "" + echo "Then run: ${BINARY_NAME}" + echo "" +} + +main "$@" diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 0000000..ff8a9d7 --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "rust", + "bump-minor-pre-major": false, + "bump-patch-for-minor-pre-major": true + } + } +}