diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2d8ace2e..d864895f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -3,7 +3,6 @@ on: push: tags: - v*.*.* - jobs: create-release: name: create-release @@ -23,6 +22,95 @@ jobs: uses: ./.github/workflows/sbom.yml with: upload_url: ${{ needs.create-release.outputs.upload_url }} + ubuntu-22-04-build: + needs: + - create-release + runs-on: + - self-hosted + - Linux + - ${{ matrix.architecture }} + strategy: + fail-fast: false + matrix: + architecture: [ARM64, X64] + include: + - architecture: ARM64 + deb_arch: arm64 + binary_arch: aarch64 + - architecture: X64 + deb_arch: amd64 + binary_arch: x86_64 + container: + image: ubuntu:22.04 + env: + DEBIAN_FRONTEND: noninteractive + HOME: /root + RUSTUP_HOME: /root/.rustup + CARGO_HOME: /root/.cargo + steps: + - name: git install + run: | + apt-get update + apt-get install -y git curl ca-certificates + git config --global --add safe.directory '*' + - uses: actions/checkout@v5 + with: + submodules: "recursive" + - uses: pnpm/action-setup@v4 + with: + version: 10.17 + run_install: false + - uses: actions/setup-node@v5 + with: + node-version: "24" + - name: Get pnpm store directory + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> ${GITHUB_ENV} + - name: Write release version + run: | + VERSION=$(echo ${GITHUB_REF_NAME#v} | cut -d '-' -f1) + echo Version: $VERSION + echo "VERSION=$VERSION" >> ${GITHUB_ENV} + - uses: actions/cache@v4 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-build-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-build-store- + - name: Install Node dependencies + run: pnpm install --frozen-lockfile + - uses: dtolnay/rust-toolchain@stable + - name: Install dependencies + run: | + apt-get install -y build-essential libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf libssl-dev libxdo-dev unzip protobuf-compiler libprotobuf-dev rpm + - name: Build packages + uses: tauri-apps/tauri-action@v0.5.23 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + args: "--bundles deb" + - name: Upload DEB + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} + asset_path: src-tauri/target/release/bundle/deb/defguard-client_${{ env.VERSION }}_${{ matrix.deb_arch }}.deb + asset_name: defguard-client_${{ env.VERSION }}_${{ matrix.deb_arch }}_ubuntu-22-04-lts.deb + asset_content_type: application/octet-stream + - name: Install ruby with deb-s3 + if: matrix.build != 'freebsd' + run: | + apt-get install -y ruby + gem install deb-s3 + echo "$(ruby -r rubygems -e 'puts Gem.user_dir')/bin" >> $GITHUB_PATH + - name: Upload DEB to APT repository + run: | + COMPONENT=$([[ "${{ github.ref_name }}" == *"-"* ]] && echo "pre-release" || echo "release") # if tag contain "-" assume it's pre-release. + + deb-s3 upload -l --bucket=apt.defguard.net --access-key-id=${{ secrets.AWS_ACCESS_KEY_APT }} --secret-access-key=${{ secrets.AWS_SECRET_KEY_APT }} --s3-region=eu-north-1 --no-fail-if-exists --codename=bookworm --component="$COMPONENT" src-tauri/target/release/bundle/deb/defguard-client_${{ env.VERSION }}_${{ matrix.deb_arch }}.deb + build-linux: needs: @@ -468,3 +556,40 @@ jobs: asset_path: defguard-client-signed.msi asset_name: defguard-client_${{ env.VERSION }}_x64_en-US.msi asset_content_type: application/octet-stream + + apt-sign: + needs: + - build-linux + - ubuntu-22-04-build + runs-on: + - self-hosted + - Linux + - X64 + strategy: + fail-fast: false + steps: + - name: Sign APT repository + run: | + export AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_APT }} + export AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_KEY_APT }} + export AWS_REGION=eu-north-1 + sudo apt update -y + sudo apt install -y awscli curl jq + + for DIST in trixie bookworm; do + aws s3 cp s3://apt.defguard.net/dists/${DIST}/Release . + + curl -X POST "${{ secrets.DEFGUARD_SIGNING_URL }}?signature_type=both" \ + -H "Authorization: Bearer ${{ secrets.DEFGUARD_SIGNING_API_KEY }}" \ + -F "file=@Release" \ + -o response.json + + cat response.json | jq -r '.files["Release.gpg"].content' | base64 --decode > Release.gpg + cat response.json | jq -r '.files.Release.content' | base64 --decode > InRelease + + aws s3 cp Release.gpg s3://apt.defguard.net/dists/${DIST}/ --acl public-read + aws s3 cp InRelease s3://apt.defguard.net/dists/${DIST}/ --acl public-read + + done + (aws s3 ls s3://apt.defguard.net/dists/ --recursive; aws s3 ls s3://apt.defguard.net/pool/ --recursive) | awk '{print ""$4"
"}' > index.html + aws s3 cp index.html s3://apt.defguard.net/ --acl public-read diff --git a/package.json b/package.json index ecc6c437..03e77a3b 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@tauri-apps/plugin-notification": "^2.3.1", "@tauri-apps/plugin-opener": "^2.5.0", "@tauri-apps/plugin-os": "^2.3.1", + "@tauri-apps/plugin-process": "^2.3.0", "@tauri-apps/plugin-window-state": "^2.4.0", "@types/byte-size": "^8.1.2", "@use-gesture/react": "^10.3.1", @@ -92,6 +93,7 @@ "react-click-away-listener": "^2.4.0", "react-dom": "^19.2.0", "react-hook-form": "^7.63.0", + "react-hotkeys-hook": "^5.2.1", "react-loading-skeleton": "^3.5.0", "react-markdown": "^10.1.0", "react-qr-code": "^2.0.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11375949..7a8fc6b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: '@tauri-apps/plugin-os': specifier: ^2.3.1 version: 2.3.1 + '@tauri-apps/plugin-process': + specifier: ^2.3.0 + version: 2.3.0 '@tauri-apps/plugin-window-state': specifier: ^2.4.0 version: 2.4.0 @@ -143,6 +146,9 @@ importers: react-hook-form: specifier: ^7.63.0 version: 7.63.0(react@19.2.0) + react-hotkeys-hook: + specifier: ^5.2.1 + version: 5.2.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react-loading-skeleton: specifier: ^3.5.0 version: 3.5.0(react@19.2.0) @@ -1210,6 +1216,9 @@ packages: '@tauri-apps/plugin-os@2.3.1': resolution: {integrity: sha512-ty5V8XDUIFbSnrk3zsFoP3kzN+vAufYzalJSlmrVhQTImIZa1aL1a03bOaP2vuBvfR+WDRC6NgV2xBl8G07d+w==} + '@tauri-apps/plugin-process@2.3.0': + resolution: {integrity: sha512-0DNj6u+9csODiV4seSxxRbnLpeGYdojlcctCuLOCgpH9X3+ckVZIEj6H7tRQ7zqWr7kSTEWnrxtAdBb0FbtrmQ==} + '@tauri-apps/plugin-window-state@2.4.0': resolution: {integrity: sha512-hRSzPNi2NG0lPFthfVY0V5C1MyWN/gGaQtQYw7i9zZhLzrhZveHZ2omHG1rIiIsjfTGbO7fhjydSoeTTK9GqLw==} @@ -2465,6 +2474,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 + react-hotkeys-hook@5.2.1: + resolution: {integrity: sha512-xbKh6zJxd/vJHT4Bw4+0pBD662Fk20V+VFhLqciCg+manTVO4qlqRqiwFOYelfHN9dBvWj9vxaPkSS26ZSIJGg==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -3854,6 +3869,10 @@ snapshots: dependencies: '@tauri-apps/api': 2.8.0 + '@tauri-apps/plugin-process@2.3.0': + dependencies: + '@tauri-apps/api': 2.8.0 + '@tauri-apps/plugin-window-state@2.4.0': dependencies: '@tauri-apps/api': 2.8.0 @@ -5297,6 +5316,11 @@ snapshots: dependencies: react: 19.2.0 + react-hotkeys-hook@5.2.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0): + dependencies: + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + react-is@16.13.1: {} react-is@18.3.1: {} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index fd9f542a..a5d3ae86 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1428,6 +1428,7 @@ dependencies = [ "tauri-plugin-notification", "tauri-plugin-opener", "tauri-plugin-os", + "tauri-plugin-process", "tauri-plugin-single-instance", "tauri-plugin-window-state", "thiserror 2.0.17", @@ -6639,6 +6640,16 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "tauri-plugin-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7461c622a5ea00eb9cd9f7a08dbd3bf79484499fd5c21aa2964677f64ca651ab" +dependencies = [ + "tauri", + "tauri-plugin", +] + [[package]] name = "tauri-plugin-single-instance" version = "2.3.6" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9bfbe204..d78acda2 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -112,6 +112,7 @@ x25519-dalek = { version = "2", features = [ "serde", "static_secrets", ] } +tauri-plugin-process = "2.3.0" [target.'cfg(unix)'.dependencies] nix = { version = "0.30.1", features = ["user", "fs"] } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index c171d2b6..d8b4de1b 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -48,6 +48,7 @@ "os:allow-hostname", "dialog:default", "clipboard-manager:allow-write-text", + "process:allow-exit", { "identifier": "http:default", "allow": [ diff --git a/src-tauri/deny.toml b/src-tauri/deny.toml index b6298cda..47d8a68d 100644 --- a/src-tauri/deny.toml +++ b/src-tauri/deny.toml @@ -87,11 +87,11 @@ ignore = [ { id = "RUSTSEC-2024-0420", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, { id = "RUSTSEC-2025-0052", reason = "Discontinued, but dark-light v2.0.0 needs it" }, { id = "RUSTSEC-2025-0057", reason = "Tauri needs it" }, - { id = "RUSTSEC-2025-0075", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2025-0080", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2025-0081", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2025-0098", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, - { id = "RUSTSEC-2025-0100", reason = "Tauri v2 GTK3 dependency (unmaintained)" }, + { id = "RUSTSEC-2025-0075", reason = "Tauri v2 dependency (unmaintained)" }, + { id = "RUSTSEC-2025-0080", reason = "Tauri v2 dependency (unmaintained)" }, + { id = "RUSTSEC-2025-0081", reason = "Tauri v2 dependency (unmaintained)" }, + { id = "RUSTSEC-2025-0098", reason = "Tauri v2 dependency (unmaintained)" }, + { id = "RUSTSEC-2025-0100", reason = "Tauri v2 dependency (unmaintained)" }, ] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. diff --git a/src-tauri/src/bin/defguard-client.rs b/src-tauri/src/bin/defguard-client.rs index 03c36f1e..e0237f9f 100644 --- a/src-tauri/src/bin/defguard-client.rs +++ b/src-tauri/src/bin/defguard-client.rs @@ -160,6 +160,7 @@ fn main() { .plugin(tauri_plugin_window_state::Builder::new().build()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) + .plugin(tauri_plugin_process::init()) .setup(|app| { // Register for linux and dev windows builds #[cfg(any(target_os = "linux", all(debug_assertions, windows)))] diff --git a/src/components/App/App.tsx b/src/components/App/App.tsx index 5bc83de6..df7132cf 100644 --- a/src/components/App/App.tsx +++ b/src/components/App/App.tsx @@ -4,7 +4,7 @@ import '../../shared/scss/index.scss'; import { QueryClient } from '@tanstack/query-core'; import { QueryClientProvider } from '@tanstack/react-query'; -import { debug } from '@tauri-apps/plugin-log'; +import { debug, info } from '@tauri-apps/plugin-log'; import { openUrl } from '@tauri-apps/plugin-opener'; import dayjs from 'dayjs'; import customParseData from 'dayjs/plugin/customParseFormat'; @@ -38,6 +38,8 @@ import { useTheme } from '../../shared/defguard-ui/hooks/theme/useTheme'; import { ThemeProvider } from '../../shared/providers/ThemeProvider/ThemeProvider'; import { routes } from '../../shared/routes'; import { ApplicationUpdateManager } from '../ApplicationUpdateManager/ApplicationUpdateManager'; +import { exit } from '@tauri-apps/plugin-process'; +import { useHotkeys } from 'react-hotkeys-hook'; dayjs.extend(duration); dayjs.extend(utc); @@ -186,6 +188,12 @@ export const App = () => { }; }, []); + // register ctrl+q keyboard shortcut + useHotkeys('ctrl+q', () => { + info("Ctrl-Q pressed, exiting."); + exit(0); + }); + if (!appLoaded) return null; return (