From 9718b2de1f250a6d84d0bf6900d066590ab3d817 Mon Sep 17 00:00:00 2001 From: dh00mk3tu Date: Thu, 5 Mar 2026 23:56:28 +0530 Subject: [PATCH 1/7] Add CI/CD workflows and build improvements for multi-platform releases - Add GitHub Actions workflows for Linux, macOS, and Windows builds - Add dev build workflow for CI on push/PR to main - Clean Rust build cache in macOS build script to fix objc2 issues - Pin Tauri dependencies to 2.0.0 for build stability Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build-all-platforms.yml | 60 +++++++++++++++++++++++ .github/workflows/build-dev.yml | 41 ++++++++++++++++ .github/workflows/build-linux.yml | 48 ++++++++++++++++++ .github/workflows/linux-release.yml | 53 ++++++++++++++++++++ build-mac.sh | 6 +++ src-tauri/Cargo.toml | 6 +-- 6 files changed, 211 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/build-all-platforms.yml create mode 100644 .github/workflows/build-dev.yml create mode 100644 .github/workflows/build-linux.yml create mode 100644 .github/workflows/linux-release.yml diff --git a/.github/workflows/build-all-platforms.yml b/.github/workflows/build-all-platforms.yml new file mode 100644 index 0000000..af42a01 --- /dev/null +++ b/.github/workflows/build-all-platforms.yml @@ -0,0 +1,60 @@ +name: Build All Platforms + +on: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + build-tauri: + strategy: + fail-fast: false + matrix: + include: + - platform: "linux-x86_64" + os: ubuntu-latest + - platform: "macos-x86_64" + os: macos-latest + - platform: "macos-aarch64" + os: macos-latest + - platform: "windows-x86_64" + os: windows-latest + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install frontend dependencies + run: npm ci + + - name: Build frontend + run: npm run generate + + - name: Build Tauri app + uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tagName: ${{ github.ref_name }} + releaseName: "this.ssh v__VERSION__" + releaseBody: "See the assets to download this version and install." + releaseDraft: false + prerelease: false + includeDebug: false + includeUpdater: false + includeSource: true + platforms: ${{ matrix.platform }} + diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml new file mode 100644 index 0000000..de20907 --- /dev/null +++ b/.github/workflows/build-dev.yml @@ -0,0 +1,41 @@ +name: Development Build + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev + + - name: Install dependencies + run: npm ci + + - name: Build frontend + run: npm run generate + + - name: Build Tauri app (debug) + run: npm run tauri:build:linux + diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml new file mode 100644 index 0000000..2217c50 --- /dev/null +++ b/.github/workflows/build-linux.yml @@ -0,0 +1,48 @@ +name: Build Linux Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + build-linux: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install dependencies + run: npm ci + + - name: Build frontend + run: npm run generate + + - name: Build Tauri app + uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tagName: ${{ github.ref_name }} + releaseName: "this.ssh v__VERSION__" + releaseBody: "See the assets to download this version and install." + releaseDraft: false + prerelease: false + includeDebug: false + includeUpdater: false + includeSource: true + platforms: linux-x86_64 + diff --git a/.github/workflows/linux-release.yml b/.github/workflows/linux-release.yml new file mode 100644 index 0000000..bbf9c58 --- /dev/null +++ b/.github/workflows/linux-release.yml @@ -0,0 +1,53 @@ +name: Linux Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + +jobs: + build-linux: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev + + - name: Install dependencies + run: npm ci + + - name: Build frontend + run: npm run generate + + - name: Build Tauri app + uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tagName: ${{ github.ref_name }} + releaseName: "this.ssh v__VERSION__" + releaseBody: "See the assets to download this version and install." + releaseDraft: false + prerelease: false + includeDebug: false + includeUpdater: false + includeSource: true + platforms: linux-x86_64 + diff --git a/build-mac.sh b/build-mac.sh index 5822215..1b4e63b 100755 --- a/build-mac.sh +++ b/build-mac.sh @@ -30,6 +30,12 @@ npm install echo "๐Ÿ”จ Building Nuxt.js frontend..." npm run generate +# Clean Rust build cache to avoid objc2-exception-helper issues +echo "๐Ÿงน Cleaning Rust build cache..." +cd src-tauri +cargo clean +cd .. + # Check if dist directory exists and contains index.html if [ ! -f "dist/index.html" ]; then echo "โŒ Error: dist/index.html not found. Frontend build failed." diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 976f9af..6db6277 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -15,13 +15,13 @@ name = "app_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] -tauri-build = { version = "2.3.0", features = [] } +tauri-build = { version = "2.0.0", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4" -tauri = { version = "2.6.2", features = [] } -tauri-plugin-log = "2" +tauri = { version = "2.0.0", features = [] } +tauri-plugin-log = "2.0.0" dirs = "5.0" ssh-key = "0.6.7" From cd53932ce9f00a4c080af8fa612f0c64163b7e43 Mon Sep 17 00:00:00 2001 From: dh00mk3tu Date: Fri, 6 Mar 2026 00:05:51 +0530 Subject: [PATCH 2/7] CI/CD fixes --- .github/workflows/build-all-platforms.yml | 37 ++++++++-------- .github/workflows/build-dev.yml | 7 +-- .github/workflows/build-linux.yml | 48 -------------------- .github/workflows/linux-release.yml | 53 ----------------------- 4 files changed, 21 insertions(+), 124 deletions(-) delete mode 100644 .github/workflows/build-linux.yml delete mode 100644 .github/workflows/linux-release.yml diff --git a/.github/workflows/build-all-platforms.yml b/.github/workflows/build-all-platforms.yml index af42a01..8906f4d 100644 --- a/.github/workflows/build-all-platforms.yml +++ b/.github/workflows/build-all-platforms.yml @@ -2,8 +2,7 @@ name: Build All Platforms on: push: - tags: - - "v*" + branches: [main] workflow_dispatch: jobs: @@ -12,16 +11,16 @@ jobs: fail-fast: false matrix: include: - - platform: "linux-x86_64" - os: ubuntu-latest - - platform: "macos-x86_64" - os: macos-latest - - platform: "macos-aarch64" - os: macos-latest - - platform: "windows-x86_64" - os: windows-latest + - platform: "ubuntu-22.04" + args: "" + - platform: "macos-latest" + args: "--target aarch64-apple-darwin" + - platform: "macos-latest" + args: "--target x86_64-apple-darwin" + - platform: "windows-latest" + args: "" - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v4 @@ -35,7 +34,13 @@ jobs: - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: - components: rustfmt, clippy + targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }} + + - name: Install system dependencies (Linux) + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev - name: Install frontend dependencies run: npm ci @@ -48,13 +53,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tagName: ${{ github.ref_name }} + tagName: v__VERSION__ releaseName: "this.ssh v__VERSION__" releaseBody: "See the assets to download this version and install." releaseDraft: false prerelease: false - includeDebug: false - includeUpdater: false - includeSource: true - platforms: ${{ matrix.platform }} - + args: ${{ matrix.args }} diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index de20907..00a2e70 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -2,13 +2,13 @@ name: Development Build on: push: - branches: [main, develop] + branches: [develop] pull_request: branches: [main] jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Checkout code @@ -22,8 +22,6 @@ jobs: - name: Setup Rust uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - name: Install system dependencies run: | @@ -38,4 +36,3 @@ jobs: - name: Build Tauri app (debug) run: npm run tauri:build:linux - diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml deleted file mode 100644 index 2217c50..0000000 --- a/.github/workflows/build-linux.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Build Linux Release - -on: - push: - tags: - - "v*" - workflow_dispatch: - -jobs: - build-linux: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - cache: "npm" - - - name: Setup Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - - - name: Install dependencies - run: npm ci - - - name: Build frontend - run: npm run generate - - - name: Build Tauri app - uses: tauri-apps/tauri-action@v0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tagName: ${{ github.ref_name }} - releaseName: "this.ssh v__VERSION__" - releaseBody: "See the assets to download this version and install." - releaseDraft: false - prerelease: false - includeDebug: false - includeUpdater: false - includeSource: true - platforms: linux-x86_64 - diff --git a/.github/workflows/linux-release.yml b/.github/workflows/linux-release.yml deleted file mode 100644 index bbf9c58..0000000 --- a/.github/workflows/linux-release.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Linux Release - -on: - push: - tags: - - "v*" - workflow_dispatch: - -jobs: - build-linux: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - cache: "npm" - - - name: Setup Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev - - - name: Install dependencies - run: npm ci - - - name: Build frontend - run: npm run generate - - - name: Build Tauri app - uses: tauri-apps/tauri-action@v0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tagName: ${{ github.ref_name }} - releaseName: "this.ssh v__VERSION__" - releaseBody: "See the assets to download this version and install." - releaseDraft: false - prerelease: false - includeDebug: false - includeUpdater: false - includeSource: true - platforms: linux-x86_64 - From 6cbe30006e1747b5a7bd963fdee78db2122e2958 Mon Sep 17 00:00:00 2001 From: dh00mk3tu Date: Fri, 6 Mar 2026 00:06:30 +0530 Subject: [PATCH 3/7] CI/CD fixes and config.toml added --- src-tauri/.cargo/config.toml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src-tauri/.cargo/config.toml diff --git a/src-tauri/.cargo/config.toml b/src-tauri/.cargo/config.toml new file mode 100644 index 0000000..f05e732 --- /dev/null +++ b/src-tauri/.cargo/config.toml @@ -0,0 +1,14 @@ +[target.x86_64-apple-darwin] +linker = "x86_64-apple-darwin-clang" + +[target.aarch64-apple-darwin] +linker = "aarch64-apple-darwin-clang" + +[build] +target = "x86_64-apple-darwin" + +[env] +CC_x86_64_apple_darwin = "x86_64-apple-darwin-clang" +CXX_x86_64_apple_darwin = "x86_64-apple-darwin-clang++" +CC_aarch64_apple_darwin = "aarch64-apple-darwin-clang" +CXX_aarch64_apple_darwin = "aarch64-apple-darwin-clang++" From 04a580f4ec50f1d48ceabe747b380abee47bef17 Mon Sep 17 00:00:00 2001 From: dh00mk3tu Date: Fri, 6 Mar 2026 02:05:05 +0530 Subject: [PATCH 4/7] Remove macOS-only Cargo config that breaks cross-platform builds --- src-tauri/.cargo/config.toml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 src-tauri/.cargo/config.toml diff --git a/src-tauri/.cargo/config.toml b/src-tauri/.cargo/config.toml deleted file mode 100644 index f05e732..0000000 --- a/src-tauri/.cargo/config.toml +++ /dev/null @@ -1,14 +0,0 @@ -[target.x86_64-apple-darwin] -linker = "x86_64-apple-darwin-clang" - -[target.aarch64-apple-darwin] -linker = "aarch64-apple-darwin-clang" - -[build] -target = "x86_64-apple-darwin" - -[env] -CC_x86_64_apple_darwin = "x86_64-apple-darwin-clang" -CXX_x86_64_apple_darwin = "x86_64-apple-darwin-clang++" -CC_aarch64_apple_darwin = "aarch64-apple-darwin-clang" -CXX_aarch64_apple_darwin = "aarch64-apple-darwin-clang++" From a031fd64cb435fe1a9c078b3ecd0c1008551d079 Mon Sep 17 00:00:00 2001 From: dh00mk3tu Date: Fri, 6 Mar 2026 02:12:01 +0530 Subject: [PATCH 5/7] Switch default branch to master and add docs from main - Update CI/CD workflows to trigger on master instead of main - Copy README, LICENSE, banner image, and GitHub Pages config from main - Add update-readme workflow for auto-updating release table Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build-all-platforms.yml | 2 +- .github/workflows/build-dev.yml | 2 +- .github/workflows/update-readme.yml | 57 ++++++ .gitignore | 30 +--- Images/this-banner.png | Bin 0 -> 32536 bytes LICENSE | 201 ++++++++++++++++++++++ README.md | 106 +++++------- _config.yml | 7 + 8 files changed, 314 insertions(+), 91 deletions(-) create mode 100644 .github/workflows/update-readme.yml create mode 100644 Images/this-banner.png create mode 100644 LICENSE create mode 100644 _config.yml diff --git a/.github/workflows/build-all-platforms.yml b/.github/workflows/build-all-platforms.yml index 8906f4d..3f21409 100644 --- a/.github/workflows/build-all-platforms.yml +++ b/.github/workflows/build-all-platforms.yml @@ -2,7 +2,7 @@ name: Build All Platforms on: push: - branches: [main] + branches: [master] workflow_dispatch: jobs: diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index 00a2e70..af32183 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -4,7 +4,7 @@ on: push: branches: [develop] pull_request: - branches: [main] + branches: [master] jobs: build: diff --git a/.github/workflows/update-readme.yml b/.github/workflows/update-readme.yml new file mode 100644 index 0000000..7033be4 --- /dev/null +++ b/.github/workflows/update-readme.yml @@ -0,0 +1,57 @@ +name: Update README with releases + +on: + release: + types: [published] + workflow_dispatch: # allows manual run (for first-time setup) + +permissions: + contents: write + +jobs: + update-readme: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Get all releases + id: releases + run: | + releases=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases \ + | jq -r '.[] | "| " + .tag_name + " | " + .published_at[0:10] + " | [View Release](" + .html_url + ") |"' ) + echo "releases<> $GITHUB_ENV + echo "$releases" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Update README + run: | + # Build the markdown table with header + table="| Version | Date | Link |\n| ------- | ---- | ---- |\n$releases" + + awk -v repl="$table" ' + // { + print "" + print repl + next + } + // { + print "" + next + } + {print} + ' README.md > README.new + + mv README.new README.md + + - name: Commit changes + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add README.md + if git commit -m "chore: update README with releases"; then + git push + else + echo "No changes to commit" + fi diff --git a/.gitignore b/.gitignore index 01cc234..578ba2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,7 @@ -# Nuxt dev/build outputs -.output -.data -.nuxt -.nitro -.cache -dist - -# Node dependencies node_modules - -# Logs -logs -*.log -Documentation.md - -# Misc -.DS_Store -.fleet -.idea - -# Local env files -.env -.env.* -!.env.example +.nuxt +assets +components +src-tauri +.output/ +dist \ No newline at end of file diff --git a/Images/this-banner.png b/Images/this-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..3b0b73ec7ff9bba098fc24a282a306a03371f852 GIT binary patch literal 32536 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 C^w`}1 literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..874ba2b --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2025] [dh00mk3tu as Anirudh Rath] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 29c6b55..4817f2a 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,62 @@ -### Building From Source +# this.ssh -To build this.ssh from source, you will need to have the following libs installed: +![this.ssh Banner](https://github.com/dh00mk3tu/this.ssh/blob/main/Images/this-banner.png) -- Node.js (v22.0.0 or later) -- npm (v9.0.0 or later) -- TypeScript (v5.0.0 or later) -- Rust Compiler (v1.88.0 or later) -- Cargo (v1.88.0 or later) -- Tauri (v2.6.2, not tested on version v2.7.0 or later) +![GitHub release (latest by date)](https://img.shields.io/github/v/release/dh00mk3tu/this.ssh) -Clone the repository: +### ๐Ÿš€ Releases -```bash -git clone git@github.com:dh00mk3tu/this.ssh.git -``` + -Navigate to the project directory: +| Version | Date | Link | +|:-------------|:------------------|:------| +| release-v1.0.0 | 2025-08-20 | [View Release](https://github.com/dh00mk3tu/this.ssh/releases/tag/release-v1.0.0) | -```bash -cd this.ssh -``` + -Change branch to `master`: -Master branch has the latest stable code. +### Simple SSH Management Tool -```bash -git checkout master -``` +`main` branch is used for the intro, setup guide and LICENCE information. -Install the dependencies: +| Section | Description | +| --------------------------------------------------------------------------------- | ------------------------------------------------- | +| [Project Overview](#project-overview) | Overview of the project and its purpose | +| [Features - Roadmap](#features---roadmap) | List of features and future plans | +| [Installation](#installation) | Instructions on how to install the project | +| [Development Setup](https://github.com/dh00mk3tu/this.ssh/wiki/Development-Setup) | Instructions for building the project from source | -```bash -npm install -``` +### Project Overview -Build the project: +It was a regular summer afternoon and I found myself in a predcament situation. +I was setting up my work repository and for that I had to setup a SSH key to be able to push my code to the remote work repository. +Now, though the process of setting up a SSH key is quite simple, and has been document so many times, it wasn't really an issue; I've done it before countless times but I still had to check the documentation just in case because I always forget the exact commands to use since it happens not that regularly. -```bash -npm run tauri build -``` +This wasn't the actual problem though; I already had my personal SSH key setup and now I had another that was associated with work, both were loaded in my SSH agent and I had to switch between them all the time. After a while I was annoyed with it that I have to switch between the SSH keys, remember the exact commands to use, or visit some documentation/GPT to get the commands right. -#### Running the Project Locally +I though to myself, "There must be a better way to manage SSH keys and connections", the asnwer was to use a config file but I didn't find it elegant enough for my needs. I wanted something that would allow me to easily add, remove, and list SSH hosts without having to manually edit a config file or remember the exact commands. -To run and to test the project locally, you need to run both the Tauri and the NUXT development servers. -Ideally I run one terminal and split it into two panes, one for each server (tmux), or tabs. +Hence, `this.ssh` was born. -Start the Tauri development server: +`this.ssh` is a simple SSH management tool that allows users to manage their SSH connections and configurations easily. It provides a user-friendly interface for adding, removing, and listing SSH hosts. -```bash -cargo tauri dev -``` +### Features - Roadmap -Start the NUXT development server: +| Features - Complete | Release Date | +| ---------------------------------- | ------------ | +| List SSH Keys | 30/07/2025 | +| Onload SSH Keys into the SSH agent | 30/07/2025 | +| Create SSH Keys | 13/08/2025 | +| Remove SSH Keys | 19/08/2025 | +| Documentation - Initial Phase | 19/08/2025 | -```bash -npm run dev -``` +--- -The application will fire a native window and the application will work within that window only. The application will not work in a browser, it is a native application amd Tauri APIs do not work in a browser environment. +| Features - Inprogress | Release Date | +| ----------------------------------- | ------------ | +| Offload SSH Keys into the SSH agent | TBR | +| Create User/User Auth | TBR | +| Save your keys to the cloud | TBR | -## Features +### Installation -### SSH Key Management - -- **View SSH Keys**: Scan and display all SSH keys in your `~/.ssh/` directory -- **SSH Agent Integration**: Show which keys are currently loaded in your SSH agent -- **Key Status**: Display whether each key is active (loaded) or inactive -- **Copy Public Keys**: Copy public key content to clipboard for easy sharing - -### SSH Key Creation - -- **Create New Keys**: Generate new SSH keys directly from the application -- **Multiple Key Types**: Support for RSA, Ed25519, and ECDSA key types -- **Configurable Key Sizes**: - - RSA: 2048 or 4096 bits - - ECDSA: 256, 384, or 521 bits - - Ed25519: 256 bits (fixed) -- **Optional Passphrase**: Add passphrase protection to your keys -- **Smart Naming**: Automatic filename generation based on key type and email - -### Key Types Supported - -- **RSA**: Traditional RSA keys with configurable bit lengths -- **Ed25519**: Modern, secure keys (recommended for new deployments) -- **ECDSA**: Elliptic curve keys with various curve sizes +Installation guide Will be updated soon once the first release is out. diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..2e3501a --- /dev/null +++ b/_config.yml @@ -0,0 +1,7 @@ +remote_theme: pages-themes/cayman@v0.2.0 +plugins: + - jekyll-remote-theme + +title: this.ssh +description: The simple SSH management tool +show_downloads: true From 38f1db7a9ba1e7a2fb0d6ab7cd223a7dc4d0a63e Mon Sep 17 00:00:00 2001 From: dh00mk3tu Date: Fri, 6 Mar 2026 02:19:03 +0530 Subject: [PATCH 6/7] Add missing Linux system deps for Tauri v2 build Add libsoup-3.0-dev and libjavascriptcoregtk-4.0-dev to fix soup3-sys build failure in CI. --- .github/workflows/build-all-platforms.yml | 2 +- .github/workflows/build-dev.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-all-platforms.yml b/.github/workflows/build-all-platforms.yml index 3f21409..3581af1 100644 --- a/.github/workflows/build-all-platforms.yml +++ b/.github/workflows/build-all-platforms.yml @@ -40,7 +40,7 @@ jobs: if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev + sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev libjavascriptcoregtk-4.0-dev - name: Install frontend dependencies run: npm ci diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index af32183..f5090b1 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -26,7 +26,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev + sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev libjavascriptcoregtk-4.0-dev - name: Install dependencies run: npm ci From 1af463e2cbf20537b791bb3c0c18be36a7dde176 Mon Sep 17 00:00:00 2001 From: dh00mk3tu Date: Fri, 6 Mar 2026 02:35:32 +0530 Subject: [PATCH 7/7] Fix Linux deps: use WebKit2GTK 4.1 for Tauri v2 Co-Authored-By: Claude Opus 4.6 --- .github/workflows/build-all-platforms.yml | 2 +- .github/workflows/build-dev.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-all-platforms.yml b/.github/workflows/build-all-platforms.yml index 3581af1..009b78d 100644 --- a/.github/workflows/build-all-platforms.yml +++ b/.github/workflows/build-all-platforms.yml @@ -40,7 +40,7 @@ jobs: if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev libjavascriptcoregtk-4.0-dev + sudo apt-get install -y libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev libjavascriptcoregtk-4.1-dev - name: Install frontend dependencies run: npm ci diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index f5090b1..b41f9e5 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -26,7 +26,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.0-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev libjavascriptcoregtk-4.0-dev + sudo apt-get install -y libwebkit2gtk-4.1-dev libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev libjavascriptcoregtk-4.1-dev - name: Install dependencies run: npm ci