├── .gitignore ├── .vscode └── settings.json ├── crates ├── core │ ├── src │ │ ├── lib.rs │ │ └── config.rs │ └── Cargo.toml ├── entity │ ├── src │ │ ├── lib.rs │ │ ├── file.rs │ │ └── modification.rs │ └── Cargo.toml ├── repo │ ├── src │ │ ├── lib.rs │ │ ├── file.rs │ │ └── modification.rs │ └── Cargo.toml ├── nix │ ├── src │ │ └── search │ │ │ ├── mod.rs │ │ │ ├── client.rs │ │ │ ├── query.rs │ │ │ ├── esclient.rs │ │ │ ├── types.rs │ │ │ └── matchers.rs │ ├── Cargo.toml │ └── tests │ │ ├── home.nix │ │ ├── home-with-vim.nix │ │ └── home-with-vim-git.nix ├── tui │ ├── src │ │ ├── lib.rs │ │ └── history.rs │ └── Cargo.toml ├── cli │ ├── src │ │ ├── types.rs │ │ ├── cmd │ │ │ ├── diff.rs │ │ │ ├── search.rs │ │ │ ├── history.rs │ │ │ ├── mod.rs │ │ │ ├── add.rs │ │ │ └── init.rs │ │ └── macros.rs │ └── Cargo.toml ├── migration │ ├── src │ │ ├── main.rs │ │ ├── lib.rs │ │ └── m20220101_000001_create_table.rs │ ├── README.md │ └── Cargo.toml ├── types │ ├── src │ │ ├── lib.rs │ │ ├── home_manager.rs │ │ ├── dnf.rs │ │ ├── yum.rs │ │ ├── pacman.rs │ │ ├── slackpkg.rs │ │ ├── apk.rs │ │ ├── fleek.rs │ │ ├── zypper.rs │ │ ├── emerge.rs │ │ ├── inventory.rs │ │ ├── nix.rs │ │ ├── git.rs │ │ ├── curl.rs │ │ ├── apt.rs │ │ └── configuration.rs │ └── Cargo.toml ├── macros │ └── Cargo.toml ├── installers │ ├── src │ │ ├── lib.rs │ │ ├── dnf.rs │ │ ├── yum.rs │ │ ├── pacman.rs │ │ ├── slackpkg.rs │ │ ├── apk.rs │ │ ├── brew.rs │ │ ├── zypper.rs │ │ ├── emerge.rs │ │ ├── nix.rs │ │ ├── fleek.rs │ │ └── curl.rs │ └── Cargo.toml └── ssh │ ├── Cargo.toml │ └── src │ └── lib.rs ├── .fluentci ├── .vscode │ └── settings.json ├── dagger.json ├── src │ ├── dagger │ │ ├── runner.ts │ │ ├── index.ts │ │ ├── queries.ts │ │ ├── list_jobs.ts │ │ ├── pipeline.ts │ │ ├── schema.ts │ │ └── jobs.ts │ ├── gitlab │ │ ├── init.ts │ │ ├── config_test.ts │ │ ├── config.ts │ │ └── README.md │ ├── aws │ │ ├── init.ts │ │ ├── config_test.ts │ │ ├── config.ts │ │ └── README.md │ ├── azure │ │ ├── init.ts │ │ ├── config_test.ts │ │ ├── config.ts │ │ └── README.md │ ├── circleci │ │ ├── init.ts │ │ ├── config_test.ts │ │ ├── config.ts │ │ └── README.md │ └── github │ │ ├── init.ts │ │ ├── config_test.ts │ │ ├── config.ts │ │ └── README.md ├── ci.ts ├── mod.ts ├── schema.graphql ├── .github │ └── workflows │ │ ├── zenith.yml │ │ ├── example.yml │ │ └── ci.yml ├── deno.json ├── fixtures │ ├── buildspec.yml │ ├── workflow.yml │ ├── azure-pipelines.yml │ ├── .gitlab-ci.yml │ └── config.yml ├── LICENSE ├── .devcontainer │ └── devcontainer.json ├── deps.ts ├── README.md ├── import_map.json ├── CONTRIBUTING.md └── gen │ └── nexus.ts ├── preview.png ├── tea.yaml ├── Cargo.toml ├── .github ├── FUNDING.yml └── workflows │ ├── flakehub-publish-tagged.yml │ ├── release.yml │ ├── tests.yml.disable │ └── release-for-mac.yml.disable ├── LICENSE ├── .devcontainer └── devcontainer.json ├── CONTRIBUTING.md ├── install.sh └── flake.nix /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | result* 3 | 4 | Crosfile* 5 | Inventory* -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /crates/core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod graph; 3 | -------------------------------------------------------------------------------- /crates/entity/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod file; 2 | pub mod modification; 3 | -------------------------------------------------------------------------------- /crates/repo/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod file; 2 | pub mod modification; 3 | -------------------------------------------------------------------------------- /.fluentci/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true 3 | } 4 | -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tsirysndr/crosup/HEAD/preview.png -------------------------------------------------------------------------------- /.fluentci/dagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": "", 3 | "name": "rust", 4 | "sdkRuntime": "tsiry/dagger-sdk-deno" 5 | } -------------------------------------------------------------------------------- /.fluentci/src/dagger/runner.ts: -------------------------------------------------------------------------------- 1 | import pipeline from "./pipeline.ts"; 2 | 3 | await pipeline(".", Deno.args); 4 | -------------------------------------------------------------------------------- /.fluentci/src/gitlab/init.ts: -------------------------------------------------------------------------------- 1 | import { generateYaml } from "./config.ts"; 2 | 3 | generateYaml().write(); 4 | -------------------------------------------------------------------------------- /.fluentci/src/aws/init.ts: -------------------------------------------------------------------------------- 1 | import { generateYaml } from "./config.ts"; 2 | 3 | generateYaml().save("buildspec.yml"); 4 | -------------------------------------------------------------------------------- /.fluentci/src/azure/init.ts: -------------------------------------------------------------------------------- 1 | import { generateYaml } from "./config.ts"; 2 | 3 | generateYaml().save("azure-pipeline.yml"); 4 | -------------------------------------------------------------------------------- /.fluentci/src/circleci/init.ts: -------------------------------------------------------------------------------- 1 | import { generateYaml } from "./config.ts"; 2 | 3 | generateYaml().save(".circleci/config.yml"); 4 | -------------------------------------------------------------------------------- /crates/nix/src/search/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod esclient; 3 | pub mod matchers; 4 | pub mod query; 5 | pub mod types; 6 | -------------------------------------------------------------------------------- /.fluentci/src/github/init.ts: -------------------------------------------------------------------------------- 1 | import { generateYaml } from "./config.ts"; 2 | 3 | generateYaml().save(".github/workflows/tests.yml"); 4 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0xa6d02B527eCA842E3Bed7995cA39834265fcE845' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /.fluentci/ci.ts: -------------------------------------------------------------------------------- 1 | import { 2 | build, 3 | test, 4 | } from "https://pkg.fluentci.io/rust_pipeline@v0.6.1/mod.ts"; 5 | 6 | await test(); 7 | await build(); 8 | -------------------------------------------------------------------------------- /.fluentci/src/dagger/index.ts: -------------------------------------------------------------------------------- 1 | import pipeline from "./pipeline.ts"; 2 | import { build, test } from "./jobs.ts"; 3 | 4 | export { pipeline, build, test }; 5 | -------------------------------------------------------------------------------- /.fluentci/mod.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/dagger/index.ts"; 2 | export * as queries from "./src/dagger/queries.ts"; 3 | export { schema } from "./src/dagger/schema.ts"; 4 | -------------------------------------------------------------------------------- /.fluentci/schema.graphql: -------------------------------------------------------------------------------- 1 | ### This file was generated by Nexus Schema 2 | ### Do not make changes to this file directly 3 | 4 | 5 | type Query { 6 | build(src: String!): String 7 | test(src: String!): String 8 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | default-members = ["crates/cli"] 3 | members = ["crates/*"] 4 | resolver = "2" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | -------------------------------------------------------------------------------- /crates/nix/src/search/client.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | 3 | use super::{query::Query, types::Hit}; 4 | 5 | #[async_trait::async_trait] 6 | pub trait NixPackagesClient { 7 | async fn search(&self, query: Query) -> Result, Error>; 8 | } 9 | -------------------------------------------------------------------------------- /crates/tui/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub mod history; 4 | 5 | pub struct App { 6 | pub items: Vec, 7 | pub content: HashMap, 8 | pub selected_index: usize, 9 | pub title: String, 10 | } 11 | -------------------------------------------------------------------------------- /.fluentci/src/dagger/queries.ts: -------------------------------------------------------------------------------- 1 | import { gql } from "../../deps.ts"; 2 | 3 | export const test = gql` 4 | query test($src: String!) { 5 | test(src: $src) 6 | } 7 | `; 8 | 9 | export const build = gql` 10 | query build($src: String!) { 11 | build(src: $src) 12 | } 13 | `; 14 | -------------------------------------------------------------------------------- /.fluentci/src/circleci/config_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; 2 | import { generateYaml } from "./config.ts"; 3 | 4 | Deno.test(function generateCircleCITest() { 5 | const circleci = generateYaml(); 6 | const actual = circleci.toString(); 7 | const expected = Deno.readTextFileSync("./fixtures/config.yml"); 8 | assertEquals(actual, expected); 9 | }); 10 | -------------------------------------------------------------------------------- /.fluentci/src/gitlab/config_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; 2 | import { generateYaml } from "./config.ts"; 3 | 4 | Deno.test(function generateGitlabCITest() { 5 | const gitlabci = generateYaml(); 6 | const actual = gitlabci.toString(); 7 | const expected = Deno.readTextFileSync("./fixtures/.gitlab-ci.yml"); 8 | assertEquals(actual, expected); 9 | }); 10 | -------------------------------------------------------------------------------- /.fluentci/src/aws/config_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; 2 | import { generateYaml } from "./config.ts"; 3 | 4 | Deno.test(function generateAWSCodePipelineTest() { 5 | const buildspec = generateYaml(); 6 | const actual = buildspec.toString(); 7 | const expected = Deno.readTextFileSync("./fixtures/buildspec.yml"); 8 | assertEquals(actual, expected); 9 | }); 10 | -------------------------------------------------------------------------------- /.fluentci/src/github/config_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; 2 | import { generateYaml } from "./config.ts"; 3 | 4 | Deno.test(function generateGithubActionsWorkflowTest() { 5 | const workflow = generateYaml(); 6 | const actual = workflow.toString(); 7 | const expected = Deno.readTextFileSync("./fixtures/workflow.yml"); 8 | assertEquals(actual, expected); 9 | }); 10 | -------------------------------------------------------------------------------- /.fluentci/src/azure/config_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; 2 | import { generateYaml } from "./config.ts"; 3 | 4 | Deno.test(function generateAzurePipelinesTest() { 5 | const azurepipelines = generateYaml(); 6 | const actual = azurepipelines.toString(); 7 | const expected = Deno.readTextFileSync("./fixtures/azure-pipelines.yml"); 8 | assertEquals(actual, expected); 9 | }); 10 | -------------------------------------------------------------------------------- /crates/cli/src/types.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Default)] 2 | pub struct InstallArgs { 3 | pub tools: Option>, 4 | pub ask: bool, 5 | pub remote_is_present: bool, 6 | pub remote: Option, 7 | pub port: Option, 8 | pub username: Option, 9 | pub inventory: Option, 10 | } 11 | 12 | #[derive(Clone, Default)] 13 | pub struct SearchArgs { 14 | pub package: String, 15 | pub channel: String, 16 | pub max_results: u32, 17 | } 18 | -------------------------------------------------------------------------------- /crates/migration/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use sea_orm_migration::{ 4 | prelude::*, 5 | sea_orm::{DbBackend, Statement}, 6 | }; 7 | 8 | #[async_std::main] 9 | async fn main() { 10 | let home = std::env::var("HOME").unwrap(); 11 | let crosup_dir = format!("{}/.config/crosup", home); 12 | fs::create_dir_all(&crosup_dir).unwrap(); 13 | 14 | let database_url = format!("sqlite:{}/modifications.sqlite3?mode=rwc", crosup_dir); 15 | 16 | std::env::set_var("DATABASE_URL", database_url); 17 | 18 | cli::run_cli(migration::Migrator).await; 19 | } 20 | -------------------------------------------------------------------------------- /crates/types/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod apk; 2 | pub mod apt; 3 | pub mod brew; 4 | pub mod configuration; 5 | pub mod curl; 6 | pub mod dnf; 7 | pub mod emerge; 8 | pub mod fleek; 9 | pub mod git; 10 | pub mod home_manager; 11 | pub mod install; 12 | pub mod inventory; 13 | pub mod nix; 14 | pub mod pacman; 15 | pub mod slackpkg; 16 | pub mod yum; 17 | pub mod zypper; 18 | 19 | pub const CROSFILE_TOML: &str = "Crosfile.toml"; 20 | pub const CROSFILE_HCL: &str = "Crosfile.hcl"; 21 | pub const INVENTORY_TOML: &str = "Inventory.toml"; 22 | pub const INVENTORY_HCL: &str = "Inventory.hcl"; 23 | -------------------------------------------------------------------------------- /.fluentci/src/dagger/list_jobs.ts: -------------------------------------------------------------------------------- 1 | import { brightGreen, stringifyTree } from "../../deps.ts"; 2 | import { runnableJobs, jobDescriptions, Job } from "./jobs.ts"; 3 | 4 | const tree = { 5 | name: brightGreen("rust_pipeline"), 6 | children: (Object.keys(runnableJobs) as Job[]).map((job) => ({ 7 | name: jobDescriptions[job] 8 | ? `${brightGreen(job)} - ${jobDescriptions[job]}` 9 | : brightGreen(job), 10 | children: [], 11 | })), 12 | }; 13 | 14 | console.log( 15 | stringifyTree( 16 | tree, 17 | (t) => t.name, 18 | (t) => t.children 19 | ) 20 | ); 21 | -------------------------------------------------------------------------------- /crates/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-macros" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.2.0" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | -------------------------------------------------------------------------------- /crates/tui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-tui" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.1.0" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | anyhow = "1.0.71" 16 | bat = "0.23.0" 17 | chrono = "0.4.26" 18 | crossterm = "0.26.1" 19 | tui = "0.19.0" 20 | -------------------------------------------------------------------------------- /crates/installers/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | use anyhow::Error; 4 | 5 | pub mod apk; 6 | pub mod apt; 7 | pub mod brew; 8 | pub mod curl; 9 | pub mod dnf; 10 | pub mod emerge; 11 | pub mod fleek; 12 | pub mod git; 13 | pub mod home_manager; 14 | pub mod nix; 15 | pub mod pacman; 16 | pub mod slackpkg; 17 | pub mod yum; 18 | pub mod zypper; 19 | 20 | pub trait Installer { 21 | fn install(&self) -> Result<(), Error>; 22 | fn is_installed(&self) -> Result; 23 | fn name(&self) -> &str; 24 | fn version(&self) -> &str; 25 | fn dependencies(&self) -> Vec; 26 | fn is_default(&self) -> bool; 27 | fn provider(&self) -> &str; 28 | fn as_any(&self) -> &dyn Any; 29 | } 30 | -------------------------------------------------------------------------------- /crates/ssh/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-ssh" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.1.0" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | anyhow = "1.0.71" 16 | owo-colors = "3.5.0" 17 | ssh2 = {version = "0.9.4", features = ["vendored-openssl"]} 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: tsirysndr # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | open_collective: # Replace with a single Open Collective username 5 | ko_fi: # Replace with a single Ko-fi username 6 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 7 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 8 | liberapay: # Replace with a single Liberapay username 9 | issuehunt: # Replace with a single IssueHunt username 10 | otechie: # Replace with a single Otechie username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -------------------------------------------------------------------------------- /.fluentci/.github/workflows/zenith.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_github_actions 2 | 3 | name: Zenith Example 4 | on: 5 | push: 6 | branches: 7 | - zenith 8 | 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup Dagger Zenith 15 | run: | 16 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.8 sh 17 | sudo mv bin/dagger /usr/local/bin 18 | dagger version 19 | - name: Run Dagger Pipelines 20 | run: | 21 | dagger query --doc test.gql 22 | dagger query --doc build.gql 23 | working-directory: example 24 | -------------------------------------------------------------------------------- /.fluentci/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "importMap": "import_map.json", 3 | "tasks": { 4 | "esm:add": "deno run -A https://esm.sh/v128 add", 5 | "esm:update": "deno run -A https://esm.sh/v128 update", 6 | "esm:remove": "deno run -A https://esm.sh/v128 remove", 7 | "schema": "deno run -A src/dagger/schema.ts", 8 | "clean": "rm -rf gen schema.graphql" 9 | }, 10 | "fmt": { 11 | "exclude": [ 12 | "example/", 13 | ".fluentci/", 14 | "gen/" 15 | ] 16 | }, 17 | "lint": { 18 | "exclude": [ 19 | "example/", 20 | ".fluentci/", 21 | "gen/" 22 | ] 23 | }, 24 | "test": { 25 | "exclude": [ 26 | "example/", 27 | ".fluentci/", 28 | "gen/" 29 | ] 30 | } 31 | } -------------------------------------------------------------------------------- /.fluentci/fixtures/buildspec.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_aws_codepipeline 2 | 3 | version: 0.2 4 | phases: 5 | install: 6 | commands: 7 | - curl -fsSL https://deno.land/x/install/install.sh | sh 8 | - export DENO_INSTALL="$HOME/.deno" 9 | - export PATH="$DENO_INSTALL/bin:$PATH" 10 | - deno install -A -r https://cli.fluentci.io -n fluentci 11 | - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 12 | - mv bin/dagger /usr/local/bin 13 | - dagger version 14 | build: 15 | commands: 16 | - fluentci run rust_pipeline test build 17 | post_build: 18 | commands: 19 | - echo Build completed on `date` 20 | -------------------------------------------------------------------------------- /crates/types/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-types" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.2.1" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | hcl-rs = "0.14.2" 16 | indexmap = { version = "1.9.3", features = ["serde"] } 17 | os-release = "0.1.0" 18 | serde = "1.0.163" 19 | -------------------------------------------------------------------------------- /crates/entity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-entity" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.1.0" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | chrono = "0.4.26" 16 | sea-orm = {version = "0.11.3", features = ["runtime-tokio-rustls", "sqlx-sqlite"]} 17 | serde = "1.0.164" 18 | -------------------------------------------------------------------------------- /crates/migration/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | pub use sea_orm_migration::prelude::*; 4 | 5 | mod m20220101_000001_create_table; 6 | 7 | pub struct Migrator; 8 | 9 | #[async_trait::async_trait] 10 | impl MigratorTrait for Migrator { 11 | fn migrations() -> Vec> { 12 | vec![Box::new(m20220101_000001_create_table::Migration)] 13 | } 14 | } 15 | 16 | pub async fn run() { 17 | let home = std::env::var("HOME").unwrap(); 18 | let crosup_dir = format!("{}/.config/crosup", home); 19 | fs::create_dir_all(&crosup_dir).unwrap(); 20 | let database_url = format!("sqlite:{}/modifications.sqlite3?mode=rwc", crosup_dir); 21 | 22 | std::env::set_var("DATABASE_URL", database_url); 23 | cli::run_cli(Migrator).await; 24 | } 25 | -------------------------------------------------------------------------------- /crates/repo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-repo" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.1.0" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | anyhow = "1.0.71" 16 | crosup-entity = {version = "0.1.0", path = "../entity"} 17 | sea-orm = {version = "0.11.3", features = ["runtime-tokio-rustls", "sqlx-sqlite"]} 18 | -------------------------------------------------------------------------------- /.fluentci/src/dagger/pipeline.ts: -------------------------------------------------------------------------------- 1 | import { uploadContext } from "../../deps.ts"; 2 | import * as jobs from "./jobs.ts"; 3 | 4 | const { build, test, exclude } = jobs; 5 | 6 | export default async function pipeline(src = ".", args: string[] = []) { 7 | if (Deno.env.has("FLUENTCI_SESSION_ID")) { 8 | await uploadContext(src, exclude); 9 | } 10 | 11 | if (args.length > 0) { 12 | await runSpecificJobs(args); 13 | return; 14 | } 15 | 16 | await test(src); 17 | await build(src); 18 | } 19 | 20 | async function runSpecificJobs(args: string[]) { 21 | for (const name of args) { 22 | // deno-lint-ignore no-explicit-any 23 | const job = (jobs as any)[name]; 24 | if (!job) { 25 | throw new Error(`Job ${name} not found`); 26 | } 27 | await job(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.fluentci/fixtures/workflow.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_github_actions 2 | 3 | name: Test 4 | on: 5 | push: 6 | branches: 7 | - main 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: denoland/setup-deno@v1 14 | with: 15 | deno-version: v1.37 16 | - name: Setup Fluent CI CLI 17 | run: deno install -A -r https://cli.fluentci.io -n fluentci 18 | - name: Setup Dagger 19 | run: | 20 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 21 | sudo mv bin/dagger /usr/local/bin 22 | dagger version 23 | - name: Run Tests and Build 24 | run: fluentci run rust_pipeline test build 25 | -------------------------------------------------------------------------------- /crates/entity/src/file.rs: -------------------------------------------------------------------------------- 1 | use sea_orm::entity::prelude::*; 2 | use sea_orm::DeriveEntityModel; 3 | 4 | #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] 5 | #[sea_orm(table_name = "file")] 6 | pub struct Model { 7 | #[sea_orm(primary_key)] 8 | pub id: i32, 9 | pub name: String, 10 | pub path: String, 11 | #[sea_orm(column_type = "Timestamp")] 12 | pub created_at: chrono::NaiveDateTime, 13 | } 14 | 15 | #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] 16 | pub enum Relation { 17 | #[sea_orm(has_many = "super::modification::Entity")] 18 | Modification, 19 | } 20 | 21 | // `Related` trait has to be implemented by hand 22 | impl Related for Entity { 23 | fn to() -> RelationDef { 24 | Relation::Modification.def() 25 | } 26 | } 27 | 28 | impl ActiveModelBehavior for ActiveModel {} 29 | -------------------------------------------------------------------------------- /.fluentci/fixtures/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_azure_pipelines 2 | 3 | trigger: 4 | - main 5 | pool: 6 | name: Default 7 | vmImage: ubuntu-latest 8 | steps: 9 | - script: | 10 | curl -fsSL https://deno.land/x/install/install.sh | sh 11 | export DENO_INSTALL="$HOME/.deno" 12 | export PATH="$DENO_INSTALL/bin:$PATH" 13 | displayName: Install Deno 14 | - script: deno install -A -r https://cli.fluentci.io -n fluentci 15 | displayName: Setup Fluent CI CLI 16 | - script: | 17 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 18 | sudo mv bin/dagger /usr/local/bin 19 | dagger version 20 | displayName: Setup Dagger 21 | - script: fluentci run rust_pipeline test build 22 | displayName: Run Dagger Pipelines 23 | -------------------------------------------------------------------------------- /.fluentci/.github/workflows/example.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_github_actions 2 | 3 | name: Example 4 | on: 5 | push: 6 | branches: 7 | - main 8 | jobs: 9 | tests: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: denoland/setup-deno@v1 14 | with: 15 | deno-version: v1.37 16 | - name: Setup Fluent CI CLI 17 | run: deno install -A -r https://cli.fluentci.io -n fluentci 18 | - name: Setup Dagger 19 | run: | 20 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.8 sh 21 | sudo mv bin/dagger /usr/local/bin 22 | dagger version 23 | - name: Run Dagger Pipelines 24 | run: dagger run deno run -A ../src/dagger/runner.ts 25 | working-directory: example 26 | -------------------------------------------------------------------------------- /crates/types/src/home_manager.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 4 | pub struct HomeManagerConfiguration {} 5 | 6 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 7 | pub struct Package { 8 | #[serde(skip_serializing, skip_deserializing)] 9 | pub name: String, 10 | 11 | #[serde(skip_serializing_if = "Option::is_none")] 12 | pub packages: Option>, 13 | 14 | #[serde(skip_serializing_if = "Option::is_none")] 15 | pub depends_on: Option>, 16 | 17 | #[serde(skip_serializing_if = "Option::is_none")] 18 | pub postinstall: Option, 19 | 20 | #[serde(skip_serializing_if = "Option::is_none")] 21 | pub version_check: Option, 22 | 23 | #[serde(skip_serializing_if = "Option::is_none")] 24 | pub apply: Option, 25 | } 26 | -------------------------------------------------------------------------------- /.fluentci/fixtures/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_gitlab_ci 2 | 3 | .docker: 4 | image: denoland/deno:alpine 5 | services: 6 | - docker:${DOCKER_VERSION}-dind 7 | variables: 8 | DOCKER_HOST: tcp://docker:2376 9 | DOCKER_TLS_VERIFY: "1" 10 | DOCKER_TLS_CERTDIR: /certs 11 | DOCKER_CERT_PATH: /certs/client 12 | DOCKER_DRIVER: overlay2 13 | DOCKER_VERSION: 20.10.16 14 | 15 | .dagger: 16 | extends: .docker 17 | before_script: 18 | - apk add docker-cli curl unzip 19 | - deno install -A -r https://cli.fluentci.io -n fluentci 20 | - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 21 | - mv bin/dagger /usr/local/bin 22 | - dagger version 23 | 24 | tests: 25 | extends: .dagger 26 | script: 27 | - fluentci run rust_pipeline test build 28 | 29 | -------------------------------------------------------------------------------- /.github/workflows/flakehub-publish-tagged.yml: -------------------------------------------------------------------------------- 1 | name: "Publish tags to FlakeHub" 2 | on: 3 | push: 4 | tags: 5 | - "v?[0-9]+.[0-9]+.[0-9]+*" 6 | workflow_dispatch: 7 | inputs: 8 | tag: 9 | description: "The existing tag to publish to FlakeHub" 10 | type: "string" 11 | required: true 12 | jobs: 13 | flakehub-publish: 14 | runs-on: "ubuntu-latest" 15 | permissions: 16 | id-token: "write" 17 | contents: "read" 18 | steps: 19 | - uses: "actions/checkout@v3" 20 | with: 21 | ref: "${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' }}" 22 | - uses: "DeterminateSystems/nix-installer-action@main" 23 | - uses: "DeterminateSystems/flakehub-push@main" 24 | with: 25 | visibility: "public" 26 | name: "tsirysndr/crosup" 27 | tag: "${{ inputs.tag }}" 28 | -------------------------------------------------------------------------------- /.fluentci/src/aws/config.ts: -------------------------------------------------------------------------------- 1 | import { BuildSpec } from "fluent_aws_codepipeline"; 2 | 3 | export function generateYaml(): BuildSpec { 4 | const buildspec = new BuildSpec(); 5 | buildspec 6 | .phase("install", { 7 | commands: [ 8 | "curl -fsSL https://deno.land/x/install/install.sh | sh", 9 | 'export DENO_INSTALL="$HOME/.deno"', 10 | 'export PATH="$DENO_INSTALL/bin:$PATH"', 11 | "deno install -A -r https://cli.fluentci.io -n fluentci", 12 | "curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh", 13 | "mv bin/dagger /usr/local/bin", 14 | "dagger version", 15 | ], 16 | }) 17 | .phase("build", { 18 | commands: ["fluentci run rust_pipeline test build"], 19 | }) 20 | .phase("post_build", { 21 | commands: ["echo Build completed on `date`"], 22 | }); 23 | return buildspec; 24 | } 25 | -------------------------------------------------------------------------------- /crates/nix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-nix" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.1.1" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | anyhow = "1.0.71" 16 | async-trait = "0.1.68" 17 | nix-editor = "0.3.0" 18 | serde = "1.0.164" 19 | serde_json = "1.0.97" 20 | surf = {version = "2.3.2", features = ["h1-client-rustls"], default-features = false} 21 | 22 | [dev-dependencies] 23 | tokio = {version = "1.28.2", features = ["full"]} 24 | -------------------------------------------------------------------------------- /crates/types/src/dnf.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct DnfConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | } 27 | -------------------------------------------------------------------------------- /crates/types/src/yum.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct YumConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | } 27 | -------------------------------------------------------------------------------- /crates/types/src/pacman.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct PacmanConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | } 27 | -------------------------------------------------------------------------------- /crates/types/src/slackpkg.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct SlackpkgConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | } 27 | -------------------------------------------------------------------------------- /.fluentci/src/dagger/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | queryType, 3 | makeSchema, 4 | dirname, 5 | join, 6 | resolve, 7 | stringArg, 8 | nonNull, 9 | } from "../../deps.ts"; 10 | 11 | import { test, build } from "./jobs.ts"; 12 | 13 | const Query = queryType({ 14 | definition(t) { 15 | t.string("test", { 16 | args: { 17 | src: nonNull(stringArg()), 18 | }, 19 | resolve: async (_root, args, _ctx) => await test(args.src), 20 | }); 21 | t.string("build", { 22 | args: { 23 | src: nonNull(stringArg()), 24 | }, 25 | resolve: async (_root, args, _ctx) => await build(args.src), 26 | }); 27 | }, 28 | }); 29 | 30 | export const schema = makeSchema({ 31 | types: [Query], 32 | outputs: { 33 | schema: resolve(join(dirname(".."), dirname(".."), "schema.graphql")), 34 | typegen: resolve(join(dirname(".."), dirname(".."), "gen", "nexus.ts")), 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /.fluentci/fixtures/config.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_circleci 2 | 3 | version: 2.1 4 | jobs: 5 | tests: 6 | steps: 7 | - checkout 8 | - run: sudo apt-get update && sudo apt-get install -y curl unzip 9 | - run: | 10 | curl -fsSL https://deno.land/x/install/install.sh | sh 11 | export DENO_INSTALL="$HOME/.deno" 12 | export PATH="$DENO_INSTALL/bin:$PATH" 13 | - run: deno install -A -r https://cli.fluentci.io -n fluentci 14 | - run: | 15 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 16 | sudo mv bin/dagger /usr/local/bin 17 | dagger version 18 | - run: 19 | name: Run Dagger Pipelines 20 | command: fluentci run rust_pipeline test build 21 | machine: 22 | image: ubuntu-2004:2023.07.1 23 | workflows: 24 | dagger: 25 | jobs: 26 | - tests 27 | -------------------------------------------------------------------------------- /.fluentci/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Do not edit this file directly. It is generated by Fluent Github Actions 2 | 3 | name: Codecov 4 | on: 5 | push: 6 | branches: 7 | - main 8 | jobs: 9 | tests: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: denoland/setup-deno@v1 14 | with: 15 | deno-version: v1.37 16 | - name: Setup Fluent CI CLI 17 | run: deno install -A -r https://cli.fluentci.io -n fluentci 18 | - name: Setup Dagger 19 | run: | 20 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.8 sh 21 | sudo mv bin/dagger /usr/local/bin 22 | dagger version 23 | - name: Run Dagger Pipelines 24 | run: dagger run fluentci . fmt lint test 25 | - name: Upload to Codecov 26 | run: dagger run fluentci codecov_pipeline 27 | env: 28 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 29 | -------------------------------------------------------------------------------- /crates/types/src/apk.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct ApkConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | pub interactive: Option, 29 | } 30 | -------------------------------------------------------------------------------- /crates/types/src/fleek.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct FleekConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | pub apply: Option, 29 | } 30 | -------------------------------------------------------------------------------- /crates/types/src/zypper.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct ZypperConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | pub non_interactive: Option, 29 | } 30 | -------------------------------------------------------------------------------- /crates/installers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-installers" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.2.1" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | anyhow = "1.0.71" 16 | crosup-macros = { path = "../macros", version = "0.2.0" } 17 | crosup-nix = { path = "../nix", version = "0.1.1" } 18 | crosup-ssh = { path = "../ssh", version = "0.1.0" } 19 | crosup-types = { path = "../types", version = "0.2.1" } 20 | indexmap = { version = "1.9.3", features = ["serde"] } 21 | owo-colors = "3.5.0" 22 | ssh2 = { version = "0.9.4", features = ["vendored-openssl"] } 23 | -------------------------------------------------------------------------------- /crates/migration/README.md: -------------------------------------------------------------------------------- 1 | # Running Migrator CLI 2 | 3 | - Generate a new migration file 4 | ```sh 5 | cargo run -- migrate generate MIGRATION_NAME 6 | ``` 7 | - Apply all pending migrations 8 | ```sh 9 | cargo run 10 | ``` 11 | ```sh 12 | cargo run -- up 13 | ``` 14 | - Apply first 10 pending migrations 15 | ```sh 16 | cargo run -- up -n 10 17 | ``` 18 | - Rollback last applied migrations 19 | ```sh 20 | cargo run -- down 21 | ``` 22 | - Rollback last 10 applied migrations 23 | ```sh 24 | cargo run -- down -n 10 25 | ``` 26 | - Drop all tables from the database, then reapply all migrations 27 | ```sh 28 | cargo run -- fresh 29 | ``` 30 | - Rollback all applied migrations, then reapply all migrations 31 | ```sh 32 | cargo run -- refresh 33 | ``` 34 | - Rollback all applied migrations 35 | ```sh 36 | cargo run -- reset 37 | ``` 38 | - Check the status of all migrations 39 | ```sh 40 | cargo run -- status 41 | ``` 42 | -------------------------------------------------------------------------------- /crates/core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-core" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.2.1" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | anyhow = "1.0.71" 16 | crosup-installers = { path = "../installers", version = "0.2.0" } 17 | crosup-macros = { path = "../macros", version = "0.2.0" } 18 | crosup-ssh = { path = "../ssh", version = "0.1.0" } 19 | crosup-types = { path = "../types", version = "0.2.1" } 20 | hcl-rs = "0.14.2" 21 | os-release = "0.1.0" 22 | owo-colors = "3.5.0" 23 | ssh2 = { version = "0.9.4", features = ["vendored-openssl"] } 24 | toml = "0.7.4" 25 | -------------------------------------------------------------------------------- /.fluentci/src/circleci/config.ts: -------------------------------------------------------------------------------- 1 | import { CircleCI, Job } from "fluent_circleci"; 2 | 3 | export function generateYaml(): CircleCI { 4 | const circleci = new CircleCI(); 5 | 6 | const tests = new Job().machine({ image: "ubuntu-2004:2023.07.1" }).steps([ 7 | "checkout", 8 | { 9 | run: "sudo apt-get update && sudo apt-get install -y curl unzip", 10 | }, 11 | { 12 | run: `\ 13 | curl -fsSL https://deno.land/x/install/install.sh | sh 14 | export DENO_INSTALL="$HOME/.deno" 15 | export PATH="$DENO_INSTALL/bin:$PATH"`, 16 | }, 17 | { 18 | run: "deno install -A -r https://cli.fluentci.io -n fluentci", 19 | }, 20 | { 21 | run: `\ 22 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 23 | sudo mv bin/dagger /usr/local/bin 24 | dagger version`, 25 | }, 26 | { 27 | run: { 28 | name: "Run Dagger Pipelines", 29 | command: "fluentci run rust_pipeline test build", 30 | }, 31 | }, 32 | ]); 33 | 34 | circleci.jobs({ tests }).workflow("dagger", ["tests"]); 35 | 36 | return circleci; 37 | } 38 | -------------------------------------------------------------------------------- /crates/types/src/emerge.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct EmergeConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub packages: Option>, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub depends_on: Option>, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub version_check: Option, 26 | 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | pub ask: Option, 29 | 30 | #[serde(skip_serializing_if = "Option::is_none")] 31 | pub verbose: Option, 32 | } 33 | -------------------------------------------------------------------------------- /.fluentci/src/gitlab/config.ts: -------------------------------------------------------------------------------- 1 | import { GitlabCI, Job } from "fluent_gitlab_ci"; 2 | 3 | export function generateYaml(): GitlabCI { 4 | const docker = new Job() 5 | .image("denoland/deno:alpine") 6 | .services(["docker:${DOCKER_VERSION}-dind"]) 7 | .variables({ 8 | DOCKER_HOST: "tcp://docker:2376", 9 | DOCKER_TLS_VERIFY: "1", 10 | DOCKER_TLS_CERTDIR: "/certs", 11 | DOCKER_CERT_PATH: "/certs/client", 12 | DOCKER_DRIVER: "overlay2", 13 | DOCKER_VERSION: "20.10.16", 14 | }); 15 | 16 | const dagger = new Job().extends(".docker").beforeScript( 17 | ` 18 | apk add docker-cli curl unzip 19 | deno install -A -r https://cli.fluentci.io -n fluentci 20 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 21 | mv bin/dagger /usr/local/bin 22 | dagger version 23 | ` 24 | ); 25 | 26 | const tests = new Job() 27 | .extends(".dagger") 28 | .script("fluentci run rust_pipeline test build"); 29 | 30 | return new GitlabCI() 31 | .addJob(".docker", docker) 32 | .addJob(".dagger", dagger) 33 | .addJob("tests", tests); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Tsiry Sandratraina 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /.fluentci/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Tsiry Sandratraina 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | release: 4 | types: [created] 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | target: 12 | - aarch64-unknown-linux-gnu 13 | - x86_64-unknown-linux-gnu 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Setup Fluent CI 17 | uses: fluentci-io/setup-fluentci@v5 18 | - name: Set env 19 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 20 | - name: Build 21 | run: | 22 | rm -rf .fluentci/deno.lock 23 | fluentci run . build 24 | env: 25 | TAG: ${{ env.RELEASE_VERSION }} 26 | TARGET: ${{ matrix.target }} 27 | - name: Upload release assets 28 | run: | 29 | for ext in tar.gz tar.gz.sha256; do 30 | export FILE=/assets/crosup_${{ env.RELEASE_VERSION }}_${{ matrix.target }}.$ext 31 | fluentci run github_pipeline release_upload 32 | done 33 | env: 34 | TAG: ${{ env.RELEASE_VERSION }} 35 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | -------------------------------------------------------------------------------- /crates/types/src/inventory.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone)] 5 | pub struct Inventory { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub server: IndexMap, 8 | } 9 | 10 | impl Default for Inventory { 11 | fn default() -> Self { 12 | let mut server = IndexMap::new(); 13 | server.insert("server1".into(), ServerConnection::default()); 14 | Self { server } 15 | } 16 | } 17 | 18 | #[derive(Serialize, Deserialize, Debug, Clone)] 19 | pub struct ServerConnection { 20 | #[serde(skip_serializing, skip_deserializing)] 21 | pub name: String, 22 | pub host: String, 23 | pub username: String, 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub port: Option, 26 | } 27 | 28 | impl Default for ServerConnection { 29 | fn default() -> Self { 30 | Self { 31 | name: "server1".to_string(), 32 | host: "127.0.0.1".to_string(), 33 | username: "username".to_string(), 34 | port: Some(22), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.fluentci/.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/debian 3 | { 4 | "name": "Debian", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/base:bullseye", 7 | "features": { 8 | "ghcr.io/devcontainers/features/github-cli:1": {}, 9 | "ghcr.io/devcontainers/features/nix:1": {} 10 | }, 11 | 12 | // Features to add to the dev container. More info: https://containers.dev/features. 13 | // "features": {}, 14 | 15 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 16 | // "forwardPorts": [], 17 | 18 | // Use 'postCreateCommand' to run commands after the container is created. 19 | "postCreateCommand": "nix develop --experimental-features \"nix-command flakes\"" 20 | // Configure tool-specific properties. 21 | // "customizations": {}, 22 | 23 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 24 | // "remoteUser": "root" 25 | } 26 | -------------------------------------------------------------------------------- /crates/migration/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup-migration" 9 | repository = "https://github.com/tsirysndr/crosup" 10 | version = "0.1.0" 11 | 12 | [lib] 13 | name = "migration" 14 | path = "src/lib.rs" 15 | 16 | [dependencies] 17 | async-std = {version = "1", features = ["attributes", "tokio1"]} 18 | chrono = "0.4.26" 19 | 20 | [dependencies.sea-orm-migration] 21 | features = [ 22 | # Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI. 23 | # View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime. 24 | # e.g. 25 | # "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature 26 | # "sqlx-postgres", # `DATABASE_DRIVER` feature 27 | "runtime-tokio-rustls", 28 | "sqlx-sqlite", 29 | ] 30 | version = "0.11.0" 31 | -------------------------------------------------------------------------------- /.fluentci/src/github/config.ts: -------------------------------------------------------------------------------- 1 | import { JobSpec, Workflow } from "fluent_github_actions"; 2 | 3 | export function generateYaml(): Workflow { 4 | const workflow = new Workflow("Test"); 5 | 6 | const push = { 7 | branches: ["main"], 8 | }; 9 | 10 | const setupDagger = `\ 11 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 12 | sudo mv bin/dagger /usr/local/bin 13 | dagger version`; 14 | 15 | const test: JobSpec = { 16 | "runs-on": "ubuntu-latest", 17 | steps: [ 18 | { 19 | uses: "actions/checkout@v2", 20 | }, 21 | { 22 | uses: "denoland/setup-deno@v1", 23 | with: { 24 | "deno-version": "v1.37", 25 | }, 26 | }, 27 | { 28 | name: "Setup Fluent CI CLI", 29 | run: "deno install -A -r https://cli.fluentci.io -n fluentci", 30 | }, 31 | { 32 | name: "Setup Dagger", 33 | run: setupDagger, 34 | }, 35 | { 36 | name: "Run Tests and Build", 37 | run: "fluentci run rust_pipeline test build", 38 | }, 39 | ], 40 | }; 41 | 42 | workflow.on({ push }).jobs({ test }); 43 | 44 | return workflow; 45 | } 46 | -------------------------------------------------------------------------------- /.fluentci/src/azure/config.ts: -------------------------------------------------------------------------------- 1 | import { AzurePipeline } from "fluent_azure_pipelines"; 2 | 3 | export function generateYaml(): AzurePipeline { 4 | const azurePipeline = new AzurePipeline(); 5 | 6 | const installDeno = `\ 7 | curl -fsSL https://deno.land/x/install/install.sh | sh 8 | export DENO_INSTALL="$HOME/.deno" 9 | export PATH="$DENO_INSTALL/bin:$PATH" 10 | `; 11 | 12 | const setupDagger = `\ 13 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 14 | sudo mv bin/dagger /usr/local/bin 15 | dagger version 16 | `; 17 | 18 | azurePipeline 19 | .trigger(["main"]) 20 | .pool({ 21 | name: "Default", 22 | vmImage: "ubuntu-latest", 23 | }) 24 | .step({ 25 | script: installDeno, 26 | displayName: "Install Deno", 27 | }) 28 | .step({ 29 | script: "deno install -A -r https://cli.fluentci.io -n fluentci", 30 | displayName: "Setup Fluent CI CLI", 31 | }) 32 | .step({ 33 | script: setupDagger, 34 | displayName: "Setup Dagger", 35 | }) 36 | .step({ 37 | script: "fluentci run rust_pipeline test build", 38 | displayName: "Run Dagger Pipelines", 39 | }); 40 | return azurePipeline; 41 | } 42 | -------------------------------------------------------------------------------- /crates/repo/src/file.rs: -------------------------------------------------------------------------------- 1 | use crosup_entity::file; 2 | use sea_orm::{ 3 | ActiveModelTrait, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, Set, 4 | }; 5 | 6 | pub struct FileRepo { 7 | db: DatabaseConnection, 8 | } 9 | 10 | impl FileRepo { 11 | pub fn new(db: &DatabaseConnection) -> Self { 12 | Self { db: db.clone() } 13 | } 14 | 15 | pub async fn find(&self, id: i32) -> Result, DbErr> { 16 | file::Entity::find_by_id(id).one(&self.db).await 17 | } 18 | 19 | pub async fn find_by_path(&self, path: &str) -> Result, DbErr> { 20 | file::Entity::find() 21 | .filter(file::Column::Path.eq(path)) 22 | .one(&self.db) 23 | .await 24 | } 25 | 26 | pub async fn create(&self, name: &str, path: &str) -> Result { 27 | let result = self.find_by_path(path).await?; 28 | 29 | if let Some(file) = result { 30 | return Ok(file); 31 | } 32 | 33 | file::ActiveModel { 34 | name: Set(name.to_owned()), 35 | path: Set(path.to_owned()), 36 | ..Default::default() 37 | } 38 | .insert(&self.db) 39 | .await 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /.fluentci/deps.ts: -------------------------------------------------------------------------------- 1 | export { assertEquals } from "https://deno.land/std@0.191.0/testing/asserts.ts"; 2 | import Client from "https://sdk.fluentci.io/v0.2.0/mod.ts"; 3 | export default Client; 4 | 5 | export { 6 | connect, 7 | uploadContext, 8 | CacheSharingMode, 9 | Container, 10 | } from "https://sdk.fluentci.io/v0.2.0/mod.ts"; 11 | export { brightGreen } from "https://deno.land/std@0.191.0/fmt/colors.ts"; 12 | export { withDevbox } from "https://nix.fluentci.io/v0.5.2/src/dagger/steps.ts"; 13 | export { stringifyTree } from "https://esm.sh/stringify-tree@1.1.1"; 14 | import gql from "https://esm.sh/graphql-tag@2.12.6"; 15 | export { gql }; 16 | 17 | export { 18 | arg, 19 | queryType, 20 | stringArg, 21 | intArg, 22 | nonNull, 23 | makeSchema, 24 | } from "npm:nexus"; 25 | export { 26 | dirname, 27 | join, 28 | resolve, 29 | } from "https://deno.land/std@0.203.0/path/mod.ts"; 30 | 31 | export * as FluentGitlabCI from "https://deno.land/x/fluent_gitlab_ci@v0.4.2/mod.ts"; 32 | export * as FluentGithubActions from "https://deno.land/x/fluent_github_actions@v0.2.1/mod.ts"; 33 | export * as FluentCircleCI from "https://deno.land/x/fluent_circleci@v0.2.5/mod.ts"; 34 | export * as FluentAzurePipelines from "https://deno.land/x/fluent_azure_pipelines@v0.2.0/mod.ts"; 35 | export * as FluentAWSCodePipeline from "https://deno.land/x/fluent_aws_codepipeline@v0.2.3/mod.ts"; 36 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/rust 3 | { 4 | "name": "Rust", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/rust:1-1-bullseye", 7 | "features": { 8 | "ghcr.io/devcontainers/features/github-cli:1": {}, 9 | "ghcr.io/devcontainers/features/nix:1": {} 10 | }, 11 | 12 | // Use 'mounts' to make the cargo cache persistent in a Docker Volume. 13 | // "mounts": [ 14 | // { 15 | // "source": "devcontainer-cargo-cache-${devcontainerId}", 16 | // "target": "/usr/local/cargo", 17 | // "type": "volume" 18 | // } 19 | // ] 20 | 21 | // Features to add to the dev container. More info: https://containers.dev/features. 22 | // "features": {}, 23 | 24 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 25 | // "forwardPorts": [], 26 | 27 | // Use 'postCreateCommand' to run commands after the container is created. 28 | "postCreateCommand": "nix develop --experimental-features \"nix-command flakes\"" 29 | 30 | // Configure tool-specific properties. 31 | // "customizations": {}, 32 | 33 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 34 | // "remoteUser": "root" 35 | } 36 | -------------------------------------------------------------------------------- /crates/cli/src/cmd/diff.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_core::config::verify_if_config_file_is_present; 3 | use crosup_repo::{file::FileRepo, modification::ModificationRepo}; 4 | use migration::MigratorTrait; 5 | use owo_colors::OwoColorize; 6 | use sea_orm::DatabaseConnection; 7 | 8 | use crate::cmd::print_diff; 9 | 10 | use super::get_database_connection; 11 | 12 | pub async fn execute_diff() -> Result<(), Error> { 13 | let (_, filename, content, _) = verify_if_config_file_is_present()?; 14 | 15 | let db: DatabaseConnection = get_database_connection().await?; 16 | migration::Migrator::up(&db, None).await?; 17 | let current_dir = std::env::current_dir()?; 18 | let path = format!("{}/{}", current_dir.display(), filename); 19 | 20 | let result = FileRepo::new(&db).find_by_path(&path).await?; 21 | if let Some(file) = result { 22 | let result = ModificationRepo::new(&db) 23 | .find_last_by_file_id(file.id) 24 | .await?; 25 | if let Some(last_modif) = result { 26 | let hash = sha256::digest(content.clone()); 27 | if hash != last_modif.hash { 28 | println!(" 📝 {} has been modified", filename.bold().cyan()); 29 | print_diff(&last_modif.content, &content); 30 | return Ok(()); 31 | } 32 | println!("{} has not been modified", filename.bold().cyan()); 33 | } 34 | } 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /crates/entity/src/modification.rs: -------------------------------------------------------------------------------- 1 | use sea_orm::entity::prelude::*; 2 | use sea_orm::DeriveEntityModel; 3 | 4 | #[derive(Clone, Debug, PartialEq, DeriveEntityModel)] 5 | #[sea_orm(table_name = "modification")] 6 | pub struct Model { 7 | #[sea_orm(primary_key)] 8 | pub id: i32, 9 | #[sea_orm(column_type = "Timestamp")] 10 | pub timestamp: chrono::NaiveDateTime, 11 | pub hash: String, 12 | pub file_id: i32, 13 | pub previous_id: Option, 14 | #[sea_orm(column_type = "Text")] 15 | pub content: String, 16 | } 17 | 18 | #[derive(Copy, Clone, Debug, EnumIter)] 19 | pub enum Relation { 20 | File, 21 | Modification, 22 | } 23 | 24 | impl RelationTrait for Relation { 25 | fn def(&self) -> RelationDef { 26 | match self { 27 | Self::File => Entity::belongs_to(super::file::Entity) 28 | .from(Column::FileId) 29 | .to(super::file::Column::Id) 30 | .into(), 31 | Self::Modification => Entity::has_one(super::modification::Entity) 32 | .from(Column::Id) 33 | .to(super::modification::Column::PreviousId) 34 | .into(), 35 | } 36 | } 37 | } 38 | 39 | impl Related for Entity { 40 | fn to() -> RelationDef { 41 | Relation::File.def() 42 | } 43 | } 44 | 45 | impl Related for Entity { 46 | fn to() -> RelationDef { 47 | Relation::Modification.def() 48 | } 49 | } 50 | 51 | impl ActiveModelBehavior for ActiveModel {} 52 | -------------------------------------------------------------------------------- /.fluentci/README.md: -------------------------------------------------------------------------------- 1 | # Rust Pipeline 2 | 3 | [![fluentci pipeline](https://img.shields.io/badge/dynamic/json?label=pkg.fluentci.io&labelColor=%23000&color=%23460cf1&url=https%3A%2F%2Fapi.fluentci.io%2Fv1%2Fpipeline%2Frust_pipeline&query=%24.version)](https://pkg.fluentci.io/rust_pipeline) 4 | [![deno module](https://shield.deno.dev/x/rust_pipeline)](https://deno.land/x/rust_pipeline) 5 | ![deno compatibility](https://shield.deno.dev/deno/^1.37) 6 | [![](https://img.shields.io/codecov/c/gh/fluent-ci-templates/rust-pipeline)](https://codecov.io/gh/fluent-ci-templates/rust-pipeline) 7 | 8 | A ready-to-use CI/CD Pipeline for your Rust projects. 9 | ## 🚀 Usage 10 | 11 | Run the following command in your Rust Project: 12 | 13 | ```bash 14 | fluentci run rust_pipeline 15 | ``` 16 | 17 | Or if you want to run specific jobs: 18 | 19 | ```bash 20 | fluentci run rust_pipeline test build 21 | ``` 22 | 23 | 24 | if you want to use it as a template: 25 | 26 | ```bash 27 | fluentci init -t rust 28 | ``` 29 | 30 | This will create a `.fluentci` folder in your project. 31 | 32 | Now you can run the pipeline with: 33 | 34 | ```bash 35 | fluentci run . 36 | ``` 37 | 38 | ## Jobs 39 | 40 | | Job | Description | 41 | | ----- | ------------------ | 42 | | build | build your project | 43 | | test | Run your tests | 44 | 45 | ## Programmatic usage 46 | 47 | You can also use this pipeline programmatically: 48 | 49 | ```ts 50 | import { build, test } from "https://pkg.fluentci.io/rust_pipeline@v0.6.1/mod.ts"; 51 | 52 | await test(); 53 | await build(); 54 | ``` 55 | -------------------------------------------------------------------------------- /.fluentci/src/aws/README.md: -------------------------------------------------------------------------------- 1 | # AWS CodePipeline 2 | 3 | [![fluentci pipeline](https://img.shields.io/badge/dynamic/json?label=pkg.fluentci.io&labelColor=%23000&color=%23460cf1&url=https%3A%2F%2Fapi.fluentci.io%2Fv1%2Fpipeline%2Frust_pipeline&query=%24.version)](https://pkg.fluentci.io/rust_pipeline) 4 | [![deno module](https://shield.deno.dev/x/rust_pipeline)](https://deno.land/x/rust_pipeline) 5 | ![deno compatibility](https://shield.deno.dev/deno/^1.34) 6 | [![](https://img.shields.io/codecov/c/gh/fluent-ci-templates/rust-pipeline)](https://codecov.io/gh/fluent-ci-templates/rust-pipeline) 7 | 8 | The following command will generate a `buildspec.yml` file in your project: 9 | 10 | ```bash 11 | fluentci ac init -t rust_pipeline 12 | ``` 13 | 14 | Generated file: 15 | 16 | ```yaml 17 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_aws_codepipeline 18 | 19 | version: 0.2 20 | phases: 21 | install: 22 | commands: 23 | - curl -fsSL https://deno.land/x/install/install.sh | sh 24 | - export DENO_INSTALL="$HOME/.deno" 25 | - export PATH="$DENO_INSTALL/bin:$PATH" 26 | - deno install -A -r https://cli.fluentci.io -n fluentci 27 | - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 28 | - mv bin/dagger /usr/local/bin 29 | - dagger version 30 | build: 31 | commands: 32 | - fluentci run rust_pipeline test build 33 | post_build: 34 | commands: 35 | - echo Build completed on `date` 36 | 37 | ``` 38 | 39 | Feel free to edit the template generator at `.fluentci/src/aws/config.ts` to your needs. 40 | -------------------------------------------------------------------------------- /crates/cli/src/cmd/search.rs: -------------------------------------------------------------------------------- 1 | use crate::types::SearchArgs; 2 | use anyhow::{anyhow, Error}; 3 | use crosup_nix::search::{ 4 | client::NixPackagesClient, esclient::ElasticSearchClient, matchers::MatchName, query::Query, 5 | }; 6 | use owo_colors::OwoColorize; 7 | use spinners::{Spinner, Spinners}; 8 | 9 | pub async fn execute_search(args: SearchArgs) -> Result<(), Error> { 10 | let client = ElasticSearchClient::new() 11 | .map_err(|e| anyhow!("Failed to create ElasticSearch client: {}", e))?; 12 | let query = Query { 13 | channel: args.channel, 14 | name: Some(MatchName { name: args.package }), 15 | max_results: args.max_results, 16 | ..Default::default() 17 | }; 18 | 19 | let mut sp = Spinner::new(Spinners::Dots9, "Searching...".into()); 20 | let results = client.search(query).await?; 21 | sp.stop(); 22 | 23 | println!(""); 24 | 25 | for result in results { 26 | match result.package.description { 27 | Some(ref description) => { 28 | println!( 29 | "{} @ {} : {}", 30 | result.package.name.cyan().underline(), 31 | result.package.version.bright_green(), 32 | description 33 | ); 34 | } 35 | None => { 36 | println!( 37 | "{} @ {}", 38 | result.package.name.cyan().underline(), 39 | result.package.version.bright_green() 40 | ); 41 | } 42 | }; 43 | } 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml.disable: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: ["master"] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: ["master"] 9 | workflow_dispatch: 10 | branches: ["*"] 11 | 12 | jobs: 13 | unit-tests: 14 | name: Run unit tests 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout code 18 | uses: actions/checkout@v2 19 | 20 | - uses: actions/cache@v3 21 | with: 22 | path: | 23 | ~/.cargo/bin/ 24 | ~/.cargo/registry/index/ 25 | ~/.cargo/registry/cache/ 26 | ~/.cargo/git/db/ 27 | target/ 28 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 29 | 30 | - name: Install Rust toolchain 31 | uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af #@v1 32 | with: 33 | profile: minimal 34 | toolchain: stable 35 | components: llvm-tools-preview 36 | override: true 37 | 38 | - name: Install cargo-llvm-cov 39 | run: | 40 | if type cargo-llvm-cov >/dev/null 2>&1; then echo "cargo-llvm-cov is already installed"; else cargo install cargo-llvm-cov ; fi 41 | 42 | - name: Run cargo-llvm-cov 43 | run: | 44 | cargo llvm-cov --all-features --lib --workspace --lcov --output-path lcov.info 45 | 46 | - name: Upload coverage to Codecov 47 | uses: codecov/codecov-action@v3 48 | with: 49 | files: lcov.info 50 | token: ${{ secrets.CODECOV_TOKEN }} 51 | -------------------------------------------------------------------------------- /crates/cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Tsiry Sandratraina "] 3 | categories = ["command-line-utilities"] 4 | description = "A simple CLI tool to quickly setup your development environment on Chomebook (ChromeOS) or any Linux Distribution." 5 | edition = "2021" 6 | keywords = ["chromebook", "chromeos", "homebrew", "docker", "nix"] 7 | license = "MIT" 8 | name = "crosup" 9 | readme = "../../README.md" 10 | repository = "https://github.com/tsirysndr/crosup" 11 | version = "0.5.2" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | anyhow = "1.0.71" 17 | clap = "3.2.20" 18 | crosup-core = { path = "../core", version = "0.2.1" } 19 | crosup-entity = { version = "0.1.0", path = "../entity" } 20 | crosup-installers = { path = "../installers", version = "0.2.0" } 21 | crosup-migration = { path = "../migration", version = "0.1.0" } 22 | crosup-nix = { path = "../nix", version = "0.1.1" } 23 | crosup-repo = { path = "../repo", version = "0.1.0" } 24 | crosup-ssh = { path = "../ssh", version = "0.1.0" } 25 | crosup-tui = { path = "../tui", version = "0.1.0" } 26 | crosup-types = { path = "../types", version = "0.2.1" } 27 | hcl-rs = "0.14.2" 28 | inquire = "0.6.2" 29 | owo-colors = "3.5.0" 30 | sea-orm = { version = "0.11.3", features = [ 31 | "runtime-tokio-rustls", 32 | "sqlx-sqlite", 33 | ] } 34 | serde = "1.0.164" 35 | sha256 = "1.1.4" 36 | similar = { version = "2.2.1", features = ["inline"] } 37 | spinners = "4.1.0" 38 | ssh2 = { version = "0.9.4", features = ["vendored-openssl"] } 39 | tokio = { version = "1.28.2", features = ["full"] } 40 | toml = "0.7.4" 41 | -------------------------------------------------------------------------------- /crates/cli/src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! install { 2 | ($args: ident, $config: ident, $session: expr) => { 3 | match $args.tools { 4 | Some(ref tools) => { 5 | for tool_name in tools { 6 | let tool_name = tool_name.replace("ble.sh", "blesh"); 7 | let mut default_config = Configuration::default(); 8 | let (default_graph, default_installers) = 9 | build_installer_graph(&mut default_config, $session.clone()); 10 | 11 | let mut visited = vec![false; default_graph.size()]; 12 | if let Some(tool) = default_installers 13 | .into_iter() 14 | .find(|installer| installer.name() == tool_name) 15 | { 16 | default_graph.install(tool, &mut visited)?; 17 | continue; 18 | } 19 | 20 | let (graph, installers) = build_installer_graph(&mut $config, $session.clone()); 21 | let tool = installers 22 | .into_iter() 23 | .find(|installer| installer.name() == tool_name) 24 | .unwrap(); 25 | let mut visited = vec![false; graph.size()]; 26 | graph.install(tool, &mut visited)?; 27 | } 28 | } 29 | None => { 30 | let (graph, _) = build_installer_graph(&mut $config, $session.clone()); 31 | graph.install_all()?; 32 | } 33 | } 34 | }; 35 | } 36 | 37 | pub(crate) use install; 38 | -------------------------------------------------------------------------------- /.fluentci/import_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "@fluentci.io/dagger": "https://sdk.fluentci.io/v0.1.9/mod.ts", 4 | "@dagger.io/dagger": "https://esm.sh/v128/*@dagger.io/dagger@0.8.4", 5 | "graphql-tag": "https://esm.sh/v128/graphql-tag@2.12.6", 6 | "graphql-request": "https://esm.sh/v128/graphql-request@6.1.0", 7 | "fluent_gitlab_ci": "https://deno.land/x/fluent_gitlab_ci@v0.4.2/mod.ts", 8 | "fluent_github_actions": "https://deno.land/x/fluent_github_actions@v0.2.1/mod.ts", 9 | "fluent_circleci": "https://deno.land/x/fluent_circleci@v0.2.5/mod.ts", 10 | "fluent_azure_pipelines": "https://deno.land/x/fluent_azure_pipelines@v0.2.0/mod.ts", 11 | "fluent_aws_codepipeline": "https://deno.land/x/fluent_aws_codepipeline@v0.2.3/mod.ts", 12 | "crypto": "node:crypto", 13 | "fs": "node:fs", 14 | "os": "node:os", 15 | "path": "node:path", 16 | "process": "node:process", 17 | "readline": "node:readline", 18 | "url": "node:url" 19 | }, 20 | "scopes": { 21 | "https://esm.sh/v128/": { 22 | "@lifeomic/axios-fetch": "https://esm.sh/v128/@lifeomic/axios-fetch@3.0.1", 23 | "adm-zip": "https://esm.sh/v128/adm-zip@0.5.10", 24 | "env-paths": "https://esm.sh/v128/env-paths@3.0.0", 25 | "execa": "https://esm.sh/v128/execa@7.1.1", 26 | "graphql-request": "https://esm.sh/v128/graphql-request@6.1.0", 27 | "graphql-tag": "https://esm.sh/v128/graphql-tag@2.12.6", 28 | "graphql": "https://esm.sh/v128/graphql@16.7.1", 29 | "node-color-log": "https://esm.sh/v128/node-color-log@10.0.2", 30 | "node-fetch": "https://esm.sh/v128/node-fetch@3.3.1", 31 | "tar": "https://esm.sh/v128/tar@6.1.15" 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /.fluentci/src/azure/README.md: -------------------------------------------------------------------------------- 1 | # Azure Pipelines 2 | 3 | [![fluentci pipeline](https://img.shields.io/badge/dynamic/json?label=pkg.fluentci.io&labelColor=%23000&color=%23460cf1&url=https%3A%2F%2Fapi.fluentci.io%2Fv1%2Fpipeline%2Frust_pipeline&query=%24.version)](https://pkg.fluentci.io/rust_pipeline) 4 | [![deno module](https://shield.deno.dev/x/rust_pipeline)](https://deno.land/x/rust_pipeline) 5 | ![deno compatibility](https://shield.deno.dev/deno/^1.34) 6 | [![](https://img.shields.io/codecov/c/gh/fluent-ci-templates/rust-pipeline)](https://codecov.io/gh/fluent-ci-templates/rust-pipeline) 7 | 8 | The following command will generate a `azure-pipelines.yml` file in your project: 9 | 10 | ```bash 11 | fluentci ap init -t rust_pipeline 12 | ``` 13 | 14 | Generated file: 15 | 16 | ```yaml 17 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_azure_pipelines 18 | 19 | trigger: 20 | - main 21 | pool: 22 | name: Default 23 | vmImage: ubuntu-latest 24 | steps: 25 | - script: | 26 | curl -fsSL https://deno.land/x/install/install.sh | sh 27 | export DENO_INSTALL="$HOME/.deno" 28 | export PATH="$DENO_INSTALL/bin:$PATH" 29 | displayName: Install Deno 30 | - script: deno install -A -r https://cli.fluentci.io -n fluentci 31 | displayName: Setup Fluent CI CLI 32 | - script: | 33 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 34 | sudo mv bin/dagger /usr/local/bin 35 | dagger version 36 | displayName: Setup Dagger 37 | - script: fluentci run rust_pipeline test build 38 | displayName: Run Dagger Pipelines 39 | 40 | ``` 41 | 42 | Feel free to edit the template generator at `.fluentci/src/azure/config.ts` to your needs. 43 | -------------------------------------------------------------------------------- /.fluentci/src/gitlab/README.md: -------------------------------------------------------------------------------- 1 | # Gitlab CI 2 | 3 | [![fluentci pipeline](https://img.shields.io/badge/dynamic/json?label=pkg.fluentci.io&labelColor=%23000&color=%23460cf1&url=https%3A%2F%2Fapi.fluentci.io%2Fv1%2Fpipeline%2Frust_pipeline&query=%24.version)](https://pkg.fluentci.io/rust_pipeline) 4 | [![deno module](https://shield.deno.dev/x/rust_pipeline)](https://deno.land/x/rust_pipeline) 5 | ![deno compatibility](https://shield.deno.dev/deno/^1.34) 6 | [![](https://img.shields.io/codecov/c/gh/fluent-ci-templates/rust-pipeline)](https://codecov.io/gh/fluent-ci-templates/rust-pipeline) 7 | 8 | The following command will generate a `.gitlab-ci.yml` file in your project: 9 | 10 | ```bash 11 | fluentci gl init -t rust_pipeline 12 | ``` 13 | 14 | Generated file: 15 | 16 | ```yaml 17 | 18 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_gitlab_ci 19 | 20 | .docker: 21 | image: denoland/deno:alpine 22 | services: 23 | - docker:${DOCKER_VERSION}-dind 24 | variables: 25 | DOCKER_HOST: tcp://docker:2376 26 | DOCKER_TLS_VERIFY: "1" 27 | DOCKER_TLS_CERTDIR: /certs 28 | DOCKER_CERT_PATH: /certs/client 29 | DOCKER_DRIVER: overlay2 30 | DOCKER_VERSION: 20.10.16 31 | 32 | .dagger: 33 | extends: .docker 34 | before_script: 35 | - apk add docker-cli curl unzip 36 | - deno install -A -r https://cli.fluentci.io -n fluentci 37 | - curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 38 | - mv bin/dagger /usr/local/bin 39 | - dagger version 40 | 41 | tests: 42 | extends: .dagger 43 | script: 44 | - fluentci run rust_pipeline test build 45 | 46 | ``` 47 | 48 | Feel free to edit the template generator at `.fluentci/src/gitlab/config.ts` to your needs. 49 | -------------------------------------------------------------------------------- /.fluentci/src/circleci/README.md: -------------------------------------------------------------------------------- 1 | # Circle CI 2 | 3 | [![fluentci pipeline](https://img.shields.io/badge/dynamic/json?label=pkg.fluentci.io&labelColor=%23000&color=%23460cf1&url=https%3A%2F%2Fapi.fluentci.io%2Fv1%2Fpipeline%2Frust_pipeline&query=%24.version)](https://pkg.fluentci.io/rust_pipeline) 4 | [![deno module](https://shield.deno.dev/x/rust_pipeline)](https://deno.land/x/rust_pipeline) 5 | ![deno compatibility](https://shield.deno.dev/deno/^1.34) 6 | [![](https://img.shields.io/codecov/c/gh/fluent-ci-templates/rust-pipeline)](https://codecov.io/gh/fluent-ci-templates/rust-pipeline) 7 | 8 | 9 | The following command will generate a `.circleci/config.yml` file in your project: 10 | 11 | ```bash 12 | fluentci cci init -t rust_pipeline 13 | ``` 14 | 15 | Generated file: 16 | 17 | ```yaml 18 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_circleci 19 | 20 | version: 2.1 21 | jobs: 22 | tests: 23 | steps: 24 | - checkout 25 | - run: sudo apt-get update && sudo apt-get install -y curl unzip 26 | - run: | 27 | curl -fsSL https://deno.land/x/install/install.sh | sh 28 | export DENO_INSTALL="$HOME/.deno" 29 | export PATH="$DENO_INSTALL/bin:$PATH" 30 | - run: deno install -A -r https://cli.fluentci.io -n fluentci 31 | - run: | 32 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 33 | sudo mv bin/dagger /usr/local/bin 34 | dagger version 35 | - run: 36 | name: Upload Coverage 37 | command: fluentci run rust_pipeline test build 38 | machine: 39 | image: ubuntu-2004:2023.07.1 40 | workflows: 41 | dagger: 42 | jobs: 43 | - tests 44 | 45 | ``` 46 | 47 | Feel free to edit the template generator at `.fluentci/src/circleci/config.ts` to your needs. 48 | -------------------------------------------------------------------------------- /.fluentci/src/github/README.md: -------------------------------------------------------------------------------- 1 | # Github Actions 2 | 3 | [![fluentci pipeline](https://img.shields.io/badge/dynamic/json?label=pkg.fluentci.io&labelColor=%23000&color=%23460cf1&url=https%3A%2F%2Fapi.fluentci.io%2Fv1%2Fpipeline%2Frust_pipeline&query=%24.version)](https://pkg.fluentci.io/rust_pipeline) 4 | [![deno module](https://shield.deno.dev/x/rust_pipeline)](https://deno.land/x/rust_pipeline) 5 | ![deno compatibility](https://shield.deno.dev/deno/^1.34) 6 | [![](https://img.shields.io/codecov/c/gh/fluent-ci-templates/rust-pipeline)](https://codecov.io/gh/fluent-ci-templates/rust-pipeline) 7 | 8 | The following command will generate a `.github/workflows/tests.yml` file in your project: 9 | 10 | ```bash 11 | fluentci gh init -t rust_pipeline 12 | ``` 13 | 14 | Or, if you already have a `.fluentci` folder (generated from `fluentci init -t rust`) in your project: 15 | 16 | ```bash 17 | fluentci gh init 18 | ``` 19 | 20 | Generated file: 21 | 22 | ```yaml 23 | # Do not edit this file directly. It is generated by https://deno.land/x/fluent_github_actions 24 | 25 | name: Test 26 | on: 27 | push: 28 | branches: 29 | - main 30 | jobs: 31 | test: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - uses: actions/checkout@v2 35 | - uses: denoland/setup-deno@v1 36 | with: 37 | deno-version: v1.37 38 | - name: Setup Fluent CI CLI 39 | run: deno install -A -r https://cli.fluentci.io -n fluentci 40 | - name: Setup Dagger 41 | run: | 42 | curl -L https://dl.dagger.io/dagger/install.sh | DAGGER_VERSION=0.8.1 sh 43 | sudo mv bin/dagger /usr/local/bin 44 | dagger version 45 | - name: Run Tests and Build 46 | run: fluentci run rust_pipeline test build 47 | 48 | ``` 49 | 50 | Feel free to edit the template generator at `.fluentci/src/github/config.ts` to your needs. 51 | -------------------------------------------------------------------------------- /crates/cli/src/cmd/history.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::get_database_connection; 4 | use anyhow::Error; 5 | use crosup_core::config::verify_if_config_file_is_present; 6 | use crosup_repo::{file::FileRepo, modification::ModificationRepo}; 7 | use crosup_tui::{history::display_history, App}; 8 | use migration::MigratorTrait; 9 | use owo_colors::OwoColorize; 10 | use sea_orm::DatabaseConnection; 11 | 12 | pub async fn execute_history() -> Result<(), Error> { 13 | let (_, filename, _, _) = verify_if_config_file_is_present()?; 14 | 15 | let db: DatabaseConnection = get_database_connection().await?; 16 | 17 | migration::Migrator::up(&db, None).await?; 18 | 19 | let current_dir = std::env::current_dir()?; 20 | let path = format!("{}/{}", current_dir.display(), filename); 21 | 22 | let result = FileRepo::new(&db).find_by_path(&path).await?; 23 | 24 | if let Some(file) = result { 25 | let result = ModificationRepo::new(&db).find_by_file_id(file.id).await?; 26 | let mut content = HashMap::new(); 27 | let mut dates = HashMap::new(); 28 | let mut hashes = HashMap::new(); 29 | for (index, m) in result.iter().enumerate() { 30 | content.insert(index, m.content.clone()); 31 | dates.insert(index, m.timestamp); 32 | hashes.insert(index, m.hash.clone()); 33 | } 34 | 35 | let app = App { 36 | items: result 37 | .into_iter() 38 | .map(|m| format!("{} {} {}", m.timestamp.to_string(), filename, m.hash)) 39 | .collect(), 40 | content, 41 | selected_index: 0, 42 | title: filename, 43 | }; 44 | display_history(app)?; 45 | return Ok(()); 46 | } 47 | 48 | println!( 49 | "{} has not been modified, no history available", 50 | filename.bold().cyan() 51 | ); 52 | 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /crates/nix/src/search/query.rs: -------------------------------------------------------------------------------- 1 | use serde::Serialize; 2 | use serde_json::{json, Value}; 3 | 4 | use super::matchers::{MatchName, MatchProgram, MatchQueryString, MatchSearch, MatchVersion}; 5 | 6 | #[derive(Default, Serialize)] 7 | pub struct Query { 8 | pub max_results: u32, 9 | pub channel: String, 10 | pub flakes: bool, 11 | pub search: Option, 12 | pub program: Option, 13 | pub name: Option, 14 | pub version: Option, 15 | pub query_string: Option, 16 | } 17 | 18 | impl Query { 19 | pub fn payload(&self) -> Result { 20 | let mut must: Vec = vec![json!({ 21 | "match": { 22 | "type": "package" 23 | } 24 | })]; 25 | 26 | if let Some(search) = &self.search { 27 | must.push(json!(search.marshal_json()?)); 28 | } 29 | 30 | if let Some(name) = &self.name { 31 | must.push(json!(name.marshal_json()?)); 32 | } 33 | 34 | if let Some(program) = &self.program { 35 | must.push(json!(program.marshal_json()?)); 36 | } 37 | 38 | if let Some(version) = &self.version { 39 | must.push(json!(version.marshal_json()?)); 40 | } 41 | 42 | if let Some(query_string) = &self.query_string { 43 | must.push(json!(query_string.marshal_json()?)); 44 | } 45 | 46 | let request_body = json!({ 47 | "from": 0, 48 | "size": self.max_results, 49 | "sort": [ 50 | { 51 | "_score": "desc", 52 | "package_attr_name": "desc", 53 | "package_pversion": "desc" 54 | }, 55 | ], 56 | "query": { 57 | "bool": { 58 | "must": must 59 | } 60 | } 61 | }); 62 | 63 | serde_json::to_value(request_body) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /crates/core/src/config.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use owo_colors::OwoColorize; 3 | use std::path::Path; 4 | 5 | use crosup_types::{ 6 | configuration::Configuration, inventory::Inventory, CROSFILE_HCL, CROSFILE_TOML, INVENTORY_HCL, 7 | INVENTORY_TOML, 8 | }; 9 | 10 | pub fn verify_if_config_file_is_present() -> Result<(Configuration, String, String, bool), Error> { 11 | if !Path::new(CROSFILE_HCL).exists() && !Path::new(CROSFILE_TOML).exists() { 12 | let config = Configuration::default(); 13 | return Ok(( 14 | config.clone(), 15 | CROSFILE_HCL.into(), 16 | hcl::to_string(&config)?, 17 | false, 18 | )); 19 | } 20 | 21 | let current_dir = std::env::current_dir()?; 22 | 23 | if Path::new(CROSFILE_HCL).exists() { 24 | let config = std::fs::read_to_string(current_dir.join(CROSFILE_HCL))?; 25 | let content = config.clone(); 26 | let config = hcl::from_str(&config)?; 27 | return Ok((config, CROSFILE_HCL.into(), content, true)); 28 | } 29 | 30 | let config = std::fs::read_to_string(current_dir.join(CROSFILE_TOML))?; 31 | let content = config.clone(); 32 | let config = toml::from_str(&config)?; 33 | return Ok((config, CROSFILE_TOML.into(), content, true)); 34 | } 35 | 36 | pub fn verify_if_inventory_config_file_is_present() -> Result { 37 | if !Path::new(INVENTORY_HCL).exists() && !Path::new(INVENTORY_TOML).exists() { 38 | return Err(anyhow::anyhow!(format!( 39 | "Inventory file not found, please create one using {}", 40 | "crosup init --inventory".bright_green() 41 | ))); 42 | } 43 | 44 | let current_dir = std::env::current_dir()?; 45 | 46 | if Path::new(INVENTORY_HCL).exists() { 47 | let config = std::fs::read_to_string(current_dir.join(INVENTORY_HCL))?; 48 | let config = hcl::from_str(&config)?; 49 | return Ok(config); 50 | } 51 | 52 | let config = std::fs::read_to_string(current_dir.join(INVENTORY_TOML))?; 53 | let config = toml::from_str(&config)?; 54 | return Ok(config); 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/release-for-mac.yml.disable: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: [created] 4 | 5 | jobs: 6 | release: 7 | name: release ${{ matrix.target }} 8 | runs-on: macos-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | include: 13 | - target: x86_64-apple-darwin 14 | archive: tar.gz tar.xz 15 | extensions: tar.gz tar.gz.sha256 16 | - target: aarch64-apple-darwin 17 | archive: tar.gz tar.xz 18 | extensions: tar.gz tar.gz.sha256 19 | 20 | steps: 21 | - name: Setup Fluent CI 22 | uses: fluentci-io/setup-fluentci@v5 23 | - name: Installing Rust toolchain 24 | uses: actions-rs/toolchain@v1 25 | with: 26 | toolchain: stable 27 | target: ${{ matrix.target }} 28 | override: true 29 | - name: Checking out sources 30 | uses: actions/checkout@v1 31 | - name: Running cargo build 32 | uses: actions-rs/cargo@v1 33 | with: 34 | command: build 35 | toolchain: stable 36 | args: -p crosup --release --target ${{ matrix.target }} 37 | - name: Install aarch64-apple-darwin toolchain 38 | if: matrix.target == 'aarch64-apple-darwin' 39 | run: rustup target add aarch64-apple-darwin 40 | - name: Set env 41 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 42 | - name: Packaging final binary 43 | shell: bash 44 | run: | 45 | cd target/${{ matrix.target }}/release 46 | tar czvf ../../../crosup_${{ env.RELEASE_VERSION }}_${{ matrix.target }}.tar.gz crosup 47 | shasum -a 256 ../../../crosup_${{ env.RELEASE_VERSION }}_${{ matrix.target }}.tar.gz > ../../../crosup_${{ env.RELEASE_VERSION }}_${{ matrix.target }}.tar.gz.sha256 48 | cd ../../../ && rm -rf target 49 | - name: Upload release assets 50 | run: | 51 | rm -rf .fluentci/deno.lock 52 | for ext in tar.gz tar.gz.sha256; do 53 | export FILE=crosup_${{ env.RELEASE_VERSION }}_${{ matrix.target }}.$ext 54 | fluentci run github_pipeline release_upload 55 | done 56 | env: 57 | TAG: ${{ env.RELEASE_VERSION }} 58 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 59 | -------------------------------------------------------------------------------- /crates/cli/src/cmd/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt, fs}; 2 | 3 | use anyhow::Error; 4 | use owo_colors::{OwoColorize, Style}; 5 | use sea_orm::{Database, DatabaseConnection}; 6 | use similar::{ChangeTag, TextDiff}; 7 | 8 | pub mod add; 9 | pub mod diff; 10 | pub mod history; 11 | pub mod init; 12 | pub mod install; 13 | pub mod search; 14 | 15 | struct Line(Option); 16 | 17 | impl fmt::Display for Line { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | match self.0 { 20 | None => write!(f, " "), 21 | Some(idx) => write!(f, "{:>4}", idx + 1), 22 | } 23 | } 24 | } 25 | 26 | pub async fn get_database_connection() -> Result { 27 | let home = std::env::var("HOME").unwrap(); 28 | let crosup_dir = format!("{}/.config/crosup", home); 29 | 30 | fs::create_dir_all(&crosup_dir)?; 31 | 32 | let database_url = format!("sqlite:{}/modifications.sqlite3?mode=rwc", crosup_dir); 33 | 34 | let db: DatabaseConnection = Database::connect(&database_url).await?; 35 | Ok(db) 36 | } 37 | 38 | pub fn print_diff(previous: &str, current: &str) { 39 | let diff = TextDiff::from_lines(previous, current); 40 | 41 | for (idx, group) in diff.grouped_ops(3).iter().enumerate() { 42 | if idx > 0 { 43 | println!("{:-^1$}", "-", 80); 44 | } 45 | for op in group { 46 | for change in diff.iter_inline_changes(op) { 47 | let (sign, style) = match change.tag() { 48 | ChangeTag::Delete => ("-", Style::new().red().bold()), 49 | ChangeTag::Insert => ("+", Style::new().green().bold()), 50 | ChangeTag::Equal => (" ", Style::new()), 51 | }; 52 | print!("{}|{}", Line(change.new_index()), style.style(sign),); 53 | for (emphasized, value) in change.iter_strings_lossy() { 54 | if emphasized { 55 | print!("{}", style.style(value).underline()); 56 | continue; 57 | } 58 | print!("{}", style.style(value)); 59 | } 60 | if change.missing_newline() { 61 | println!(); 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /crates/cli/src/cmd/add.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | 3 | use anyhow::Error; 4 | use crosup_core::{config::verify_if_config_file_is_present, graph::build_installer_graph}; 5 | use crosup_types::configuration::Configuration; 6 | use owo_colors::OwoColorize; 7 | 8 | use crate::{cmd::print_diff, macros::install, types::InstallArgs}; 9 | 10 | pub async fn execute_add(tools: Vec, ask: bool) -> Result<(), Error> { 11 | let (mut current_config, filename, content, is_present) = verify_if_config_file_is_present()?; 12 | 13 | current_config.packages = match current_config.packages { 14 | Some(ref mut packages) => { 15 | tools.iter().for_each(|x| { 16 | if !packages.contains(x) { 17 | packages.push(x.clone()) 18 | } 19 | }); 20 | Some(packages.clone()) 21 | } 22 | None => Some(tools.clone()), 23 | }; 24 | let new_content = match filename.ends_with(".hcl") { 25 | true => hcl::to_string(¤t_config)?, 26 | false => toml::to_string(¤t_config)?, 27 | }; 28 | 29 | print_diff(&content, &new_content); 30 | 31 | if ask { 32 | ask_confirmation(ask, tools.clone()); 33 | } 34 | 35 | let args = InstallArgs { 36 | ..Default::default() 37 | }; 38 | 39 | install!(args, current_config, None); 40 | 41 | if is_present { 42 | fs::write(filename, new_content)?; 43 | } 44 | 45 | Ok(()) 46 | } 47 | 48 | fn ask_confirmation(ask: bool, tools: Vec) { 49 | if ask { 50 | println!("\n-> The following packages will be added to your configuration:"); 51 | 52 | for tool in tools.iter() { 53 | println!(" - {}", tool.bright_green()); 54 | } 55 | 56 | match tools.len() { 57 | 1 => println!("-> Are you sure you want to install this package? [y/N]"), 58 | _ => println!( 59 | "-> Are you sure you want to install these {} packages? [y/N]", 60 | tools.len().bold().cyan() 61 | ), 62 | }; 63 | let mut input = String::new(); 64 | std::io::stdin() 65 | .read_line(&mut input) 66 | .expect("Failed to read line"); 67 | match input.trim() { 68 | "y" | "Y" => {} 69 | _ => std::process::exit(0), 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /crates/repo/src/modification.rs: -------------------------------------------------------------------------------- 1 | use crosup_entity::modification; 2 | use sea_orm::{ 3 | ActiveModelTrait, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QueryOrder, 4 | Set, 5 | }; 6 | 7 | pub struct ModificationRepo { 8 | db: DatabaseConnection, 9 | } 10 | 11 | impl ModificationRepo { 12 | pub fn new(db: &DatabaseConnection) -> Self { 13 | Self { db: db.clone() } 14 | } 15 | 16 | pub async fn find(&self, id: i32) -> Result, DbErr> { 17 | modification::Entity::find_by_id(id).one(&self.db).await 18 | } 19 | 20 | pub async fn find_by_hash(&self, hash: &str) -> Result, DbErr> { 21 | modification::Entity::find() 22 | .filter(modification::Column::Hash.eq(hash)) 23 | .one(&self.db) 24 | .await 25 | } 26 | 27 | pub async fn find_by_file_id(&self, file_id: i32) -> Result, DbErr> { 28 | modification::Entity::find() 29 | .filter(modification::Column::FileId.eq(file_id)) 30 | .order_by_desc(modification::Column::Timestamp) 31 | .all(&self.db) 32 | .await 33 | } 34 | 35 | pub async fn find_last_by_file_id( 36 | &self, 37 | file_id: i32, 38 | ) -> Result, DbErr> { 39 | modification::Entity::find() 40 | .filter(modification::Column::FileId.eq(file_id)) 41 | .order_by_desc(modification::Column::Timestamp) 42 | .one(&self.db) 43 | .await 44 | } 45 | 46 | pub async fn create( 47 | &self, 48 | file_id: i32, 49 | hash: &str, 50 | content: &str, 51 | ) -> Result { 52 | let result = self.find_by_hash(hash).await?; 53 | 54 | if result.is_some() { 55 | return Ok(result.unwrap()); 56 | } 57 | 58 | let last_modification = self.find_last_by_file_id(file_id).await?; 59 | let previous_id = last_modification.map(|m| m.id); 60 | 61 | modification::ActiveModel { 62 | file_id: Set(file_id), 63 | hash: Set(hash.to_owned()), 64 | content: Set(content.to_owned()), 65 | previous_id: Set(previous_id), 66 | ..Default::default() 67 | } 68 | .insert(&self.db) 69 | .await 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /crates/nix/src/search/esclient.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use surf::{http::auth::BasicAuth, Client, Config, Url}; 4 | 5 | use super::{client::NixPackagesClient, query::Query, types::Hit}; 6 | 7 | // Taken from the upstream repository 8 | // https://github.com/NixOS/nixos-search/blob/main/frontend/src/index.js 9 | pub const ELASTICSEARCH_USERNAME: &str = "aWVSALXpZv"; 10 | pub const ELASTICSEARCH_PASSWORD: &str = "X8gPHnzL52wFEekuxsfQ9cSh"; 11 | pub const ELASTICSEARCH_BASE_URL: &str = 12 | "https://nixos-search-7-1733963800.us-east-1.bonsaisearch.net:443/"; 13 | pub const ELASTICSEARCH_INDEX_PREFIX: &str = "latest-*-"; 14 | 15 | pub struct ElasticSearchClient { 16 | pub client: Client, 17 | } 18 | 19 | impl ElasticSearchClient { 20 | pub fn new() -> Result { 21 | let auth = BasicAuth::new(ELASTICSEARCH_USERNAME, ELASTICSEARCH_PASSWORD); 22 | let client = Config::new() 23 | .set_base_url(Url::parse(ELASTICSEARCH_BASE_URL).unwrap()) 24 | .set_timeout(Some(Duration::from_secs(5))) 25 | .add_header("Content-Type", "application/json")? 26 | .add_header("Accept", "application/json")? 27 | .add_header(auth.name(), auth.value())? 28 | .try_into()?; 29 | Ok(Self { client }) 30 | } 31 | } 32 | 33 | #[async_trait::async_trait] 34 | impl NixPackagesClient for ElasticSearchClient { 35 | async fn search(&self, query: Query) -> Result, anyhow::Error> { 36 | let body = query.payload()?; 37 | let response = self 38 | .client 39 | .post(format!( 40 | "{}nixos-{}/_search", 41 | ELASTICSEARCH_INDEX_PREFIX, query.channel 42 | )) 43 | .body(body) 44 | .recv_json::() 45 | .await 46 | .map_err(|e| anyhow::anyhow!(e))?; 47 | Ok(response.hits.hits) 48 | } 49 | } 50 | 51 | #[cfg(test)] 52 | mod tests { 53 | use crate::search::matchers::MatchName; 54 | 55 | use super::*; 56 | 57 | #[tokio::test] 58 | async fn test_search() { 59 | let client = ElasticSearchClient::new().unwrap(); 60 | let query = Query { 61 | channel: "unstable".to_string(), 62 | name: Some(MatchName { name: "vim".into() }), 63 | max_results: 10, 64 | ..Default::default() 65 | }; 66 | let packages = client.search(query).await.unwrap(); 67 | assert!(packages.len() > 0); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /crates/nix/tests/home.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | # Home Manager needs a bit of information about you and the paths it should 5 | # manage. 6 | home.username = "tsirysndr"; 7 | home.homeDirectory = "/home/tsirysndr"; 8 | 9 | # This value determines the Home Manager release that your configuration is 10 | # compatible with. This helps avoid breakage when a new Home Manager release 11 | # introduces backwards incompatible changes. 12 | # 13 | # You should not change this value, even if you update Home Manager. If you do 14 | # want to update the value, then make sure to first check the Home Manager 15 | # release notes. 16 | home.stateVersion = "23.05"; # Please read the comment before changing. 17 | 18 | # The home.packages option allows you to install Nix packages into your 19 | # environment. 20 | home.packages = [ 21 | # # Adds the 'hello' command to your environment. It prints a friendly 22 | # # "Hello, world!" when run. 23 | pkgs.hello 24 | 25 | # # It is sometimes useful to fine-tune packages, for example, by applying 26 | # # overrides. You can do that directly here, just don't forget the 27 | # # parentheses. Maybe you want to install Nerd Fonts with a limited number of 28 | # # fonts? 29 | # (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; }) 30 | 31 | # # You can also create simple shell scripts directly inside your 32 | # # configuration. For example, this adds a command 'my-hello' to your 33 | # # environment: 34 | # (pkgs.writeShellScriptBin "my-hello" '' 35 | # echo "Hello, ${config.home.username}!" 36 | # '') 37 | ]; 38 | 39 | # Home Manager is pretty good at managing dotfiles. The primary way to manage 40 | # plain files is through 'home.file'. 41 | home.file = { 42 | # # Building this configuration will create a copy of 'dotfiles/screenrc' in 43 | # # the Nix store. Activating the configuration will then make '~/.screenrc' a 44 | # # symlink to the Nix store copy. 45 | # ".screenrc".source = dotfiles/screenrc; 46 | 47 | # # You can also set the file content immediately. 48 | # ".gradle/gradle.properties".text = '' 49 | # org.gradle.console=verbose 50 | # org.gradle.daemon.idletimeout=3600000 51 | # ''; 52 | }; 53 | 54 | # You can also manage environment variables but you will have to manually 55 | # source 56 | # 57 | # ~/.nix-profile/etc/profile.d/hm-session-vars.sh 58 | # 59 | # or 60 | # 61 | # /etc/profiles/per-user/tsirysndr/etc/profile.d/hm-session-vars.sh 62 | # 63 | # if you don't want to manage your shell through Home Manager. 64 | home.sessionVariables = { 65 | # EDITOR = "emacs"; 66 | }; 67 | 68 | # Let Home Manager install and manage itself. 69 | programs.home-manager.enable = true; 70 | } 71 | -------------------------------------------------------------------------------- /crates/nix/tests/home-with-vim.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | # Home Manager needs a bit of information about you and the paths it should 5 | # manage. 6 | home.username = "tsirysndr"; 7 | home.homeDirectory = "/home/tsirysndr"; 8 | 9 | # This value determines the Home Manager release that your configuration is 10 | # compatible with. This helps avoid breakage when a new Home Manager release 11 | # introduces backwards incompatible changes. 12 | # 13 | # You should not change this value, even if you update Home Manager. If you do 14 | # want to update the value, then make sure to first check the Home Manager 15 | # release notes. 16 | home.stateVersion = "23.05"; # Please read the comment before changing. 17 | 18 | # The home.packages option allows you to install Nix packages into your 19 | # environment. 20 | home.packages = [ 21 | # # Adds the 'hello' command to your environment. It prints a friendly 22 | # # "Hello, world!" when run. 23 | pkgs.hello 24 | 25 | # # It is sometimes useful to fine-tune packages, for example, by applying 26 | # # overrides. You can do that directly here, just don't forget the 27 | # # parentheses. Maybe you want to install Nerd Fonts with a limited number of 28 | # # fonts? 29 | # (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; }) 30 | 31 | # # You can also create simple shell scripts directly inside your 32 | # # configuration. For example, this adds a command 'my-hello' to your 33 | # # environment: 34 | # (pkgs.writeShellScriptBin "my-hello" '' 35 | # echo "Hello, ${config.home.username}!" 36 | # '') 37 | pkgs.vim 38 | ]; 39 | 40 | # Home Manager is pretty good at managing dotfiles. The primary way to manage 41 | # plain files is through 'home.file'. 42 | home.file = { 43 | # # Building this configuration will create a copy of 'dotfiles/screenrc' in 44 | # # the Nix store. Activating the configuration will then make '~/.screenrc' a 45 | # # symlink to the Nix store copy. 46 | # ".screenrc".source = dotfiles/screenrc; 47 | 48 | # # You can also set the file content immediately. 49 | # ".gradle/gradle.properties".text = '' 50 | # org.gradle.console=verbose 51 | # org.gradle.daemon.idletimeout=3600000 52 | # ''; 53 | }; 54 | 55 | # You can also manage environment variables but you will have to manually 56 | # source 57 | # 58 | # ~/.nix-profile/etc/profile.d/hm-session-vars.sh 59 | # 60 | # or 61 | # 62 | # /etc/profiles/per-user/tsirysndr/etc/profile.d/hm-session-vars.sh 63 | # 64 | # if you don't want to manage your shell through Home Manager. 65 | home.sessionVariables = { 66 | # EDITOR = "emacs"; 67 | }; 68 | 69 | # Let Home Manager install and manage itself. 70 | programs.home-manager.enable = true; 71 | } 72 | -------------------------------------------------------------------------------- /crates/types/src/nix.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct NixConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub pkg: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Package { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | 15 | #[serde(skip_serializing_if = "Option::is_none")] 16 | pub impure: Option, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub experimental_features: Option, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub accept_flake_config: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub preinstall: Option, 26 | pub flake: String, 27 | 28 | #[serde(skip_serializing_if = "Option::is_none")] 29 | pub depends_on: Option>, 30 | 31 | #[serde(skip_serializing_if = "Option::is_none")] 32 | pub version_check: Option, 33 | } 34 | 35 | pub fn default_nix_install() -> IndexMap { 36 | let mut pkg = IndexMap::new(); 37 | pkg.insert("flox".into(), 38 | Package { 39 | name: "flox".into(), 40 | impure: Some(true), 41 | experimental_features: Some("nix-command flakes".into()), 42 | accept_flake_config: Some(true), 43 | flake: "github:flox/floxpkgs#flox.fromCatalog".into(), 44 | preinstall: Some("echo 'extra-trusted-substituters = https://cache.floxdev.com' | sudo tee -a /etc/nix/nix.conf && echo 'extra-trusted-public-keys = flox-store-public-0:8c/B+kjIaQ+BloCmNkRUKwaVPFWkriSAd0JJvuDu4F0=' | sudo tee -a /etc/nix/nix.conf".into()), 45 | ..Default::default() 46 | } 47 | ); 48 | 49 | pkg.insert( 50 | "cachix".into(), 51 | Package { 52 | name: "cachix".into(), 53 | flake: "github:cachix/cachix".into(), 54 | ..Default::default() 55 | }, 56 | ); 57 | 58 | pkg.insert( 59 | "devenv".into(), 60 | Package { 61 | name: "devenv".into(), 62 | accept_flake_config: Some(true), 63 | flake: "github:cachix/devenv/latest".into(), 64 | preinstall: Some( 65 | r#"echo "trusted-users = root $USER" | sudo tee -a /etc/nix/nix.conf 66 | sudo pkill nix-daemon 67 | cachix use devenv"# 68 | .into(), 69 | ), 70 | depends_on: Some(vec!["cachix".into()]), 71 | ..Default::default() 72 | }, 73 | ); 74 | 75 | let mut nix = IndexMap::new(); 76 | nix.insert("install".into(), NixConfiguration { pkg }); 77 | nix 78 | } 79 | -------------------------------------------------------------------------------- /crates/nix/tests/home-with-vim-git.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | # Home Manager needs a bit of information about you and the paths it should 5 | # manage. 6 | home.username = "tsirysndr"; 7 | home.homeDirectory = "/home/tsirysndr"; 8 | 9 | # This value determines the Home Manager release that your configuration is 10 | # compatible with. This helps avoid breakage when a new Home Manager release 11 | # introduces backwards incompatible changes. 12 | # 13 | # You should not change this value, even if you update Home Manager. If you do 14 | # want to update the value, then make sure to first check the Home Manager 15 | # release notes. 16 | home.stateVersion = "23.05"; # Please read the comment before changing. 17 | 18 | # The home.packages option allows you to install Nix packages into your 19 | # environment. 20 | home.packages = [ 21 | # # Adds the 'hello' command to your environment. It prints a friendly 22 | # # "Hello, world!" when run. 23 | pkgs.hello 24 | 25 | # # It is sometimes useful to fine-tune packages, for example, by applying 26 | # # overrides. You can do that directly here, just don't forget the 27 | # # parentheses. Maybe you want to install Nerd Fonts with a limited number of 28 | # # fonts? 29 | # (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; }) 30 | 31 | # # You can also create simple shell scripts directly inside your 32 | # # configuration. For example, this adds a command 'my-hello' to your 33 | # # environment: 34 | # (pkgs.writeShellScriptBin "my-hello" '' 35 | # echo "Hello, ${config.home.username}!" 36 | # '') 37 | pkgs.vim 38 | pkgs.git 39 | ]; 40 | 41 | # Home Manager is pretty good at managing dotfiles. The primary way to manage 42 | # plain files is through 'home.file'. 43 | home.file = { 44 | # # Building this configuration will create a copy of 'dotfiles/screenrc' in 45 | # # the Nix store. Activating the configuration will then make '~/.screenrc' a 46 | # # symlink to the Nix store copy. 47 | # ".screenrc".source = dotfiles/screenrc; 48 | 49 | # # You can also set the file content immediately. 50 | # ".gradle/gradle.properties".text = '' 51 | # org.gradle.console=verbose 52 | # org.gradle.daemon.idletimeout=3600000 53 | # ''; 54 | }; 55 | 56 | # You can also manage environment variables but you will have to manually 57 | # source 58 | # 59 | # ~/.nix-profile/etc/profile.d/hm-session-vars.sh 60 | # 61 | # or 62 | # 63 | # /etc/profiles/per-user/tsirysndr/etc/profile.d/hm-session-vars.sh 64 | # 65 | # if you don't want to manage your shell through Home Manager. 66 | home.sessionVariables = { 67 | # EDITOR = "emacs"; 68 | }; 69 | 70 | # Let Home Manager install and manage itself. 71 | programs.home-manager.enable = true; 72 | } 73 | -------------------------------------------------------------------------------- /crates/cli/src/cmd/init.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use anyhow::Error; 4 | use inquire::Confirm; 5 | use owo_colors::OwoColorize; 6 | 7 | use crosup_types::{ 8 | configuration::{ConfigFormat, Configuration}, 9 | inventory::Inventory, 10 | }; 11 | 12 | pub fn execute_init( 13 | cfg_format: ConfigFormat, 14 | inventory: bool, 15 | packages: Option>, 16 | ) -> Result<(), Error> { 17 | let ext = match cfg_format { 18 | ConfigFormat::HCL => "hcl", 19 | ConfigFormat::TOML => "toml", 20 | }; 21 | 22 | let filename = match inventory { 23 | true => format!("Inventory.{}", ext), 24 | false => format!("Crosfile.{}", ext), 25 | }; 26 | 27 | if std::path::Path::new(&filename).exists() { 28 | let answer = Confirm::new( 29 | format!( 30 | "A {} file already exists in this directory, do you want to overwrite it?", 31 | filename.bright_green() 32 | ) 33 | .as_str(), 34 | ) 35 | .with_default(false) 36 | .with_help_message("Press y to overwrite the file or n to exit") 37 | .prompt(); 38 | if answer.is_err() || !answer.unwrap() { 39 | println!("Exiting..."); 40 | return Ok(()); 41 | } 42 | } 43 | 44 | if inventory { 45 | let inventory = Inventory::default(); 46 | let serialized = match cfg_format { 47 | ConfigFormat::HCL => hcl::to_string(&inventory).unwrap(), 48 | ConfigFormat::TOML => toml::to_string_pretty(&inventory).unwrap(), 49 | }; 50 | 51 | let mut file = std::fs::File::create(&filename).unwrap(); 52 | file.write_all(serialized.as_bytes()).unwrap(); 53 | println!("Created {} ✨", filename.bright_green()); 54 | return Ok(()); 55 | } 56 | 57 | let config = match packages { 58 | Some(packages) => Configuration { 59 | packages: Some(packages), 60 | install: None, 61 | brew: None, 62 | apt: None, 63 | pacman: None, 64 | git: None, 65 | nix: None, 66 | curl: None, 67 | yum: None, 68 | dnf: None, 69 | zypper: None, 70 | apk: None, 71 | emerge: None, 72 | slackpkg: None, 73 | fleek: None, 74 | }, 75 | None => Configuration::default(), 76 | }; 77 | 78 | let serialized = match cfg_format { 79 | ConfigFormat::HCL => hcl::to_string(&config).unwrap(), 80 | ConfigFormat::TOML => toml::to_string_pretty(&config).unwrap(), 81 | }; 82 | 83 | let mut file = std::fs::File::create(&filename).unwrap(); 84 | file.write_all(serialized.as_bytes()).unwrap(); 85 | println!("Created {} ✨", filename.bright_green()); 86 | 87 | Ok(()) 88 | } 89 | -------------------------------------------------------------------------------- /crates/nix/src/search/types.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Serialize, Deserialize)] 4 | pub struct Response { 5 | pub error: Option, 6 | pub status: Option, 7 | pub hits: Hits, 8 | } 9 | 10 | #[derive(Debug, Serialize, Deserialize)] 11 | pub struct Hits { 12 | pub hits: Vec, 13 | } 14 | 15 | #[derive(Debug, Serialize, Deserialize)] 16 | pub struct ResponseError { 17 | pub error: Error, 18 | pub status: i32, 19 | } 20 | 21 | #[derive(Debug, Serialize, Deserialize)] 22 | pub struct Error { 23 | pub r#type: String, 24 | pub reason: String, 25 | #[serde(rename = "resource.type")] 26 | pub resource_type: String, 27 | #[serde(rename = "resource.id")] 28 | pub resource_id: String, 29 | } 30 | 31 | #[derive(Debug, Serialize, Deserialize)] 32 | pub struct Hit { 33 | #[serde(rename = "_id")] 34 | pub id: String, 35 | #[serde(rename = "_source")] 36 | pub package: Package, 37 | } 38 | 39 | #[derive(Debug, Serialize, Deserialize)] 40 | pub struct License { 41 | #[serde(rename = "fullName")] 42 | pub full_name: String, 43 | #[serde(skip_serializing_if = "Option::is_none")] 44 | pub url: Option, 45 | } 46 | 47 | #[derive(Debug, Serialize, Deserialize)] 48 | pub struct FlakeResolved { 49 | pub r#type: String, 50 | pub owner: String, 51 | pub repo: String, 52 | pub url: String, 53 | } 54 | 55 | #[derive(Debug, Serialize, Deserialize)] 56 | pub struct Package { 57 | pub r#type: String, 58 | #[serde(rename = "package_pname")] 59 | pub name: String, 60 | #[serde(rename = "package_attr_name")] 61 | pub attr_name: String, 62 | #[serde(rename = "package_attr_set")] 63 | pub attr_set: String, 64 | #[serde(rename = "package_outputs")] 65 | pub outputs: Vec, 66 | #[serde( 67 | rename = "package_description", 68 | skip_serializing_if = "Option::is_none" 69 | )] 70 | pub description: Option, 71 | #[serde(rename = "package_programs")] 72 | pub programs: Vec, 73 | #[serde(rename = "package_homepage")] 74 | pub homepage: Vec, 75 | #[serde(rename = "package_pversion")] 76 | pub version: String, 77 | #[serde(rename = "package_platforms")] 78 | pub platforms: Vec, 79 | #[serde(rename = "package_position")] 80 | pub position: String, 81 | #[serde(rename = "package_license")] 82 | pub licenses: Vec, 83 | #[serde(rename = "flake_name", skip_serializing_if = "Option::is_none")] 84 | pub flake_name: Option, 85 | #[serde(rename = "flake_description", skip_serializing_if = "Option::is_none")] 86 | pub flake_description: Option, 87 | #[serde(rename = "flake_resolved", skip_serializing_if = "Option::is_none")] 88 | pub flake_resolved: Option, 89 | } 90 | -------------------------------------------------------------------------------- /crates/types/src/git.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use os_release::OsRelease; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 6 | pub struct GitConfiguration { 7 | #[serde(serialize_with = "hcl::ser::labeled_block")] 8 | pub repo: IndexMap, 9 | } 10 | 11 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 12 | pub struct Repository { 13 | #[serde(skip_serializing, skip_deserializing)] 14 | pub name: String, 15 | pub url: String, 16 | pub install: String, 17 | 18 | #[serde(skip_serializing_if = "Option::is_none")] 19 | pub preinstall: Option, 20 | 21 | #[serde(skip_serializing_if = "Option::is_none")] 22 | pub postinstall: Option, 23 | 24 | #[serde(skip_serializing_if = "Option::is_none")] 25 | pub install_check: Option, 26 | 27 | #[serde(skip_serializing_if = "Option::is_none")] 28 | pub recursive: Option, 29 | 30 | #[serde(skip_serializing_if = "Option::is_none")] 31 | pub depth: Option, 32 | 33 | #[serde(skip_serializing_if = "Option::is_none")] 34 | pub shallow_submodules: Option, 35 | 36 | #[serde(skip_serializing_if = "Option::is_none")] 37 | pub depends_on: Option>, 38 | } 39 | 40 | pub fn default_git_install() -> IndexMap { 41 | let mut repo = IndexMap::new(); 42 | 43 | let mut blesh = Repository { 44 | name: "blesh".into(), 45 | url: "https://github.com/akinomyoga/ble.sh.git".into(), 46 | install: "make -C ble.sh install PREFIX=~/.local".into(), 47 | preinstall: None, 48 | postinstall: Some("echo 'source ~/.local/share/blesh/ble.sh' >> ~/.bashrc".into()), 49 | install_check: Some("~/.local/share/blesh/ble.sh".into()), 50 | recursive: Some(true), 51 | depth: Some(1), 52 | shallow_submodules: Some(true), 53 | depends_on: None, 54 | }; 55 | 56 | if cfg!(target_os = "linux") { 57 | // determine linux distribution using os-release 58 | if let Ok(os_release) = OsRelease::new() { 59 | let os = os_release.id.to_lowercase(); 60 | let os = os.as_str(); 61 | match os { 62 | "ubuntu" | "debian" | "linuxmint" | "pop" | "elementary" | "zorin" => { 63 | blesh.preinstall = Some("sudo apt-get install -y gawk build-essential".into()); 64 | } 65 | _ => {} 66 | } 67 | } 68 | } 69 | 70 | if cfg!(target_os = "macos") { 71 | blesh.preinstall = Some("brew install gawk bash".into()); 72 | blesh.depends_on = Some(vec!["homebrew".into()]); 73 | } 74 | 75 | repo.insert("blesh".into(), blesh); 76 | let mut git = IndexMap::new(); 77 | git.insert("install".into(), GitConfiguration { repo }); 78 | git 79 | } 80 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. 49 | 50 | 51 | ## Licensing 52 | 53 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 54 | -------------------------------------------------------------------------------- /.fluentci/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug 4 | report, new feature, correction, or additional documentation, we greatly value 5 | feedback and contributions from our community. 6 | 7 | Please read through this document before submitting any issues or pull requests 8 | to ensure we have all the necessary information to effectively respond to your 9 | bug report or contribution. 10 | 11 | ## Reporting Bugs/Feature Requests 12 | 13 | We welcome you to use the GitHub issue tracker to report bugs or suggest 14 | features. 15 | 16 | When filing an issue, please check existing open, or recently closed, issues to 17 | make sure somebody else hasn't already reported the issue. Please try to include 18 | as much information as you can. Details like these are incredibly useful: 19 | 20 | - A reproducible test case or series of steps 21 | - The version of our code being used 22 | - Any modifications you've made relevant to the bug 23 | - Anything unusual about your environment or deployment 24 | 25 | ## Contributing via Pull Requests 26 | 27 | Contributions via pull requests are much appreciated. Before sending us a pull 28 | request, please ensure that: 29 | 30 | 1. You are working against the latest source on the _master_ branch. 31 | 2. You check existing open, and recently merged, pull requests to make sure 32 | someone else hasn't addressed the problem already. 33 | 3. You open an issue to discuss any significant work - we would hate for your 34 | time to be wasted. 35 | 36 | To send us a pull request, please: 37 | 38 | 1. Fork the repository. 39 | 2. Modify the source; please focus on the specific change you are contributing. 40 | If you also reformat all the code, it will be hard for us to focus on your 41 | change. 42 | 3. Ensure local tests pass. 43 | 4. Commit to your fork using clear commit messages. 44 | 5. Send us a pull request, answering any default questions in the pull request 45 | interface. 46 | 6. Pay attention to any automated CI failures reported in the pull request, and 47 | stay involved in the conversation. 48 | 49 | GitHub provides additional document on 50 | [forking a repository](https://help.github.com/articles/fork-a-repo/) and 51 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 52 | 53 | ## Finding contributions to work on 54 | 55 | Looking at the existing issues is a great way to find something to contribute 56 | on. As our projects, by default, use the default GitHub issue labels 57 | (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 58 | 'help wanted' issues is a great place to start. 59 | 60 | ## Code of Conduct 61 | 62 | This project has adopted the 63 | [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1, 64 | available at 65 | https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. 66 | 67 | ## Licensing 68 | 69 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to 70 | confirm the licensing of your contribution. 71 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly MAGENTA="$(tput setaf 5 2>/dev/null || echo '')" 4 | readonly GREEN="$(tput setaf 2 2>/dev/null || echo '')" 5 | readonly CYAN="$(tput setaf 6 2>/dev/null || echo '')" 6 | readonly NO_COLOR="$(tput sgr0 2>/dev/null || echo '')" 7 | 8 | # Define the release information 9 | RELEASE_URL="https://api.github.com/repos/tsirysndr/crosup/releases/latest" 10 | 11 | # Determine the operating system 12 | OS=$(uname -s) 13 | if [ "$OS" = "Darwin" ]; then 14 | # Determine the CPU architecture 15 | ARCH=$(uname -m) 16 | if [ "$ARCH" = "arm64" ]; then 17 | ASSET_NAME="_aarch64-apple-darwin.tar.gz" 18 | else 19 | ASSET_NAME="_x86_64-apple-darwin.tar.gz" 20 | fi 21 | elif [ "$OS" = "Linux" ]; then 22 | # Determine the CPU architecture 23 | ARCH=$(uname -m) 24 | if [ "$ARCH" = "aarch64" ]; then 25 | ASSET_NAME="_aarch64-unknown-linux-gnu.tar.gz" 26 | elif [ "$ARCH" = "x86_64" ]; then 27 | ASSET_NAME="_x86_64-unknown-linux-gnu.tar.gz" 28 | else 29 | echo "Unsupported architecture: $ARCH" 30 | exit 1 31 | fi 32 | else 33 | echo "Unsupported operating system: $OS" 34 | exit 1 35 | fi 36 | 37 | # Retrieve the download URL for the desired asset 38 | DOWNLOAD_URL=$(curl -sSL $RELEASE_URL | grep -o "browser_download_url.*$ASSET_NAME\"" | cut -d ' ' -f 2) 39 | 40 | ASSET_NAME=$(basename $DOWNLOAD_URL) 41 | 42 | # Define the installation directory 43 | INSTALL_DIR="/usr/local/bin" 44 | 45 | DOWNLOAD_URL=`echo $DOWNLOAD_URL | tr -d '\"'` 46 | 47 | # Download the asset 48 | curl -SL $DOWNLOAD_URL -o /tmp/$ASSET_NAME 49 | 50 | # Extract the asset 51 | tar -xzf /tmp/$ASSET_NAME -C /tmp 52 | 53 | # Set the correct permissions for the binary 54 | chmod +x /tmp/crosup 55 | 56 | # Move the extracted binary to the installation directory 57 | # use sudo if OS is Linux 58 | if [ "$OS" = "Linux" ]; then 59 | if command -v sudo >/dev/null 2>&1; then 60 | sudo mv /tmp/crosup $INSTALL_DIR 61 | else 62 | mv /tmp/crosup $INSTALL_DIR 63 | fi 64 | else 65 | mv /tmp/crosup $INSTALL_DIR 66 | fi 67 | 68 | # Clean up temporary files 69 | rm /tmp/$ASSET_NAME 70 | 71 | echo "Installation completed! 🎉" 72 | 73 | cat << EOF 74 | ${CYAN} 75 | ______ __ __ 76 | / ____/________ _____/ / / /___ 77 | / / / ___/ __ \/ ___/ / / / __ \\ 78 | / /___/ / / /_/ (__ ) /_/ / /_/ / 79 | \____/_/ \____/____/\____/ .___/ 80 | /_/ 81 | ${NO_COLOR} 82 | Quickly setup your development environment on your new Chromebook/ChromeOS 🚀 ✨ 83 | 84 | ${GREEN}https://github.com/tsirysndr/crosup${NO_COLOR} 85 | 86 | Please file an issue if you encounter any problems! 87 | 88 | =============================================================================== 89 | 90 | EOF 91 | 92 | # Check if the "--skip" argument was provided 93 | if [[ "$@" != *--skip* ]]; then 94 | crosup install --ask 95 | else 96 | printf "%s\n" "Run ${GREEN}'crosup install'${NO_COLOR} to install your development environment" 97 | fi 98 | 99 | -------------------------------------------------------------------------------- /.fluentci/src/dagger/jobs.ts: -------------------------------------------------------------------------------- 1 | import Client, { connect } from "../../deps.ts"; 2 | 3 | export enum Job { 4 | test = "test", 5 | build = "build", 6 | } 7 | 8 | export const exclude = ["target", ".git", ".devbox", ".fluentci"]; 9 | 10 | export const test = async (src = ".", options: string[] = []) => { 11 | await connect(async (client: Client) => { 12 | const context = client.host().directory(src); 13 | const ctr = client 14 | .pipeline(Job.test) 15 | .container() 16 | .from("rust:1.83-bullseye") 17 | .withDirectory("/app", context, { exclude }) 18 | .withWorkdir("/app") 19 | .withMountedCache("/app/target", client.cacheVolume("target")) 20 | .withMountedCache("/root/cargo/registry", client.cacheVolume("registry")) 21 | .withExec(["cargo", "test", ...options]); 22 | 23 | const result = await ctr.stdout(); 24 | 25 | console.log(result); 26 | }); 27 | return "done"; 28 | }; 29 | 30 | export const build = async (src = ".") => { 31 | await connect(async (client: Client) => { 32 | const context = client.host().directory(src); 33 | const ctr = client 34 | .pipeline(Job.build) 35 | .container() 36 | .from("rust:1.83-bullseye") 37 | .withExec(["apt-get", "update"]) 38 | .withExec([ 39 | "apt-get", 40 | "install", 41 | "-y", 42 | "build-essential", 43 | "gcc-aarch64-linux-gnu", 44 | ]) 45 | .withDirectory("/app", context, { exclude }) 46 | .withWorkdir("/app") 47 | .withMountedCache("/app/target", client.cacheVolume("target")) 48 | .withMountedCache("/root/cargo/registry", client.cacheVolume("registry")) 49 | .withMountedCache("/assets", client.cacheVolume("gh-release-assets")) 50 | .withEnvVariable( 51 | "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER", 52 | Deno.env.get("TARGET") === "aarch64-unknown-linux-gnu" 53 | ? "aarch64-linux-gnu-gcc" 54 | : "", 55 | ) 56 | .withEnvVariable("TAG", Deno.env.get("TAG") || "latest") 57 | .withEnvVariable( 58 | "TARGET", 59 | Deno.env.get("TARGET") || "x86_64-unknown-linux-gnu", 60 | ) 61 | .withExec(["sh", "-c", "rustup target add $TARGET"]) 62 | .withExec([ 63 | "sh", 64 | "-c", 65 | "cargo build -p crosup --release --target $TARGET", 66 | ]) 67 | .withExec(["sh", "-c", "cp target/${TARGET}/release/crosup ."]) 68 | .withExec([ 69 | "sh", 70 | "-c", 71 | "tar czvf /assets/crosup_${TAG}_${TARGET}.tar.gz crosup", 72 | ]) 73 | .withExec([ 74 | "sh", 75 | "-c", 76 | "shasum -a 256 /assets/crosup_${TAG}_${TARGET}.tar.gz > /assets/crosup_${TAG}_${TARGET}.tar.gz.sha256", 77 | ]); 78 | 79 | await ctr.stdout(); 80 | }); 81 | return "Done"; 82 | }; 83 | 84 | export type JobExec = (src?: string) => 85 | | Promise 86 | | (( 87 | src?: string, 88 | options?: { 89 | ignore: string[]; 90 | }, 91 | ) => Promise); 92 | 93 | export const runnableJobs: Record = { 94 | [Job.test]: test, 95 | [Job.build]: build, 96 | }; 97 | 98 | export const jobDescriptions: Record = { 99 | [Job.test]: "Run tests", 100 | [Job.build]: "Build the project", 101 | }; 102 | -------------------------------------------------------------------------------- /crates/ssh/src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use owo_colors::OwoColorize; 3 | use ssh2::Session; 4 | use std::{ 5 | collections::HashMap, 6 | io::{self, Read}, 7 | process::Command, 8 | }; 9 | 10 | pub fn exec(sess: Session, command: &str) -> Result<(), Error> { 11 | let mut channel = sess.channel_session()?; 12 | 13 | channel.exec(command)?; 14 | 15 | let mut buffer = [0; 1024]; 16 | loop { 17 | match channel.read(&mut buffer) { 18 | Ok(n) => { 19 | if n > 0 { 20 | let chunk = String::from_utf8_lossy(&buffer[..n]); 21 | print!("{}", chunk); 22 | } else { 23 | break; 24 | } 25 | } 26 | Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => continue, 27 | Err(err) => { 28 | return Err(err.into()); 29 | } 30 | } 31 | } 32 | 33 | if channel.exit_status()? != 0 { 34 | let command = command.bright_green(); 35 | return Err(Error::msg(format!( 36 | "{} exit status is not 0, exit status = {}", 37 | command, 38 | channel.exit_status()? 39 | ))); 40 | } 41 | 42 | Ok(()) 43 | } 44 | 45 | pub fn setup_ssh_agent_var() -> Result<(), Error> { 46 | println!("-> Setting up ssh-agent {}", "ssh-agent -s".bright_green()); 47 | let child = Command::new("ssh-agent").arg("-s").output()?; 48 | let output = String::from_utf8(child.stdout)?; 49 | 50 | let mut envs = HashMap::new(); 51 | 52 | for line in output.lines() { 53 | let env = line.split(";").next(); 54 | if let Some(env) = env { 55 | let mut env = env.split("="); 56 | let key = env.next(); 57 | let value = env.next(); 58 | if let (Some(key), Some(value)) = (key, value) { 59 | std::env::set_var(key, value); 60 | envs.insert(key, value); 61 | } 62 | } 63 | } 64 | 65 | println!( 66 | "-> Adding ssh key {}", 67 | "ssh-add ~/.ssh/id_rsa".bright_green() 68 | ); 69 | 70 | let mut child = Command::new("sh") 71 | .arg("-c") 72 | .arg("ssh-add ~/.ssh/id_rsa") 73 | .envs(envs) 74 | .spawn()?; 75 | 76 | child.wait()?; 77 | 78 | Ok(()) 79 | } 80 | 81 | pub fn setup_ssh_connection(addr: &str, username: &str) -> Result { 82 | setup_ssh_agent_var()?; 83 | let tcp = std::net::TcpStream::connect(addr)?; 84 | let mut sess = Session::new()?; 85 | let mut agent = sess.agent()?; 86 | agent.connect()?; 87 | 88 | sess.set_tcp_stream(tcp); 89 | sess.handshake()?; 90 | sess.userauth_agent(username)?; 91 | 92 | if !sess.authenticated() { 93 | return Err(Error::msg("authentication failed")); 94 | } 95 | 96 | Ok(sess) 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | use super::*; 102 | 103 | #[test] 104 | fn it_works() { 105 | // let session = setup_ssh_connection("192.168.8.101:22", "tsirysandratraina").unwrap(); 106 | let session = setup_ssh_connection("localhost:22", "tsirysndr").unwrap(); 107 | exec(session.clone(), "sh -c 'PATH=/home/linuxbrew/.linuxbrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin minikube version'").unwrap(); 108 | assert!(true); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /crates/types/src/curl.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 5 | pub struct CurlConfiguration { 6 | #[serde(serialize_with = "hcl::ser::labeled_block")] 7 | pub script: IndexMap, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 11 | pub struct Script { 12 | #[serde(skip_serializing, skip_deserializing)] 13 | pub name: String, 14 | pub url: String, 15 | 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | pub enable_sudo: Option, 18 | 19 | #[serde(skip_serializing_if = "Option::is_none")] 20 | pub postinstall: Option, 21 | 22 | #[serde(skip_serializing_if = "Option::is_none")] 23 | pub version_check: Option, 24 | 25 | #[serde(skip_serializing_if = "Option::is_none")] 26 | pub args: Option, 27 | 28 | #[serde( 29 | skip_serializing_if = "Option::is_none", 30 | serialize_with = "hcl::ser::block" 31 | )] 32 | pub env: Option>, 33 | 34 | #[serde(skip_serializing_if = "Option::is_none")] 35 | pub shell: Option, 36 | 37 | #[serde(skip_serializing_if = "Option::is_none")] 38 | pub depends_on: Option>, 39 | } 40 | 41 | pub fn default_nix_installer() -> Script { 42 | Script { 43 | name: "nix".into(), 44 | url: "https://install.determinate.systems/nix".into(), 45 | enable_sudo: Some(true), 46 | args: Some("install --no-confirm".into()), 47 | ..Default::default() 48 | } 49 | } 50 | 51 | pub fn default_brew_installer() -> Script { 52 | let postinstall = match std::env::consts::OS { 53 | "macos" => Some("echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> ~/.zprofile".into()), 54 | "linux" => { 55 | Some("echo 'eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)' >> ~/.bashrc".into()) 56 | } 57 | _ => None, 58 | }; 59 | 60 | Script { 61 | name: "homebrew".into(), 62 | url: "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh".into(), 63 | postinstall, 64 | version_check: Some("brew".into()), 65 | env: Some( 66 | [("NONINTERACTIVE".into(), "true".into())] 67 | .iter() 68 | .cloned() 69 | .collect(), 70 | ), 71 | shell: Some("bash".into()), 72 | ..Default::default() 73 | } 74 | } 75 | 76 | pub fn default_curl_install() -> IndexMap { 77 | let mut script = IndexMap::new(); 78 | script.insert( 79 | "devbox".into(), 80 | Script { 81 | name: "devbox".into(), 82 | url: "https://get.jetpack.io/devbox".into(), 83 | shell: Some("bash".into()), 84 | depends_on: Some(vec!["nix".into()]), 85 | env: Some([("FORCE".into(), "1".into())].iter().cloned().collect()), 86 | ..Default::default() 87 | }, 88 | ); 89 | 90 | script.insert( 91 | "atuin".into(), 92 | Script { 93 | name: "atuin".into(), 94 | url: "https://raw.githubusercontent.com/ellie/atuin/main/install.sh".into(), 95 | shell: Some("bash".into()), 96 | ..Default::default() 97 | }, 98 | ); 99 | 100 | script.insert("nix".into(), default_nix_installer()); 101 | 102 | script.insert("homebrew".into(), default_brew_installer()); 103 | 104 | let mut curl = IndexMap::new(); 105 | curl.insert("install".into(), CurlConfiguration { script }); 106 | curl 107 | } 108 | -------------------------------------------------------------------------------- /crates/migration/src/m20220101_000001_create_table.rs: -------------------------------------------------------------------------------- 1 | use sea_orm_migration::prelude::*; 2 | 3 | #[derive(DeriveMigrationName)] 4 | pub struct Migration; 5 | 6 | #[async_trait::async_trait] 7 | impl MigrationTrait for Migration { 8 | async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { 9 | manager 10 | .create_table( 11 | Table::create() 12 | .table(File::Table) 13 | .if_not_exists() 14 | .col( 15 | ColumnDef::new(File::Id) 16 | .integer() 17 | .not_null() 18 | .auto_increment() 19 | .primary_key(), 20 | ) 21 | .col(ColumnDef::new(File::Name).string().not_null()) 22 | .col(ColumnDef::new(File::Path).string().not_null()) 23 | .col( 24 | ColumnDef::new(File::CreatedAt) 25 | .timestamp() 26 | .extra("DEFAULT CURRENT_TIMESTAMP".into()), 27 | ) 28 | .to_owned(), 29 | ) 30 | .await?; 31 | manager 32 | .create_table( 33 | Table::create() 34 | .table(Modification::Table) 35 | .if_not_exists() 36 | .col( 37 | ColumnDef::new(Modification::Id) 38 | .integer() 39 | .not_null() 40 | .auto_increment() 41 | .primary_key(), 42 | ) 43 | .col( 44 | ColumnDef::new(Modification::Timestamp) 45 | .timestamp() 46 | .extra("DEFAULT CURRENT_TIMESTAMP".into()), 47 | ) 48 | .col( 49 | ColumnDef::new(Modification::Hash) 50 | .string() 51 | .unique_key() 52 | .not_null(), 53 | ) 54 | .col(ColumnDef::new(Modification::FileId).integer().not_null()) 55 | .col(ColumnDef::new(Modification::PreviousId).integer().null()) 56 | .col(ColumnDef::new(Modification::Content).text().not_null()) 57 | .foreign_key( 58 | ForeignKey::create() 59 | .name("modification_file_id") 60 | .from(Modification::Table, Modification::FileId) 61 | .to(File::Table, File::Id), 62 | ) 63 | .foreign_key( 64 | ForeignKey::create() 65 | .name("modification_previous_id") 66 | .from(Modification::Table, Modification::PreviousId) 67 | .to(Modification::Table, Modification::Id), 68 | ) 69 | .to_owned(), 70 | ) 71 | .await 72 | } 73 | 74 | async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { 75 | manager 76 | .drop_table(Table::drop().table(File::Table).to_owned()) 77 | .await?; 78 | manager 79 | .drop_table(Table::drop().table(Modification::Table).to_owned()) 80 | .await 81 | } 82 | } 83 | 84 | /// Learn more at https://docs.rs/sea-query#iden 85 | #[derive(Iden)] 86 | enum File { 87 | Table, 88 | Id, 89 | Name, 90 | Path, 91 | CreatedAt, 92 | } 93 | 94 | #[derive(Iden)] 95 | enum Modification { 96 | Table, 97 | Id, 98 | Timestamp, 99 | Hash, 100 | FileId, 101 | PreviousId, 102 | Content, 103 | } 104 | -------------------------------------------------------------------------------- /crates/types/src/apt.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use os_release::OsRelease; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 6 | pub struct AptConfiguration { 7 | #[serde(serialize_with = "hcl::ser::labeled_block")] 8 | pub pkg: IndexMap, 9 | } 10 | 11 | #[derive(Serialize, Deserialize, Debug, Clone, Default)] 12 | pub struct Package { 13 | #[serde(skip_serializing, skip_deserializing)] 14 | pub name: String, 15 | 16 | #[serde(skip_serializing_if = "Option::is_none")] 17 | pub url: Option, 18 | 19 | #[serde(skip_serializing_if = "Option::is_none")] 20 | pub gpg_key: Option, 21 | 22 | #[serde(skip_serializing_if = "Option::is_none")] 23 | pub gpg_path: Option, 24 | 25 | #[serde(skip_serializing_if = "Option::is_none")] 26 | pub setup_repository: Option, 27 | 28 | #[serde(skip_serializing_if = "Option::is_none")] 29 | pub apt_update: Option, 30 | 31 | #[serde(skip_serializing_if = "Option::is_none")] 32 | pub packages: Option>, 33 | 34 | #[serde(skip_serializing_if = "Option::is_none")] 35 | pub depends_on: Option>, 36 | 37 | #[serde(skip_serializing_if = "Option::is_none")] 38 | pub postinstall: Option, 39 | 40 | #[serde(skip_serializing_if = "Option::is_none")] 41 | pub version_check: Option, 42 | } 43 | 44 | pub fn default_apt_install() -> IndexMap { 45 | let mut pkg = IndexMap::new(); 46 | 47 | // detect linux 48 | if cfg!(target_os = "linux") { 49 | // determine linux distribution using os-release 50 | if let Ok(os_release) = OsRelease::new() { 51 | let os = os_release.id.to_lowercase(); 52 | let os = os.as_str(); 53 | 54 | if os == "debian" { 55 | pkg.insert( 56 | "docker".into(), 57 | Package { 58 | name: "docker".into(), 59 | gpg_key: Some("https://download.docker.com/linux/debian/gpg".into()), 60 | gpg_path: Some("/etc/apt/keyrings/docker.gpg".into()), 61 | setup_repository: Some( 62 | r#"echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null"#.into()), 63 | apt_update: Some(true), 64 | packages: Some(vec![ 65 | "docker-ce".into(), 66 | "docker-ce-cli".into(), 67 | "containerd.io".into(), 68 | "docker-buildx-plugin".into(), 69 | "docker-compose-plugin".into() 70 | ]), 71 | depends_on: Some(vec!["ca-certificates".into(),"curl".into(), "gnupg".into()]), 72 | postinstall: Some("sudo usermod -aG docker $USER && newgrp docker".into()), 73 | ..Default::default() 74 | }, 75 | ); 76 | } 77 | 78 | if os == "debian" || os == "ubuntu" { 79 | pkg.insert( 80 | "vscode".into(), 81 | Package { 82 | name: "code".into(), 83 | url: Some( 84 | "https://code.visualstudio.com/sha/download?build=stable&os=linux-deb-x64".into(), 85 | ), 86 | version_check: Some("code".into()), 87 | ..Default::default() 88 | }, 89 | ); 90 | } 91 | } 92 | } 93 | 94 | let mut apt = IndexMap::new(); 95 | apt.insert("install".into(), AptConfiguration { pkg }); 96 | apt 97 | } 98 | -------------------------------------------------------------------------------- /crates/installers/src/dnf.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_macros::{check_version, dnf_install, exec_sh_with_output}; 3 | use crosup_types::dnf::Package; 4 | use owo_colors::OwoColorize; 5 | use ssh2::Session; 6 | use std::{any::Any, io::BufRead, process::Stdio}; 7 | 8 | use super::Installer; 9 | 10 | #[derive(Default, Clone)] 11 | pub struct DnfInstaller { 12 | pub name: String, 13 | pub version: String, 14 | pub dependencies: Vec, 15 | pub dnf_dependencies: Vec, 16 | pub packages: Option>, 17 | pub postinstall: Option, 18 | pub version_check: Option, 19 | pub provider: String, 20 | pub session: Option, 21 | } 22 | 23 | impl From for DnfInstaller { 24 | fn from(pkg: Package) -> Self { 25 | Self { 26 | name: pkg.name, 27 | packages: pkg.packages, 28 | dnf_dependencies: pkg.depends_on.unwrap_or(vec![]), 29 | provider: "dnf".into(), 30 | version_check: pkg.version_check, 31 | ..Default::default() 32 | } 33 | } 34 | } 35 | 36 | impl DnfInstaller { 37 | pub fn install_dependencies(&self) -> Result<(), Error> { 38 | if self.dnf_dependencies.is_empty() { 39 | return Ok(()); 40 | } 41 | 42 | println!( 43 | "-> Installing dependencies for {}", 44 | self.name.bright_green() 45 | ); 46 | let deps = self.dnf_dependencies.join(" "); 47 | dnf_install!(deps, self.session.clone()); 48 | Ok(()) 49 | } 50 | 51 | fn postinstall(&self) -> Result<(), Error> { 52 | if let Some(command) = self.postinstall.clone() { 53 | println!( 54 | "-> Running postinstall command:\n{}", 55 | command.bright_green() 56 | ); 57 | for cmd in command.split("\n") { 58 | exec_sh_with_output!(cmd, self.session.clone()); 59 | } 60 | } 61 | Ok(()) 62 | } 63 | } 64 | 65 | impl Installer for DnfInstaller { 66 | fn install(&self) -> Result<(), Error> { 67 | if self.is_installed().is_ok() { 68 | if self.is_installed().unwrap() { 69 | println!( 70 | "-> {} is already installed, skipping", 71 | self.name().bright_green() 72 | ); 73 | return Ok(()); 74 | } 75 | } 76 | 77 | self.install_dependencies()?; 78 | 79 | if let Some(packages) = self.packages.clone() { 80 | let packages = packages.join(" "); 81 | let command = format!("sudo dnf install -y {}", packages); 82 | println!("-> Running {}", command.bright_green()); 83 | dnf_install!(packages, self.session.clone()); 84 | } 85 | 86 | self.postinstall()?; 87 | Ok(()) 88 | } 89 | 90 | fn is_installed(&self) -> Result { 91 | println!( 92 | "-> Checking if {} is already installed", 93 | self.name.bright_green() 94 | ); 95 | if let Some(command) = self.version_check.clone() { 96 | check_version!(self, command, self.session.clone()); 97 | return Ok(false); 98 | } 99 | let command = self.name.clone(); 100 | check_version!(self, command, self.session.clone()); 101 | Ok(false) 102 | } 103 | 104 | fn name(&self) -> &str { 105 | &self.name 106 | } 107 | 108 | fn version(&self) -> &str { 109 | &self.version 110 | } 111 | 112 | fn dependencies(&self) -> Vec { 113 | self.dependencies.clone() 114 | } 115 | 116 | fn is_default(&self) -> bool { 117 | true 118 | } 119 | 120 | fn provider(&self) -> &str { 121 | &self.provider 122 | } 123 | 124 | fn as_any(&self) -> &dyn Any { 125 | self 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /crates/installers/src/yum.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_macros::{check_version, exec_sh_with_output, yum_install}; 3 | use crosup_types::yum::Package; 4 | use owo_colors::OwoColorize; 5 | use ssh2::Session; 6 | use std::{any::Any, io::BufRead, process::Stdio}; 7 | 8 | use super::Installer; 9 | 10 | #[derive(Default, Clone)] 11 | pub struct YumInstaller { 12 | pub name: String, 13 | pub version: String, 14 | pub dependencies: Vec, 15 | pub yum_dependencies: Vec, 16 | pub packages: Option>, 17 | pub postinstall: Option, 18 | pub version_check: Option, 19 | pub provider: String, 20 | pub session: Option, 21 | } 22 | 23 | impl From for YumInstaller { 24 | fn from(pkg: Package) -> Self { 25 | Self { 26 | name: pkg.name, 27 | packages: pkg.packages, 28 | yum_dependencies: pkg.depends_on.unwrap_or(vec![]), 29 | provider: "yum".into(), 30 | version_check: pkg.version_check, 31 | ..Default::default() 32 | } 33 | } 34 | } 35 | 36 | impl YumInstaller { 37 | pub fn install_dependencies(&self) -> Result<(), Error> { 38 | if self.yum_dependencies.is_empty() { 39 | return Ok(()); 40 | } 41 | 42 | println!( 43 | "-> Installing dependencies for {}", 44 | self.name.bright_green() 45 | ); 46 | let deps = self.yum_dependencies.join(" "); 47 | yum_install!(deps, self.session.clone()); 48 | Ok(()) 49 | } 50 | 51 | fn postinstall(&self) -> Result<(), Error> { 52 | if let Some(command) = self.postinstall.clone() { 53 | println!( 54 | "-> Running postinstall command:\n{}", 55 | command.bright_green() 56 | ); 57 | for cmd in command.split("\n") { 58 | exec_sh_with_output!(cmd, self.session.clone()); 59 | } 60 | } 61 | Ok(()) 62 | } 63 | } 64 | 65 | impl Installer for YumInstaller { 66 | fn install(&self) -> Result<(), Error> { 67 | if self.is_installed().is_ok() { 68 | if self.is_installed().unwrap() { 69 | println!( 70 | "-> {} is already installed, skipping", 71 | self.name().bright_green() 72 | ); 73 | return Ok(()); 74 | } 75 | } 76 | 77 | self.install_dependencies()?; 78 | 79 | if let Some(packages) = self.packages.clone() { 80 | let packages = packages.join(" "); 81 | let command = format!("sudo yum install -y {}", packages); 82 | println!("-> Running {}", command.bright_green()); 83 | yum_install!(packages, self.session.clone()); 84 | } 85 | 86 | self.postinstall()?; 87 | Ok(()) 88 | } 89 | 90 | fn is_installed(&self) -> Result { 91 | println!( 92 | "-> Checking if {} is already installed", 93 | self.name.bright_green() 94 | ); 95 | if let Some(command) = self.version_check.clone() { 96 | check_version!(self, command, self.session.clone()); 97 | return Ok(false); 98 | } 99 | let command = self.name.clone(); 100 | check_version!(self, command, self.session.clone()); 101 | Ok(false) 102 | } 103 | 104 | fn name(&self) -> &str { 105 | &self.name 106 | } 107 | 108 | fn version(&self) -> &str { 109 | &self.version 110 | } 111 | 112 | fn dependencies(&self) -> Vec { 113 | self.dependencies.clone() 114 | } 115 | 116 | fn is_default(&self) -> bool { 117 | true 118 | } 119 | 120 | fn provider(&self) -> &str { 121 | &self.provider 122 | } 123 | 124 | fn as_any(&self) -> &dyn Any { 125 | self 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /crates/installers/src/pacman.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_macros::{check_version, exec_sh_with_output, pacman_install}; 3 | use crosup_types::pacman::Package; 4 | use owo_colors::OwoColorize; 5 | use ssh2::Session; 6 | use std::{any::Any, io::BufRead, process::Stdio}; 7 | 8 | use super::Installer; 9 | 10 | #[derive(Default, Clone)] 11 | pub struct PacmanInstaller { 12 | pub name: String, 13 | pub version: String, 14 | pub dependencies: Vec, 15 | pub pacman_dependencies: Vec, 16 | pub packages: Option>, 17 | pub postinstall: Option, 18 | pub version_check: Option, 19 | pub non_interactive: bool, 20 | pub provider: String, 21 | pub session: Option, 22 | } 23 | 24 | impl From for PacmanInstaller { 25 | fn from(pkg: Package) -> Self { 26 | Self { 27 | name: pkg.name, 28 | packages: pkg.packages, 29 | pacman_dependencies: pkg.depends_on.unwrap_or(vec![]), 30 | provider: "pacman".into(), 31 | version_check: pkg.version_check, 32 | ..Default::default() 33 | } 34 | } 35 | } 36 | 37 | impl PacmanInstaller { 38 | pub fn install_dependencies(&self) -> Result<(), Error> { 39 | if self.pacman_dependencies.is_empty() { 40 | return Ok(()); 41 | } 42 | 43 | println!( 44 | "-> Installing dependencies for {}", 45 | self.name.bright_green() 46 | ); 47 | let deps = self.pacman_dependencies.join(" "); 48 | pacman_install!(deps, self.session.clone()); 49 | Ok(()) 50 | } 51 | 52 | fn postinstall(&self) -> Result<(), Error> { 53 | if let Some(command) = self.postinstall.clone() { 54 | println!( 55 | "-> Running postinstall command:\n{}", 56 | command.bright_green() 57 | ); 58 | for cmd in command.split("\n") { 59 | exec_sh_with_output!(cmd, self.session.clone()); 60 | } 61 | } 62 | Ok(()) 63 | } 64 | } 65 | 66 | impl Installer for PacmanInstaller { 67 | fn install(&self) -> Result<(), Error> { 68 | if self.is_installed().is_ok() { 69 | if self.is_installed().unwrap() { 70 | println!( 71 | "-> {} is already installed, skipping", 72 | self.name().bright_green() 73 | ); 74 | return Ok(()); 75 | } 76 | } 77 | 78 | self.install_dependencies()?; 79 | 80 | if let Some(packages) = self.packages.clone() { 81 | let packages = packages.join(" "); 82 | let command = format!("sudo pacman -S {}", packages); 83 | println!("-> Running {}", command.bright_green()); 84 | pacman_install!(packages, self.session.clone()); 85 | } 86 | 87 | self.postinstall()?; 88 | Ok(()) 89 | } 90 | 91 | fn is_installed(&self) -> Result { 92 | println!( 93 | "-> Checking if {} is already installed", 94 | self.name.bright_green() 95 | ); 96 | if let Some(command) = self.version_check.clone() { 97 | check_version!(self, command, self.session.clone()); 98 | return Ok(false); 99 | } 100 | let command = self.name.clone(); 101 | check_version!(self, command, self.session.clone()); 102 | Ok(false) 103 | } 104 | 105 | fn name(&self) -> &str { 106 | &self.name 107 | } 108 | 109 | fn version(&self) -> &str { 110 | &self.version 111 | } 112 | 113 | fn dependencies(&self) -> Vec { 114 | self.dependencies.clone() 115 | } 116 | 117 | fn is_default(&self) -> bool { 118 | true 119 | } 120 | 121 | fn provider(&self) -> &str { 122 | &self.provider 123 | } 124 | 125 | fn as_any(&self) -> &dyn Any { 126 | self 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /crates/installers/src/slackpkg.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_macros::{check_version, exec_sh_with_output, slackpkg_install}; 3 | use crosup_types::slackpkg::Package; 4 | use owo_colors::OwoColorize; 5 | use ssh2::Session; 6 | use std::{any::Any, io::BufRead, process::Stdio}; 7 | 8 | use super::Installer; 9 | 10 | #[derive(Default, Clone)] 11 | pub struct SlackpkgInstaller { 12 | pub name: String, 13 | pub version: String, 14 | pub dependencies: Vec, 15 | pub slackpkg_dependencies: Vec, 16 | pub packages: Option>, 17 | pub postinstall: Option, 18 | pub version_check: Option, 19 | pub provider: String, 20 | pub session: Option, 21 | } 22 | 23 | impl From for SlackpkgInstaller { 24 | fn from(pkg: Package) -> Self { 25 | Self { 26 | name: pkg.name, 27 | packages: pkg.packages, 28 | slackpkg_dependencies: pkg.depends_on.unwrap_or(vec![]), 29 | provider: "slackpkg".into(), 30 | version_check: pkg.version_check, 31 | ..Default::default() 32 | } 33 | } 34 | } 35 | 36 | impl SlackpkgInstaller { 37 | pub fn install_dependencies(&self) -> Result<(), Error> { 38 | if self.slackpkg_dependencies.is_empty() { 39 | return Ok(()); 40 | } 41 | 42 | println!( 43 | "-> Installing dependencies for {}", 44 | self.name.bright_green() 45 | ); 46 | let deps = self.slackpkg_dependencies.join(" "); 47 | slackpkg_install!(deps, self.session.clone()); 48 | Ok(()) 49 | } 50 | 51 | fn postinstall(&self) -> Result<(), Error> { 52 | if let Some(command) = self.postinstall.clone() { 53 | println!( 54 | "-> Running postinstall command:\n{}", 55 | command.bright_green() 56 | ); 57 | for cmd in command.split("\n") { 58 | exec_sh_with_output!(cmd, self.session.clone()); 59 | } 60 | } 61 | Ok(()) 62 | } 63 | } 64 | 65 | impl Installer for SlackpkgInstaller { 66 | fn install(&self) -> Result<(), Error> { 67 | if self.is_installed().is_ok() { 68 | if self.is_installed().unwrap() { 69 | println!( 70 | "-> {} is already installed, skipping", 71 | self.name().bright_green() 72 | ); 73 | return Ok(()); 74 | } 75 | } 76 | 77 | self.install_dependencies()?; 78 | 79 | if let Some(packages) = self.packages.clone() { 80 | let packages = packages.join(" "); 81 | let command = format!("sudo slackpkg install {}", packages); 82 | println!("-> Running {}", command.bright_green()); 83 | slackpkg_install!(packages, self.session.clone()); 84 | } 85 | 86 | self.postinstall()?; 87 | Ok(()) 88 | } 89 | 90 | fn is_installed(&self) -> Result { 91 | println!( 92 | "-> Checking if {} is already installed", 93 | self.name.bright_green() 94 | ); 95 | if let Some(command) = self.version_check.clone() { 96 | check_version!(self, command, self.session.clone()); 97 | return Ok(false); 98 | } 99 | let command = self.name.clone(); 100 | check_version!(self, command, self.session.clone()); 101 | Ok(false) 102 | } 103 | 104 | fn name(&self) -> &str { 105 | &self.name 106 | } 107 | 108 | fn version(&self) -> &str { 109 | &self.version 110 | } 111 | 112 | fn dependencies(&self) -> Vec { 113 | self.dependencies.clone() 114 | } 115 | 116 | fn is_default(&self) -> bool { 117 | true 118 | } 119 | 120 | fn provider(&self) -> &str { 121 | &self.provider 122 | } 123 | 124 | fn as_any(&self) -> &dyn Any { 125 | self 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /.fluentci/gen/nexus.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file was generated by Nexus Schema 3 | * Do not make changes to this file directly 4 | */ 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | declare global { 13 | interface NexusGen extends NexusGenTypes {} 14 | } 15 | 16 | export interface NexusGenInputs { 17 | } 18 | 19 | export interface NexusGenEnums { 20 | } 21 | 22 | export interface NexusGenScalars { 23 | String: string 24 | Int: number 25 | Float: number 26 | Boolean: boolean 27 | ID: string 28 | } 29 | 30 | export interface NexusGenObjects { 31 | Query: {}; 32 | } 33 | 34 | export interface NexusGenInterfaces { 35 | } 36 | 37 | export interface NexusGenUnions { 38 | } 39 | 40 | export type NexusGenRootTypes = NexusGenObjects 41 | 42 | export type NexusGenAllTypes = NexusGenRootTypes & NexusGenScalars 43 | 44 | export interface NexusGenFieldTypes { 45 | Query: { // field return type 46 | build: string | null; // String 47 | test: string | null; // String 48 | } 49 | } 50 | 51 | export interface NexusGenFieldTypeNames { 52 | Query: { // field return type name 53 | build: 'String' 54 | test: 'String' 55 | } 56 | } 57 | 58 | export interface NexusGenArgTypes { 59 | Query: { 60 | build: { // args 61 | src: string; // String! 62 | } 63 | test: { // args 64 | src: string; // String! 65 | } 66 | } 67 | } 68 | 69 | export interface NexusGenAbstractTypeMembers { 70 | } 71 | 72 | export interface NexusGenTypeInterfaces { 73 | } 74 | 75 | export type NexusGenObjectNames = keyof NexusGenObjects; 76 | 77 | export type NexusGenInputNames = never; 78 | 79 | export type NexusGenEnumNames = never; 80 | 81 | export type NexusGenInterfaceNames = never; 82 | 83 | export type NexusGenScalarNames = keyof NexusGenScalars; 84 | 85 | export type NexusGenUnionNames = never; 86 | 87 | export type NexusGenObjectsUsingAbstractStrategyIsTypeOf = never; 88 | 89 | export type NexusGenAbstractsUsingStrategyResolveType = never; 90 | 91 | export type NexusGenFeaturesConfig = { 92 | abstractTypeStrategies: { 93 | isTypeOf: false 94 | resolveType: true 95 | __typename: false 96 | } 97 | } 98 | 99 | export interface NexusGenTypes { 100 | context: any; 101 | inputTypes: NexusGenInputs; 102 | rootTypes: NexusGenRootTypes; 103 | inputTypeShapes: NexusGenInputs & NexusGenEnums & NexusGenScalars; 104 | argTypes: NexusGenArgTypes; 105 | fieldTypes: NexusGenFieldTypes; 106 | fieldTypeNames: NexusGenFieldTypeNames; 107 | allTypes: NexusGenAllTypes; 108 | typeInterfaces: NexusGenTypeInterfaces; 109 | objectNames: NexusGenObjectNames; 110 | inputNames: NexusGenInputNames; 111 | enumNames: NexusGenEnumNames; 112 | interfaceNames: NexusGenInterfaceNames; 113 | scalarNames: NexusGenScalarNames; 114 | unionNames: NexusGenUnionNames; 115 | allInputTypes: NexusGenTypes['inputNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['scalarNames']; 116 | allOutputTypes: NexusGenTypes['objectNames'] | NexusGenTypes['enumNames'] | NexusGenTypes['unionNames'] | NexusGenTypes['interfaceNames'] | NexusGenTypes['scalarNames']; 117 | allNamedTypes: NexusGenTypes['allInputTypes'] | NexusGenTypes['allOutputTypes'] 118 | abstractTypes: NexusGenTypes['interfaceNames'] | NexusGenTypes['unionNames']; 119 | abstractTypeMembers: NexusGenAbstractTypeMembers; 120 | objectsUsingAbstractStrategyIsTypeOf: NexusGenObjectsUsingAbstractStrategyIsTypeOf; 121 | abstractsUsingStrategyResolveType: NexusGenAbstractsUsingStrategyResolveType; 122 | features: NexusGenFeaturesConfig; 123 | } 124 | 125 | 126 | declare global { 127 | interface NexusGenPluginTypeConfig { 128 | } 129 | interface NexusGenPluginInputTypeConfig { 130 | } 131 | interface NexusGenPluginFieldConfig { 132 | } 133 | interface NexusGenPluginInputFieldConfig { 134 | } 135 | interface NexusGenPluginSchemaConfig { 136 | } 137 | interface NexusGenPluginArgConfig { 138 | } 139 | } -------------------------------------------------------------------------------- /crates/installers/src/apk.rs: -------------------------------------------------------------------------------- 1 | use crosup_macros::{apk_add, check_version, exec_sh_with_output}; 2 | use crosup_types::apk::Package; 3 | 4 | use anyhow::Error; 5 | use owo_colors::OwoColorize; 6 | use ssh2::Session; 7 | use std::{any::Any, io::BufRead, process::Stdio}; 8 | 9 | use super::Installer; 10 | 11 | #[derive(Default, Clone)] 12 | pub struct ApkInstaller { 13 | pub name: String, 14 | pub version: String, 15 | pub dependencies: Vec, 16 | pub apk_dependencies: Vec, 17 | pub packages: Option>, 18 | pub postinstall: Option, 19 | pub version_check: Option, 20 | pub interactive: bool, 21 | pub provider: String, 22 | pub session: Option, 23 | } 24 | 25 | impl From for ApkInstaller { 26 | fn from(pkg: Package) -> Self { 27 | Self { 28 | name: pkg.name, 29 | packages: pkg.packages, 30 | apk_dependencies: pkg.depends_on.unwrap_or(vec![]), 31 | provider: "apk".into(), 32 | version_check: pkg.version_check, 33 | interactive: pkg.interactive.unwrap_or(false), 34 | ..Default::default() 35 | } 36 | } 37 | } 38 | 39 | impl ApkInstaller { 40 | pub fn install_dependencies(&self) -> Result<(), Error> { 41 | if self.apk_dependencies.is_empty() { 42 | return Ok(()); 43 | } 44 | 45 | println!( 46 | "-> Installing dependencies for {}", 47 | self.name.bright_green() 48 | ); 49 | let deps = self.apk_dependencies.join(" "); 50 | apk_add!(deps, "", self.session.clone()); 51 | Ok(()) 52 | } 53 | 54 | fn postinstall(&self) -> Result<(), Error> { 55 | if let Some(command) = self.postinstall.clone() { 56 | println!( 57 | "-> Running postinstall command:\n{}", 58 | command.bright_green() 59 | ); 60 | for cmd in command.split("\n") { 61 | exec_sh_with_output!(cmd, self.session.clone()); 62 | } 63 | } 64 | Ok(()) 65 | } 66 | } 67 | 68 | impl Installer for ApkInstaller { 69 | fn install(&self) -> Result<(), Error> { 70 | if self.is_installed().is_ok() { 71 | if self.is_installed().unwrap() { 72 | println!( 73 | "-> {} is already installed, skipping", 74 | self.name().bright_green() 75 | ); 76 | return Ok(()); 77 | } 78 | } 79 | 80 | self.install_dependencies()?; 81 | 82 | if let Some(packages) = self.packages.clone() { 83 | let options = match self.interactive { 84 | true => "--interactive", 85 | false => "", 86 | }; 87 | let packages = packages.join(" "); 88 | let command = format!("sudo apk add {} {}", options, packages); 89 | println!("-> Running {}", command.bright_green()); 90 | apk_add!(packages, options, self.session.clone()); 91 | } 92 | 93 | self.postinstall()?; 94 | Ok(()) 95 | } 96 | 97 | fn is_installed(&self) -> Result { 98 | println!( 99 | "-> Checking if {} is already installed", 100 | self.name.bright_green() 101 | ); 102 | if let Some(command) = self.version_check.clone() { 103 | check_version!(self, command, self.session.clone()); 104 | return Ok(false); 105 | } 106 | let command = self.name.clone(); 107 | check_version!(self, command, self.session.clone()); 108 | Ok(false) 109 | } 110 | 111 | fn name(&self) -> &str { 112 | &self.name 113 | } 114 | 115 | fn version(&self) -> &str { 116 | &self.version 117 | } 118 | 119 | fn dependencies(&self) -> Vec { 120 | self.dependencies.clone() 121 | } 122 | 123 | fn is_default(&self) -> bool { 124 | true 125 | } 126 | 127 | fn provider(&self) -> &str { 128 | &self.provider 129 | } 130 | 131 | fn as_any(&self) -> &dyn Any { 132 | self 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /crates/installers/src/brew.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use owo_colors::OwoColorize; 3 | use ssh2::Session; 4 | use std::{any::Any, io::BufRead, process::Stdio}; 5 | 6 | use crosup_macros::{brew_install, check_version, exec_bash_with_output}; 7 | use crosup_types::brew::{BrewConfiguration, Package}; 8 | 9 | use super::Installer; 10 | 11 | #[derive(Default, Clone)] 12 | pub struct BrewInstaller { 13 | pub name: String, 14 | pub version: String, 15 | pub dependencies: Vec, 16 | pub brew_dependencies: Vec, 17 | pub pkgs: Vec, 18 | pub preinstall: Option, 19 | pub postinstall: Option, 20 | pub version_check: Option, 21 | pub provider: String, 22 | pub session: Option, 23 | pub cask: bool, 24 | } 25 | 26 | impl From for BrewInstaller { 27 | fn from(config: BrewConfiguration) -> Self { 28 | Self { 29 | name: "brew".to_string(), 30 | version: "latest".to_string(), 31 | dependencies: vec!["homebrew".into()], 32 | pkgs: config.pkgs.unwrap_or(vec![]), 33 | ..Default::default() 34 | } 35 | } 36 | } 37 | 38 | impl From for BrewInstaller { 39 | fn from(pkg: Package) -> Self { 40 | Self { 41 | name: pkg.name, 42 | version: "latest".to_string(), 43 | dependencies: vec!["homebrew".into()], 44 | preinstall: pkg.preinstall, 45 | postinstall: pkg.postinstall, 46 | provider: "brew".into(), 47 | version_check: pkg.version_check, 48 | cask: pkg.cask.unwrap_or(false), 49 | ..Default::default() 50 | } 51 | } 52 | } 53 | 54 | impl BrewInstaller { 55 | fn preinstall(&self) -> Result<(), Error> { 56 | if let Some(command) = self.preinstall.clone() { 57 | println!("-> Running preinstall command:\n{}", command.bright_green()); 58 | for cmd in command.split("\n") { 59 | exec_bash_with_output!(cmd, self.session.clone()); 60 | } 61 | } 62 | Ok(()) 63 | } 64 | 65 | fn postinstall(&self) -> Result<(), Error> { 66 | if let Some(command) = self.postinstall.clone() { 67 | println!( 68 | "-> Running postinstall command:\n{}", 69 | command.bright_green() 70 | ); 71 | for cmd in command.split("\n") { 72 | exec_bash_with_output!(cmd, self.session.clone()); 73 | } 74 | } 75 | Ok(()) 76 | } 77 | } 78 | 79 | impl Installer for BrewInstaller { 80 | fn install(&self) -> Result<(), Error> { 81 | if self.is_installed().is_ok() { 82 | println!( 83 | "-> {} is already installed, skipping", 84 | self.name().bright_green() 85 | ); 86 | return Ok(()); 87 | } 88 | println!("-> 🚚 Installing {}", self.name().bright_green()); 89 | self.preinstall()?; 90 | brew_install!(self, &self.name, self.cask, self.session.clone()); 91 | self.postinstall()?; 92 | Ok(()) 93 | } 94 | 95 | fn is_installed(&self) -> Result { 96 | println!( 97 | "-> Checking if {} is already installed", 98 | self.name.bright_green() 99 | ); 100 | if let Some(command) = self.version_check.clone() { 101 | check_version!(self, command, self.session.clone()); 102 | return Ok(false); 103 | } 104 | let command = self.name.clone(); 105 | check_version!(self, command, self.session.clone()); 106 | Ok(false) 107 | } 108 | 109 | fn name(&self) -> &str { 110 | &self.name 111 | } 112 | 113 | fn version(&self) -> &str { 114 | &self.version 115 | } 116 | 117 | fn dependencies(&self) -> Vec { 118 | self.dependencies.clone() 119 | } 120 | 121 | fn is_default(&self) -> bool { 122 | true 123 | } 124 | 125 | fn provider(&self) -> &str { 126 | &self.provider 127 | } 128 | 129 | fn as_any(&self) -> &dyn Any { 130 | self 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /crates/installers/src/zypper.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_macros::{check_version, exec_sh_with_output, zypper_install}; 3 | use crosup_types::zypper::Package; 4 | use owo_colors::OwoColorize; 5 | use ssh2::Session; 6 | use std::{any::Any, io::BufRead, process::Stdio}; 7 | 8 | use super::Installer; 9 | 10 | #[derive(Default, Clone)] 11 | pub struct ZypperInstaller { 12 | pub name: String, 13 | pub version: String, 14 | pub dependencies: Vec, 15 | pub zypper_dependencies: Vec, 16 | pub packages: Option>, 17 | pub postinstall: Option, 18 | pub version_check: Option, 19 | pub non_interactive: bool, 20 | pub provider: String, 21 | pub session: Option, 22 | } 23 | 24 | impl From for ZypperInstaller { 25 | fn from(pkg: Package) -> Self { 26 | Self { 27 | name: pkg.name, 28 | packages: pkg.packages, 29 | zypper_dependencies: pkg.depends_on.unwrap_or(vec![]), 30 | provider: "zypper".into(), 31 | version_check: pkg.version_check, 32 | non_interactive: pkg.non_interactive.unwrap_or(true), 33 | ..Default::default() 34 | } 35 | } 36 | } 37 | 38 | impl ZypperInstaller { 39 | pub fn install_dependencies(&self) -> Result<(), Error> { 40 | if self.zypper_dependencies.is_empty() { 41 | return Ok(()); 42 | } 43 | 44 | println!( 45 | "-> Installing dependencies for {}", 46 | self.name.bright_green() 47 | ); 48 | let deps = self.zypper_dependencies.join(" "); 49 | zypper_install!(deps, "--non-interactive", self.session.clone()); 50 | Ok(()) 51 | } 52 | 53 | fn postinstall(&self) -> Result<(), Error> { 54 | if let Some(command) = self.postinstall.clone() { 55 | println!( 56 | "-> Running postinstall command:\n{}", 57 | command.bright_green() 58 | ); 59 | for cmd in command.split("\n") { 60 | exec_sh_with_output!(cmd, self.session.clone()); 61 | } 62 | } 63 | Ok(()) 64 | } 65 | } 66 | 67 | impl Installer for ZypperInstaller { 68 | fn install(&self) -> Result<(), Error> { 69 | if self.is_installed().is_ok() { 70 | if self.is_installed().unwrap() { 71 | println!( 72 | "-> {} is already installed, skipping", 73 | self.name().bright_green() 74 | ); 75 | return Ok(()); 76 | } 77 | } 78 | 79 | self.install_dependencies()?; 80 | 81 | if let Some(packages) = self.packages.clone() { 82 | let options = match self.non_interactive { 83 | true => "--non-interactive", 84 | false => "", 85 | }; 86 | let packages = packages.join(" "); 87 | let command = format!("sudo zypper install {} {}", options, packages); 88 | println!("-> Running {}", command.bright_green()); 89 | zypper_install!(packages, options, self.session.clone()); 90 | } 91 | 92 | self.postinstall()?; 93 | Ok(()) 94 | } 95 | 96 | fn is_installed(&self) -> Result { 97 | println!( 98 | "-> Checking if {} is already installed", 99 | self.name.bright_green() 100 | ); 101 | if let Some(command) = self.version_check.clone() { 102 | check_version!(self, command, self.session.clone()); 103 | return Ok(false); 104 | } 105 | let command = self.name.clone(); 106 | check_version!(self, command, self.session.clone()); 107 | Ok(false) 108 | } 109 | 110 | fn name(&self) -> &str { 111 | &self.name 112 | } 113 | 114 | fn version(&self) -> &str { 115 | &self.version 116 | } 117 | 118 | fn dependencies(&self) -> Vec { 119 | self.dependencies.clone() 120 | } 121 | 122 | fn is_default(&self) -> bool { 123 | true 124 | } 125 | 126 | fn provider(&self) -> &str { 127 | &self.provider 128 | } 129 | 130 | fn as_any(&self) -> &dyn Any { 131 | self 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /crates/installers/src/emerge.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_macros::{check_version, emerge_install, exec_sh_with_output}; 3 | use crosup_types::emerge::Package; 4 | use owo_colors::OwoColorize; 5 | use ssh2::Session; 6 | use std::{any::Any, io::BufRead, process::Stdio}; 7 | 8 | use super::Installer; 9 | 10 | #[derive(Default, Clone)] 11 | pub struct EmergeInstaller { 12 | pub name: String, 13 | pub version: String, 14 | pub dependencies: Vec, 15 | pub emerge_dependencies: Vec, 16 | pub packages: Option>, 17 | pub postinstall: Option, 18 | pub version_check: Option, 19 | pub ask: bool, 20 | pub verbose: bool, 21 | pub provider: String, 22 | pub session: Option, 23 | } 24 | 25 | impl From for EmergeInstaller { 26 | fn from(pkg: Package) -> Self { 27 | Self { 28 | name: pkg.name, 29 | packages: pkg.packages, 30 | emerge_dependencies: pkg.depends_on.unwrap_or(vec![]), 31 | provider: "emerge".into(), 32 | version_check: pkg.version_check, 33 | ask: pkg.ask.unwrap_or(false), 34 | verbose: pkg.verbose.unwrap_or(false), 35 | ..Default::default() 36 | } 37 | } 38 | } 39 | 40 | impl EmergeInstaller { 41 | pub fn install_dependencies(&self) -> Result<(), Error> { 42 | if self.emerge_dependencies.is_empty() { 43 | return Ok(()); 44 | } 45 | 46 | println!( 47 | "-> Installing dependencies for {}", 48 | self.name.bright_green() 49 | ); 50 | let deps = self.emerge_dependencies.join(" "); 51 | emerge_install!(deps, "--ask --verbose", self.session.clone()); 52 | Ok(()) 53 | } 54 | 55 | fn postinstall(&self) -> Result<(), Error> { 56 | if let Some(command) = self.postinstall.clone() { 57 | println!( 58 | "-> Running postinstall command:\n{}", 59 | command.bright_green() 60 | ); 61 | for cmd in command.split("\n") { 62 | exec_sh_with_output!(cmd, self.session.clone()); 63 | } 64 | } 65 | Ok(()) 66 | } 67 | } 68 | 69 | impl Installer for EmergeInstaller { 70 | fn install(&self) -> Result<(), Error> { 71 | if self.is_installed().is_ok() { 72 | if self.is_installed().unwrap() { 73 | println!( 74 | "-> {} is already installed, skipping", 75 | self.name().bright_green() 76 | ); 77 | return Ok(()); 78 | } 79 | } 80 | 81 | self.install_dependencies()?; 82 | 83 | if let Some(packages) = self.packages.clone() { 84 | let options = match self.ask { 85 | true => "--ask", 86 | false => "", 87 | }; 88 | let options = match self.verbose { 89 | true => format!("{} --verbose", options), 90 | false => format!("{}", options), 91 | }; 92 | let packages = packages.join(" "); 93 | let command = format!("sudo emerge install {} {}", options, packages); 94 | println!("-> Running {}", command.bright_green()); 95 | emerge_install!(packages, options, self.session.clone()); 96 | } 97 | 98 | self.postinstall()?; 99 | Ok(()) 100 | } 101 | 102 | fn is_installed(&self) -> Result { 103 | println!( 104 | "-> Checking if {} is already installed", 105 | self.name.bright_green() 106 | ); 107 | if let Some(command) = self.version_check.clone() { 108 | check_version!(self, command, self.session.clone()); 109 | return Ok(false); 110 | } 111 | let command = self.name.clone(); 112 | check_version!(self, command, self.session.clone()); 113 | Ok(false) 114 | } 115 | 116 | fn name(&self) -> &str { 117 | &self.name 118 | } 119 | 120 | fn version(&self) -> &str { 121 | &self.version 122 | } 123 | 124 | fn dependencies(&self) -> Vec { 125 | self.dependencies.clone() 126 | } 127 | 128 | fn is_default(&self) -> bool { 129 | true 130 | } 131 | 132 | fn provider(&self) -> &str { 133 | &self.provider 134 | } 135 | 136 | fn as_any(&self) -> &dyn Any { 137 | self 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /crates/types/src/configuration.rs: -------------------------------------------------------------------------------- 1 | use indexmap::IndexMap; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use crate::fleek::FleekConfiguration; 5 | 6 | use super::{ 7 | apk::ApkConfiguration, 8 | apt::{default_apt_install, AptConfiguration}, 9 | brew::{default_brew_install, BrewConfiguration}, 10 | curl::{default_curl_install, CurlConfiguration}, 11 | dnf::DnfConfiguration, 12 | emerge::EmergeConfiguration, 13 | git::{default_git_install, GitConfiguration}, 14 | install::InstallConfiguration, 15 | nix::{default_nix_install, NixConfiguration}, 16 | pacman::PacmanConfiguration, 17 | slackpkg::SlackpkgConfiguration, 18 | yum::YumConfiguration, 19 | zypper::ZypperConfiguration, 20 | }; 21 | 22 | pub enum ConfigFormat { 23 | TOML, 24 | HCL, 25 | } 26 | 27 | #[derive(Serialize, Deserialize, Debug, Clone)] 28 | pub struct Configuration { 29 | #[serde(skip_serializing_if = "Option::is_none")] 30 | pub packages: Option>, 31 | #[serde( 32 | skip_serializing_if = "Option::is_none", 33 | serialize_with = "hcl::ser::block" 34 | )] 35 | pub install: Option, 36 | #[serde( 37 | skip_serializing_if = "Option::is_none", 38 | serialize_with = "hcl::ser::labeled_block" 39 | )] 40 | pub brew: Option>, 41 | 42 | #[serde( 43 | skip_serializing_if = "Option::is_none", 44 | serialize_with = "hcl::ser::labeled_block" 45 | )] 46 | pub git: Option>, 47 | 48 | #[serde( 49 | skip_serializing_if = "Option::is_none", 50 | serialize_with = "hcl::ser::labeled_block" 51 | )] 52 | pub nix: Option>, 53 | 54 | #[serde( 55 | skip_serializing_if = "Option::is_none", 56 | serialize_with = "hcl::ser::labeled_block" 57 | )] 58 | pub curl: Option>, 59 | 60 | #[serde( 61 | skip_serializing_if = "Option::is_none", 62 | serialize_with = "hcl::ser::labeled_block" 63 | )] 64 | pub apt: Option>, 65 | 66 | #[serde( 67 | skip_serializing_if = "Option::is_none", 68 | serialize_with = "hcl::ser::labeled_block" 69 | )] 70 | pub yum: Option>, 71 | 72 | #[serde( 73 | skip_serializing_if = "Option::is_none", 74 | serialize_with = "hcl::ser::labeled_block" 75 | )] 76 | pub dnf: Option>, 77 | 78 | #[serde( 79 | skip_serializing_if = "Option::is_none", 80 | serialize_with = "hcl::ser::labeled_block" 81 | )] 82 | pub zypper: Option>, 83 | 84 | #[serde( 85 | skip_serializing_if = "Option::is_none", 86 | serialize_with = "hcl::ser::labeled_block" 87 | )] 88 | pub apk: Option>, 89 | 90 | #[serde( 91 | skip_serializing_if = "Option::is_none", 92 | serialize_with = "hcl::ser::labeled_block" 93 | )] 94 | pub pacman: Option>, 95 | 96 | #[serde( 97 | skip_serializing_if = "Option::is_none", 98 | serialize_with = "hcl::ser::labeled_block" 99 | )] 100 | pub emerge: Option>, 101 | 102 | #[serde( 103 | skip_serializing_if = "Option::is_none", 104 | serialize_with = "hcl::ser::labeled_block" 105 | )] 106 | pub slackpkg: Option>, 107 | 108 | #[serde( 109 | skip_serializing_if = "Option::is_none", 110 | serialize_with = "hcl::ser::labeled_block" 111 | )] 112 | pub fleek: Option>, 113 | } 114 | 115 | impl Default for Configuration { 116 | fn default() -> Self { 117 | Configuration { 118 | packages: None, 119 | install: None, 120 | brew: Some(default_brew_install()), 121 | git: Some(default_git_install()), 122 | nix: Some(default_nix_install()), 123 | curl: Some(default_curl_install()), 124 | apt: Some(default_apt_install()), 125 | yum: None, 126 | dnf: None, 127 | zypper: None, 128 | apk: None, 129 | pacman: None, 130 | emerge: None, 131 | slackpkg: None, 132 | fleek: None, 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /crates/installers/src/nix.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use owo_colors::OwoColorize; 3 | use ssh2::Session; 4 | use std::{any::Any, io::BufRead, process::Stdio}; 5 | 6 | use crosup_macros::{check_version, exec_bash_with_output}; 7 | use crosup_types::nix::Package; 8 | 9 | use super::Installer; 10 | 11 | #[derive(Default, Clone)] 12 | pub struct NixInstaller { 13 | pub name: String, 14 | pub version: String, 15 | pub dependencies: Vec, 16 | pub impure: Option, 17 | pub experimental_features: Option, 18 | pub accept_flake_config: Option, 19 | pub preinstall: Option, 20 | pub flake: String, 21 | pub version_check: Option, 22 | pub provider: String, 23 | pub session: Option, 24 | } 25 | 26 | impl From for NixInstaller { 27 | fn from(pkg: Package) -> Self { 28 | let mut dependencies = vec!["nix".into()]; 29 | dependencies.extend(pkg.depends_on.unwrap_or(vec![])); 30 | Self { 31 | name: pkg.name, 32 | impure: pkg.impure, 33 | experimental_features: pkg.experimental_features, 34 | accept_flake_config: pkg.accept_flake_config, 35 | preinstall: pkg.preinstall, 36 | flake: pkg.flake, 37 | dependencies, 38 | version_check: pkg.version_check, 39 | provider: "nix".into(), 40 | ..Default::default() 41 | } 42 | } 43 | } 44 | 45 | impl NixInstaller { 46 | fn preinstall(&self) -> Result<(), Error> { 47 | if let Some(command) = self.preinstall.clone() { 48 | println!("-> Running preinstall command:\n{}", command.bright_green()); 49 | for cmd in command.split("\n") { 50 | exec_bash_with_output!( 51 | format!( 52 | ". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && {}", 53 | cmd 54 | ), 55 | self.session.clone() 56 | ); 57 | } 58 | } 59 | Ok(()) 60 | } 61 | } 62 | 63 | impl Installer for NixInstaller { 64 | fn install(&self) -> Result<(), Error> { 65 | if self.is_installed().is_ok() { 66 | println!( 67 | "-> {} is already installed, skipping", 68 | self.name().bright_green() 69 | ); 70 | return Ok(()); 71 | } 72 | println!("-> 🚚 Installing {}", self.name().bright_green()); 73 | self.preinstall()?; 74 | 75 | let impure = match self.impure { 76 | Some(impure) => match impure { 77 | true => "--impure", 78 | false => "", 79 | }, 80 | None => "", 81 | }; 82 | 83 | let experimental_features = match self.experimental_features.clone() { 84 | Some(features) => format!("--experimental-features \"{}\"", features), 85 | None => "".to_string(), 86 | }; 87 | 88 | let accept_flake_config = match self.accept_flake_config { 89 | Some(accept) => match accept { 90 | true => "--accept-flake-config", 91 | false => "", 92 | }, 93 | None => "", 94 | }; 95 | 96 | let command = format!( 97 | r#". /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && \ 98 | nix profile install {} {} {} \ 99 | '{}'"#, 100 | impure, experimental_features, accept_flake_config, self.flake 101 | ); 102 | exec_bash_with_output!(command, self.session.clone()); 103 | 104 | Ok(()) 105 | } 106 | 107 | fn is_installed(&self) -> Result { 108 | println!( 109 | "-> Checking if {} is already installed", 110 | self.name.bright_green() 111 | ); 112 | if let Some(command) = self.version_check.clone() { 113 | check_version!(self, command, self.session.clone()); 114 | return Ok(false); 115 | } 116 | let command = self.name.clone(); 117 | check_version!(self, command, self.session.clone()); 118 | Ok(false) 119 | } 120 | 121 | fn name(&self) -> &str { 122 | &self.name 123 | } 124 | 125 | fn version(&self) -> &str { 126 | &self.version 127 | } 128 | 129 | fn dependencies(&self) -> Vec { 130 | self.dependencies.clone() 131 | } 132 | 133 | fn is_default(&self) -> bool { 134 | true 135 | } 136 | 137 | fn provider(&self) -> &str { 138 | &self.provider 139 | } 140 | 141 | fn as_any(&self) -> &dyn Any { 142 | self 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /crates/installers/src/fleek.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use crosup_macros::{check_version, exec_sh_with_output, fleek_install}; 3 | use crosup_types::fleek::Package; 4 | use owo_colors::OwoColorize; 5 | use ssh2::Session; 6 | use std::{any::Any, io::BufRead, path::Path, process::Stdio}; 7 | 8 | use super::Installer; 9 | 10 | #[derive(Default, Clone)] 11 | pub struct FleekInstaller { 12 | pub name: String, 13 | pub version: String, 14 | pub dependencies: Vec, 15 | pub fleek_dependencies: Vec, 16 | pub packages: Option>, 17 | pub postinstall: Option, 18 | pub version_check: Option, 19 | pub provider: String, 20 | pub session: Option, 21 | pub apply: bool, 22 | } 23 | 24 | impl From for FleekInstaller { 25 | fn from(pkg: Package) -> Self { 26 | Self { 27 | name: pkg.name, 28 | packages: pkg.packages, 29 | dependencies: vec!["nix".into()], 30 | fleek_dependencies: pkg.depends_on.unwrap_or(vec![]), 31 | provider: "fleek".into(), 32 | version_check: pkg.version_check, 33 | apply: pkg.apply.unwrap_or(true), 34 | ..Default::default() 35 | } 36 | } 37 | } 38 | 39 | impl FleekInstaller { 40 | pub fn fleek_init(&self) -> Result<(), Error> { 41 | println!("-> Checking if {} exists", "~/.fleek.yml".bright_green()); 42 | 43 | let home = std::env::var("HOME").unwrap(); 44 | let fleek_path = format!("{}/.fleek.yml", home); 45 | 46 | if Path::new(&fleek_path).exists() { 47 | println!("-> {} exists", "~/.fleek.yml".bright_green()); 48 | return Ok(()); 49 | } 50 | 51 | println!( 52 | "-> {} does not exist, please run {} first", 53 | "~/.fleek.yml".bright_green(), 54 | "nix run github:ublue-os/fleek -- init".bright_green() 55 | ); 56 | return Err(anyhow::anyhow!("Fleek not initialized")); 57 | } 58 | 59 | pub fn install_dependencies(&self) -> Result<(), Error> { 60 | if self.fleek_dependencies.is_empty() { 61 | return Ok(()); 62 | } 63 | 64 | println!( 65 | "-> Installing dependencies for {}", 66 | self.name.bright_green() 67 | ); 68 | let deps = self.fleek_dependencies.join(" "); 69 | fleek_install!(deps, "--apply", self.session.clone()); 70 | Ok(()) 71 | } 72 | 73 | fn postinstall(&self) -> Result<(), Error> { 74 | if let Some(command) = self.postinstall.clone() { 75 | println!( 76 | "-> Running postinstall command:\n{}", 77 | command.bright_green() 78 | ); 79 | for cmd in command.split("\n") { 80 | exec_sh_with_output!(cmd, self.session.clone()); 81 | } 82 | } 83 | Ok(()) 84 | } 85 | } 86 | 87 | impl Installer for FleekInstaller { 88 | fn install(&self) -> Result<(), Error> { 89 | self.fleek_init()?; 90 | self.install_dependencies()?; 91 | 92 | if let Some(packages) = self.packages.clone() { 93 | let options = if self.apply { "--apply" } else { "" }; 94 | let packages = packages.join(" "); 95 | let command = match self.apply { 96 | true => format!("nix run github:ublue-os/fleek add {} {}", options, packages), 97 | false => format!("nix run github:ublue-os/fleek add {}", packages), 98 | }; 99 | println!("-> Running {}", command.bright_green()); 100 | fleek_install!(packages, options, self.session.clone()); 101 | } 102 | 103 | self.postinstall()?; 104 | Ok(()) 105 | } 106 | 107 | fn is_installed(&self) -> Result { 108 | println!( 109 | "-> Checking if {} is already installed", 110 | self.name.bright_green() 111 | ); 112 | if let Some(command) = self.version_check.clone() { 113 | check_version!(self, command, self.session.clone()); 114 | return Ok(false); 115 | } 116 | let command = self.name.clone(); 117 | check_version!(self, command, self.session.clone()); 118 | Ok(false) 119 | } 120 | 121 | fn name(&self) -> &str { 122 | &self.name 123 | } 124 | 125 | fn version(&self) -> &str { 126 | &self.version 127 | } 128 | 129 | fn dependencies(&self) -> Vec { 130 | self.dependencies.clone() 131 | } 132 | 133 | fn is_default(&self) -> bool { 134 | true 135 | } 136 | 137 | fn provider(&self) -> &str { 138 | &self.provider 139 | } 140 | 141 | fn as_any(&self) -> &dyn Any { 142 | self 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /crates/tui/src/history.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use bat::PrettyPrinter; 3 | use crossterm::{ 4 | event::{self, DisableMouseCapture, Event, KeyCode}, 5 | execute, 6 | terminal::{disable_raw_mode, enable_raw_mode, LeaveAlternateScreen}, 7 | }; 8 | use std::{io, thread, time::Duration}; 9 | use tui::{ 10 | backend::CrosstermBackend, 11 | layout::{Constraint, Direction, Layout}, 12 | style::{Color, Style}, 13 | text::{Span, Spans}, 14 | widgets::{Block, List, ListItem}, 15 | Terminal, 16 | }; 17 | 18 | use crate::App; 19 | 20 | pub fn display_history(mut app: App) -> Result<(), Error> { 21 | enable_raw_mode()?; 22 | let stdout = io::stdout(); 23 | 24 | let backend = CrosstermBackend::new(stdout); 25 | let mut terminal = Terminal::new(backend)?; 26 | terminal.clear()?; 27 | 28 | render_initial_ui(&mut terminal, &mut app)?; 29 | 30 | loop { 31 | if let Event::Key(key) = event::read()? { 32 | match key.code { 33 | KeyCode::Char('q') => break, 34 | KeyCode::Up => { 35 | if app.selected_index > 0 { 36 | app.selected_index -= 1; 37 | } 38 | } 39 | KeyCode::Down => { 40 | if app.selected_index < app.items.len() - 1 { 41 | app.selected_index += 1; 42 | } 43 | } 44 | KeyCode::Enter => { 45 | let content = app.content.get(&app.selected_index).unwrap(); 46 | clear_screen(&mut terminal)?; 47 | let language = match app.title.ends_with(".hcl") { 48 | true => "hcl", 49 | false => "toml", 50 | }; 51 | 52 | PrettyPrinter::new() 53 | .input_from_bytes(content.as_bytes()) 54 | .language(language) 55 | .paging_mode(bat::PagingMode::Always) 56 | .line_numbers(true) 57 | .print() 58 | .unwrap(); 59 | return Ok(()); 60 | } 61 | _ => {} 62 | } 63 | } 64 | 65 | render_ui(&mut terminal, &mut app)?; 66 | thread::sleep(Duration::from_millis(10)); 67 | } 68 | 69 | clear_screen(&mut terminal)?; 70 | 71 | Ok(()) 72 | } 73 | 74 | pub fn clear_screen(terminal: &mut Terminal>) -> Result<(), Error> { 75 | disable_raw_mode()?; 76 | let mut stdout = io::stdout(); 77 | terminal.clear()?; 78 | execute!(stdout, LeaveAlternateScreen, DisableMouseCapture)?; 79 | Ok(()) 80 | } 81 | 82 | pub fn render_initial_ui( 83 | terminal: &mut Terminal>, 84 | app: &mut App, 85 | ) -> Result<(), Error> { 86 | terminal.draw(|f| { 87 | let size = f.size(); 88 | let chunks = Layout::default() 89 | .direction(Direction::Vertical) 90 | .constraints([Constraint::Length(3), Constraint::Min(0)].as_ref()) 91 | .split(size); 92 | 93 | let items: Vec = app 94 | .items 95 | .iter() 96 | .enumerate() 97 | .map(|(i, item)| { 98 | let style = if i == app.selected_index { 99 | Style::default().fg(Color::Yellow) 100 | } else { 101 | Style::default().fg(Color::White) 102 | }; 103 | ListItem::new(Spans::from(Span::styled(item, style))) 104 | }) 105 | .collect(); 106 | 107 | let list = List::new(items).block(tui::widgets::Block::default()); 108 | f.render_widget(list, chunks[1]); 109 | })?; 110 | Ok(()) 111 | } 112 | 113 | pub fn render_ui( 114 | terminal: &mut Terminal>, 115 | app: &mut App, 116 | ) -> Result<(), Error> { 117 | terminal.draw(|f| { 118 | let size = f.size(); 119 | let chunks = Layout::default() 120 | .direction(Direction::Vertical) 121 | .constraints([Constraint::Length(3), Constraint::Min(0)].as_ref()) 122 | .split(size); 123 | 124 | let items: Vec = app 125 | .items 126 | .iter() 127 | .enumerate() 128 | .map(|(i, item)| { 129 | let style = if i == app.selected_index { 130 | Style::default().fg(Color::Yellow) 131 | } else { 132 | Style::default().fg(Color::White) 133 | }; 134 | ListItem::new(Spans::from(Span::styled(item, style))) 135 | }) 136 | .collect(); 137 | 138 | let list = List::new(items).block(Block::default()); 139 | f.render_widget(list, chunks[1]); 140 | })?; 141 | Ok(()) 142 | } 143 | -------------------------------------------------------------------------------- /crates/installers/src/curl.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use indexmap::IndexMap; 3 | use owo_colors::OwoColorize; 4 | use ssh2::Session; 5 | use std::{any::Any, io::BufRead, process::Stdio}; 6 | 7 | use crosup_macros::{check_version, exec_bash_with_output}; 8 | use crosup_types::curl::Script; 9 | 10 | use super::Installer; 11 | 12 | #[derive(Default, Clone)] 13 | pub struct CurlInstaller { 14 | pub name: String, 15 | pub version: String, 16 | pub dependencies: Vec, 17 | pub url: String, 18 | pub enable_sudo: Option, 19 | pub version_check: Option, 20 | pub postinstall: Option, 21 | pub args: Option, 22 | pub env: Option>, 23 | pub shell: String, 24 | pub provider: String, 25 | pub session: Option, 26 | } 27 | 28 | impl From