diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6e1e850..b4fa2a8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,72 +6,186 @@ on: tags: - 'v[0-9]+.[0-9]+.[0-9]+' +env: + APP_NAME: cargo-appraiser + jobs: - build: + build-linux: + runs-on: ubuntu-latest + strategy: + matrix: + target: [ + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + "armv7-unknown-linux-gnueabihf" + ] + + steps: + # 1. Checkout the repository + - uses: actions/checkout@v4 + + # 2. Install Rust Toolchain + - name: Install Rust Toolchain + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + toolchain: stable + + # 3. Install `cross` + - name: Install Cross + run: | + cargo install cross + + # 4. Cache Cargo dependencies (optional but recommended) + - name: Cache Cargo Registry + uses: actions/cache@v3 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-registry- + + - name: Cache Cargo Git + uses: actions/cache@v3 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-git- + + # 5. Cache Cross Docker Image (optional but recommended) + - name: Cache Cross Docker Image + uses: actions/cache@v3 + with: + path: ~/.cache/cross + key: ${{ runner.os }}-cross-${{ matrix.target }} + restore-keys: | + ${{ runner.os }}-cross- + + # 6. Build the project using `cross` + - name: Build with Cross + env: + RUSTFLAGS: "-Awarnings" + run: cross build --release --target ${{ matrix.target }} --bin ${{ env.APP_NAME }} --features vendored-openssl + + # 7. Upload the build artifact + - name: Upload Build Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.APP_NAME }}-${{ matrix.target }} + path: target/${{ matrix.target }}/release/${{ env.APP_NAME }} + if-no-files-found: error + + build-macos: strategy: fail-fast: false matrix: - target: - - x86_64-unknown-linux-musl - - x86_64-apple-darwin include: - - target: x86_64-unknown-linux-musl - os: ubuntu-latest - target: x86_64-apple-darwin - os: macos-latest - runs-on: ${{ matrix.os }} + runs-on: macos-14-large + - target: aarch64-apple-darwin + runs-on: macos-latest + + runs-on: ${{ matrix.runs-on }} + steps: - uses: actions/checkout@v4 - - name: Build Linux - if: matrix.os == 'ubuntu-latest' - run: | - docker run --rm -t \ - -v $HOME/.cargo/registry/:/root/.cargo/registry \ - -v "$(pwd)":/volume \ - clux/muslrust:stable \ - cargo build --release --bin kopium --target ${{ matrix.target }} - - name: Prepare macOS - if: matrix.os == 'macos-latest' - uses: actions-rs/toolchain@v1 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable with: - toolchain: nightly - target: ${{ matrix.target }} - override: true + targets: ${{ matrix.target }} + + - name: Install and configure OpenSSL + run: | + # Install OpenSSL@3 using Homebrew + brew install openssl@3 + # Set OpenSSL environment variables + echo "OPENSSL_DIR=$(brew --prefix openssl@3)" >> $GITHUB_ENV + echo "OPENSSL_INCLUDE_DIR=$(brew --prefix openssl@3)/include" >> $GITHUB_ENV + echo "OPENSSL_LIB_DIR=$(brew --prefix openssl@3)/lib" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=$(brew --prefix openssl@3)/lib/pkgconfig" >> $GITHUB_ENV + echo "$(brew --prefix openssl@3)/bin" >> $GITHUB_PATH + - name: Build macOS - if: matrix.os == 'macos-latest' uses: actions-rs/cargo@v1 + env: + OPENSSL_DIR: ${{ env.OPENSSL_DIR }} + OPENSSL_INCLUDE_DIR: ${{ env.OPENSSL_INCLUDE_DIR }} + OPENSSL_LIB_DIR: ${{ env.OPENSSL_LIB_DIR }} + PKG_CONFIG_PATH: ${{ env.PKG_CONFIG_PATH }} with: - toolchain: stable command: build - args: --release --bin kopium --target ${{ matrix.target }} - - name: Upload - uses: actions/upload-artifact@v3 + args: --release --bin ${{ env.APP_NAME }} --target ${{ matrix.target }} + + - name: Upload macOS artifact + uses: actions/upload-artifact@v4 with: - name: kopium-${{ matrix.os }}-amd64 - path: target/${{ matrix.target }}/release/kopium + name: ${{ env.APP_NAME }}-${{ matrix.target }} + path: target/${{ matrix.target }}/release/${{ env.APP_NAME }} + if-no-files-found: error + + build-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-pc-windows-msvc + + - name: Build Windows + run: cargo build --release --bin ${{ env.APP_NAME }} --target x86_64-pc-windows-msvc + + - name: Upload Windows artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ env.APP_NAME }}-x86_64-pc-windows-msvc + path: target/x86_64-pc-windows-msvc/release/${{ env.APP_NAME }}.exe if-no-files-found: error release: - needs: build + needs: [build-linux, build-macos, build-windows] runs-on: ubuntu-latest + env: + TARGETS: >- + x86_64-unknown-linux-gnu + aarch64-unknown-linux-gnu + x86_64-apple-darwin + aarch64-apple-darwin + x86_64-pc-windows-msvc + armv7-unknown-linux-gnueabihf + FILES: >- + cargo-appraiser-linux-amd64 + cargo-appraiser-linux-arm64 + cargo-appraiser-darwin-amd64 + cargo-appraiser-darwin-arm64 + cargo-appraiser-windows-amd64 + cargo-appraiser-linux-armhf steps: - - uses: actions/checkout@v4 - - name: Download - uses: actions/download-artifact@v3 - - name: Layout + - name: Download artifacts + uses: actions/download-artifact@v4 + + - name: Rename and move artifacts run: | - mv kopium-ubuntu-latest-amd64/kopium ./kopium-linux-amd64 - mv kopium-macos-latest-amd64/kopium ./kopium-darwin-amd64 - rm -rf kopium-ubuntu-latest-amd64 kopium-macos-latest-amd64 + mkdir -p ./artifacts + targets=(${{ env.TARGETS }}) + files=(${{ env.FILES }}) + for i in "${!targets[@]}"; do + artifact_name="${{ env.APP_NAME }}-${targets[$i]}" + output_name="${files[$i]}" + + if [[ "${targets[$i]}" == *"-windows-"* ]]; then + mv "$artifact_name/${{ env.APP_NAME }}.exe" "./artifacts/$output_name.exe" + else + mv "$artifact_name/${{ env.APP_NAME }}" "./artifacts/$output_name" + fi + done - name: Release - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v2 with: generate_release_notes: true - draft: true + draft: false fail_on_unmatched_files: true - files: | - kopium-darwin-amd64 - kopium-linux-amd64 \ No newline at end of file + files: ./artifacts/** diff --git a/Cargo.lock b/Cargo.lock index 437564d..ebbd7bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,6 +257,7 @@ dependencies = [ "lsp-async-stub", "my-cargo", "once_cell", + "openssl", "parking_lot", "ribboncurls", "semver", @@ -836,6 +837,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -2453,12 +2469,47 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-src" +version = "300.3.2+3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a211a18d945ef7e648cc6e0058f4c548ee46aab922ea203e0d30e966ea23647b" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.103" @@ -2467,6 +2518,7 @@ checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index bf1e736..836d78f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,9 @@ clap = { version = "4.5.18", features = ["derive"] } once_cell = "1.19.0" ribboncurls = "0.4.1" semver = "1.0.23" + +openssl = { version = '0.10', optional = true } + +[features] +default = [] +vendored-openssl = ["openssl/vendored"] diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..02b8589 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,38 @@ +# Cross.toml + +# Global [build] settings that apply to all targets unless overridden +[build] +# Use Zig as a cross-compiler (optional) +zig = "2.17" # Specify the Zig version you prefer + +# Optional: Disable building Rust's standard library if not needed +# build-std = false + +# Optional: Enable or disable Xargo +# xargo = true + +# Optional: Set a default target +# default-target = "aarch64-unknown-linux-musl" + +# Global environment variables and volumes +[build.env] +volumes = [] +passthrough = ["CARGO_*", "RUST_*", "OPENSSL_DIR"] + +# target.x86_64-unknown-linux-gnu +[target.x86_64-unknown-linux-gnu] + +[target.x86_64-unknown-linux-gnu.env] +volumes = [] + +# target.aarch64-unknown-linux-gnu +[target.aarch64-unknown-linux-gnu] + +[target.aarch64-unknown-linux-gnu.env] +volumes = [] + +# target.armv7-unknown-linux-gnueabihf +[target.armv7-unknown-linux-gnueabihf] + +[target.armv7-unknown-linux-gnueabihf.env] +volumes = [] diff --git a/src/config.rs b/src/config.rs index 10a9163..8a529f5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,8 @@ use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; use std::sync::RwLock; +use crate::decoration::DecorationFormat; + #[derive(Default, Debug, Serialize, Deserialize, Clone)] pub struct Config { #[serde(flatten)] @@ -15,67 +17,6 @@ pub struct RendererConfig { pub decoration_format: DecorationFormat, } -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct DecorationFormat { - #[serde(default = "default_latest")] - pub latest: String, - #[serde(default = "default_local")] - pub local: String, - #[serde(default = "default_not_installed")] - pub not_installed: String, - #[serde(default = "default_loading")] - pub loading: String, - #[serde(default = "default_mixed_upgradeable")] - pub mixed_upgradeable: String, - #[serde(default = "default_compatible_latest")] - pub compatible_latest: String, - #[serde(default = "default_noncompatible_latest")] - pub noncompatible_latest: String, -} - -impl Default for DecorationFormat { - fn default() -> Self { - Self { - latest: default_latest(), - compatible_latest: default_compatible_latest(), - local: default_local(), - noncompatible_latest: default_noncompatible_latest(), - not_installed: default_not_installed(), - loading: default_loading(), - mixed_upgradeable: default_mixed_upgradeable(), - } - } -} - -fn default_latest() -> String { - "✅ {{installed}}".to_string() -} - -fn default_mixed_upgradeable() -> String { - "🚀🔒 {{installed}} -> {{latest_matched}}, {{latest}}".to_string() -} - -fn default_compatible_latest() -> String { - "🚀 {{installed}} -> {{latest}}".to_string() -} - -fn default_noncompatible_latest() -> String { - "🔒 {{installed}}, {{latest}}".to_string() -} - -fn default_not_installed() -> String { - "Not installed".to_string() -} - -fn default_loading() -> String { - "Loading...".to_string() -} - -fn default_local() -> String { - "Local".to_string() -} - pub static GLOBAL_CONFIG: Lazy> = Lazy::new(|| RwLock::new(Config::default())); pub fn initialize_config(config: Config) { diff --git a/src/controller/appraiser.rs b/src/controller/appraiser.rs index 0436c2f..300c1fd 100644 --- a/src/controller/appraiser.rs +++ b/src/controller/appraiser.rs @@ -12,7 +12,7 @@ use crate::{ usecase::{diff_symbol_maps, Walker}, }; -use super::cargo::{get_latest_version, parse_cargo_output, CargoResolveOutput}; +use super::cargo::{parse_cargo_output, CargoResolveOutput}; #[derive(Debug, Clone)] pub struct Ctx { @@ -96,13 +96,6 @@ impl Appraiser { path = Some(msg.path.to_string()); rev = 0; dirty_nodes.clear(); - - eprintln!("clear hints"); - - render_tx - .send(DecorationEvent::Reset(path.as_ref().unwrap().to_string())) - .await - .unwrap(); } rev += 1; @@ -207,7 +200,7 @@ impl Appraiser { continue; } - //resolve cargo dependencies in child task + //resolve cargo dependencies in another task cargo_tx .send(Ctx { path: msg.path.to_string(), @@ -224,7 +217,7 @@ impl Appraiser { if output.ctx.rev != rev { continue; } - //populate deps usting cargo tree result + //populate deps for dep in &mut dependencies { let key = dep.toml_key(); if output.dependencies.is_empty() @@ -235,17 +228,12 @@ impl Appraiser { // Take resolved out of the output.dependencies hashmap let resolved = output.dependencies.remove(&key).unwrap(); dep.resolved = Some(resolved); - } - let summaries_map = get_latest_version(&output.ctx.path); - - //populate dep resolved and latest_summary - for dep in &mut dependencies { let package_name = dep.package_name(); - if !summaries_map.contains_key(package_name) { + if !output.summaries.contains_key(package_name) { continue; } - let summaries = summaries_map.get(package_name).unwrap(); + let summaries = output.summaries.get(package_name).unwrap(); dep.summaries = Some(summaries.clone()); //if not installed, we don't need to know the latest version @@ -262,7 +250,6 @@ impl Appraiser { if &installed == summary.version() { dep.matched_summary = Some(summary.clone()); } - let pp = installed.pre.is_empty(); match latest { Some(cur) if summary.version() > cur => { latest = Some(summary.version()); @@ -302,11 +289,12 @@ impl Appraiser { _ => {} } } - } - //send to render - for dep in &dependencies { - if dirty_nodes.contains_key(&dep.id) { + //send to render + if let Some(rev) = dirty_nodes.get(&dep.id) { + if *rev > output.ctx.rev { + continue; + } //send to render task render_tx .send(DecorationEvent::Dependency( @@ -317,8 +305,8 @@ impl Appraiser { )) .await .unwrap(); + dirty_nodes.remove(&dep.id); } - continue; } } _ => {} diff --git a/src/controller/cargo.rs b/src/controller/cargo.rs index 7df86fd..6adf4d2 100644 --- a/src/controller/cargo.rs +++ b/src/controller/cargo.rs @@ -28,6 +28,7 @@ pub struct CargoResolveOutput { pub ctx: Ctx, //the hashmap key is toml_id, which is: pub dependencies: HashMap, + pub summaries: HashMap>, } pub async fn parse_cargo_output(ctx: &Ctx) -> CargoResolveOutput { @@ -97,10 +98,11 @@ pub async fn parse_cargo_output(ctx: &Ctx) -> CargoResolveOutput { CargoResolveOutput { ctx: ctx.clone(), dependencies: res, + summaries: summaries_map(&ctx.path), } } -pub fn get_latest_version(path: &str) -> HashMap> { +fn summaries_map(path: &str) -> HashMap> { let path = Path::new(path); let gctx = cargo::util::context::GlobalContext::default().unwrap(); let workspace = cargo::core::Workspace::new(path, &gctx).unwrap(); diff --git a/src/decoration.rs b/src/decoration.rs index 0b770b9..fb37d29 100644 --- a/src/decoration.rs +++ b/src/decoration.rs @@ -1,4 +1,4 @@ -use clap::arg; +use serde::{Deserialize, Serialize}; use tokio::sync::mpsc::Sender; use tower_lsp::{ lsp_types::{InlayHint, Range}, @@ -59,3 +59,64 @@ pub enum DecorationEvent { DependencyLoading(String, String, Range), Dependency(String, String, Range, Dependency), } + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct DecorationFormat { + #[serde(default = "default_latest")] + pub latest: String, + #[serde(default = "default_local")] + pub local: String, + #[serde(default = "default_not_installed")] + pub not_installed: String, + #[serde(default = "default_loading")] + pub loading: String, + #[serde(default = "default_mixed_upgradeable")] + pub mixed_upgradeable: String, + #[serde(default = "default_compatible_latest")] + pub compatible_latest: String, + #[serde(default = "default_noncompatible_latest")] + pub noncompatible_latest: String, +} + +impl Default for DecorationFormat { + fn default() -> Self { + Self { + latest: default_latest(), + compatible_latest: default_compatible_latest(), + local: default_local(), + noncompatible_latest: default_noncompatible_latest(), + not_installed: default_not_installed(), + loading: default_loading(), + mixed_upgradeable: default_mixed_upgradeable(), + } + } +} + +fn default_latest() -> String { + "✅ {{installed}}".to_string() +} + +fn default_mixed_upgradeable() -> String { + "🚀🔒 {{installed}} -> {{latest_matched}}, {{latest}}".to_string() +} + +fn default_compatible_latest() -> String { + "🚀 {{installed}} -> {{latest}}".to_string() +} + +fn default_noncompatible_latest() -> String { + "🔒 {{installed}}, {{latest}}".to_string() +} + +fn default_not_installed() -> String { + "Not installed".to_string() +} + +fn default_loading() -> String { + "Loading...".to_string() +} + +fn default_local() -> String { + "Local".to_string() +} diff --git a/src/main.rs b/src/main.rs index 9ef96c5..b280b85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -158,10 +158,6 @@ impl LanguageServer for CargoAppraiser { .send(CargoDocumentEvent::Saved(CargoTomlPayload { path, text })) .await .unwrap(); - - // self.client - // .log_message(MessageType::INFO, "Cargo.toml saved. ") - // .await; }; }