diff --git a/.github/workflows/flathub.yml b/.github/workflows/flathub.yml
index 98d95e02..5ecad626 100644
--- a/.github/workflows/flathub.yml
+++ b/.github/workflows/flathub.yml
@@ -16,6 +16,21 @@ jobs:
- name: Checkout app repo
uses: actions/checkout@v4
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: "22"
+
+ - name: Install source generators
+ run: |
+ pip install flatpak-node-generator tomlkit aiohttp
+ git clone --depth 1 https://github.com/flatpak/flatpak-builder-tools flatpak/flatpak-builder-tools
+
- name: Run update-manifest.sh
run: bash flatpak/update-manifest.sh "${{ github.event.release.tag_name }}"
@@ -29,6 +44,9 @@ jobs:
- name: Copy updated files to Flathub repo
run: |
cp flatpak/app.yaak.Yaak.yml flathub-repo/
+ cp flatpak/app.yaak.Yaak.metainfo.xml flathub-repo/
+ cp flatpak/cargo-sources.json flathub-repo/
+ cp flatpak/node-sources.json flathub-repo/
cp LICENSE flathub-repo/
sed -i 's|path: \.\./LICENSE|path: LICENSE|' flathub-repo/app.yaak.Yaak.yml
diff --git a/.gitignore b/.gitignore
index a8773a8a..40c48bd8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,6 @@ crates-tauri/yaak-app/tauri.worktree.conf.json
# Flatpak build artifacts
flatpak-repo/
.flatpak-builder/
+flatpak/flatpak-builder-tools/
+flatpak/cargo-sources.json
+flatpak/node-sources.json
diff --git a/crates/yaak-templates/build-wasm.cjs b/crates/yaak-templates/build-wasm.cjs
new file mode 100644
index 00000000..4f86941b
--- /dev/null
+++ b/crates/yaak-templates/build-wasm.cjs
@@ -0,0 +1,8 @@
+const { execSync } = require('node:child_process');
+
+if (process.env.SKIP_WASM_BUILD === '1') {
+ console.log('Skipping wasm-pack build (SKIP_WASM_BUILD=1)');
+ return;
+}
+
+execSync('wasm-pack build --target bundler', { stdio: 'inherit' });
diff --git a/crates/yaak-templates/package.json b/crates/yaak-templates/package.json
index f4c75149..9826bfb6 100644
--- a/crates/yaak-templates/package.json
+++ b/crates/yaak-templates/package.json
@@ -6,7 +6,7 @@
"scripts": {
"bootstrap": "npm run build",
"build": "run-s build:*",
- "build:pack": "wasm-pack build --target bundler",
+ "build:pack": "node build-wasm.cjs",
"build:clean": "rimraf ./pkg/.gitignore"
},
"devDependencies": {
diff --git a/flatpak/app.yaak.Yaak.metainfo.xml b/flatpak/app.yaak.Yaak.metainfo.xml
index 6fd4120d..83f52d38 100644
--- a/flatpak/app.yaak.Yaak.metainfo.xml
+++ b/flatpak/app.yaak.Yaak.metainfo.xml
@@ -52,6 +52,5 @@
-
diff --git a/flatpak/app.yaak.Yaak.yml b/flatpak/app.yaak.Yaak.yml
index ad4e5817..d5f6119c 100644
--- a/flatpak/app.yaak.Yaak.yml
+++ b/flatpak/app.yaak.Yaak.yml
@@ -3,6 +3,11 @@ runtime: org.gnome.Platform
runtime-version: "49"
sdk: org.gnome.Sdk
command: yaak-app
+
+sdk-extensions:
+ - org.freedesktop.Sdk.Extension.node22
+ - org.freedesktop.Sdk.Extension.rust-stable
+
rename-desktop-file: yaak.desktop
rename-icon: yaak-app
@@ -36,26 +41,135 @@ modules:
- name: yaak
buildsystem: simple
+ build-options:
+ append-path: /app/bin:/usr/lib/sdk/node22/bin:/usr/lib/sdk/rust-stable/bin
+ env:
+ CARGO_HOME: /run/build/yaak/cargo
+ XDG_CACHE_HOME: /run/build/yaak/flatpak-node/cache
+ npm_config_cache: /run/build/yaak/flatpak-node/npm-cache
+ npm_config_offline: "true"
+ npm_config_nodedir: /usr/lib/sdk/node22
+ NODE_OPTIONS: --max_old_space_size=4096
+
build-commands:
- - ar -x yaak.deb
- - tar -xf data.tar.gz
- - mv usr/bin/* /app/bin
- - mv usr/lib/* /app/lib
- - mv usr/share/* /app/share
+ # Vendor Node.js binary (sidecar for plugin runtime)
+ - mkdir -p crates-tauri/yaak-app/vendored/node
+ - install -Dm755 vendored-node/bin/node crates-tauri/yaak-app/vendored/node/yaaknode
+
+ # Vendor protoc binary and includes
+ - mkdir -p crates-tauri/yaak-app/vendored/protoc
+ - install -Dm755 protoc-bin/protoc crates-tauri/yaak-app/vendored/protoc/yaakprotoc
+ - mkdir -p crates-tauri/yaak-app/vendored/protoc/include && cp -r protoc-bin/google crates-tauri/yaak-app/vendored/protoc/include/google
+
+ # Patch lockfile: add resolved URLs for nested workspace deps that npm
+ # omits (see https://github.com/npm/cli/issues/4460)
+ - >-
+ node -e "const fs=require('fs');
+ const p='package-lock.json';
+ const d=JSON.parse(fs.readFileSync(p,'utf-8'));
+ for(const[n,info]of Object.entries(d.packages||{})){
+ if(!n||info.link||info.resolved||!n.includes('node_modules/')||!info.version)continue;
+ const pkg=n.split('node_modules/').pop();
+ const base=pkg.split('/').pop();
+ info.resolved='https://registry.npmjs.org/'+pkg+'/-/'+base+'-'+info.version+'.tgz';
+ }fs.writeFileSync(p,JSON.stringify(d,null,2));"
+
+ # Install npm dependencies offline
+ - npm ci --offline
+
+ # Pre-fetch Cargo dependencies offline
+ - cargo --offline fetch --manifest-path Cargo.toml
+
+ # Skip wasm-pack build (pre-built wasm is checked into the repo)
+ - >-
+ node -e "const fs=require('fs');
+ const p='crates/yaak-templates/package.json';
+ const d=JSON.parse(fs.readFileSync(p));
+ d.scripts['build:pack']='echo Skipping wasm-pack build';
+ fs.writeFileSync(p,JSON.stringify(d,null,2));"
+
+ # Build all workspace packages (frontend, plugins, wasm, plugin-runtime)
+ - npm run build
+
+ # Copy built plugins to vendored directory
+ - npm run vendor:vendor-plugins
+
+ # Build the Tauri app (cargo build directly to avoid inotify limits from tauri CLI)
+ - cargo build --offline --release -p yaak-app
+
+ # Install binary
+ - install -Dm755 target/release/yaak-app /app/bin/yaak-app
+
+ # Install icons from source
+ - install -Dm644 crates-tauri/yaak-app/icons/release/32x32.png /app/share/icons/hicolor/32x32/apps/yaak-app.png
+ - install -Dm644 crates-tauri/yaak-app/icons/release/64x64.png /app/share/icons/hicolor/64x64/apps/yaak-app.png
+ - install -Dm644 crates-tauri/yaak-app/icons/release/128x128.png /app/share/icons/hicolor/128x128/apps/yaak-app.png
+ - install -Dm644 crates-tauri/yaak-app/icons/release/icon.png /app/share/icons/hicolor/512x512/apps/yaak-app.png
+
+ # Install desktop file
+ - >-
+ printf '[Desktop Entry]\nCategories=Development;\nComment=The API client for modern developers\nExec=yaak-app\nIcon=yaak-app\nName=Yaak\nStartupWMClass=yaak\nTerminal=false\nType=Application\n'
+ > yaak.desktop
+ - install -Dm644 yaak.desktop /app/share/applications/yaak.desktop
+
+ # Install metainfo and license
+ - install -Dm644 app.yaak.Yaak.metainfo.xml /app/share/metainfo/app.yaak.Yaak.metainfo.xml
- install -Dm644 LICENSE /app/share/licenses/app.yaak.Yaak/LICENSE
sources:
- - type: file
- dest-filename: yaak.deb
- url: https://github.com/mountain-loop/yaak/releases/download/v2026.1.2/yaak_2026.1.2_amd64.deb
- sha256: "c4236b5bcf391e579dc79b71c3b5c58f6f9bfc6c175fc70426d0ca85799beba5"
+ # Application source
+ - type: git
+ url: https://github.com/mountain-loop/yaak.git
+ tag: v2026.1.2
+ commit: bd7e840a5700ddefb5ef1f22771cc5555000f777
+ x-checker-data:
+ type: git
+ tag-pattern: ^v(\d+\.\d+\.\d+)$
+
+ # Offline npm dependencies
+ - node-sources.json
+
+ # Offline Cargo dependencies
+ - cargo-sources.json
+
+ # Vendored Node.js binary (x86_64)
+ - type: archive
+ url: https://nodejs.org/download/release/v24.11.1/node-v24.11.1-linux-x64.tar.gz
+ sha256: 58a5ff5cc8f2200e458bea22e329d5c1994aa1b111d499ca46ec2411d58239ca
+ strip-components: 1
+ dest: vendored-node
only-arches:
- x86_64
- - type: file
- dest-filename: yaak.deb
- url: https://github.com/mountain-loop/yaak/releases/download/v2026.1.2/yaak_2026.1.2_arm64.deb
- sha256: "9ba9b7c9df56ffb9b801e40cb38685f1650cf7e2f9e85dad0ae3329f8e01ff6d"
+
+ # Vendored Node.js binary (aarch64)
+ - type: archive
+ url: https://nodejs.org/download/release/v24.11.1/node-v24.11.1-linux-arm64.tar.gz
+ sha256: 0dc93ec5c798b0d347f068db6d205d03dea9a71765e6a53922b682b91265d71f
+ strip-components: 1
+ dest: vendored-node
only-arches:
- aarch64
+
+ # Vendored protoc binary and includes (x86_64)
+ - type: archive
+ url: https://github.com/protocolbuffers/protobuf/releases/download/v33.1/protoc-33.1-linux-x86_64.zip
+ sha256: f3340e28a83d1c637d8bafdeed92b9f7db6a384c26bca880a6e5217b40a4328b
+ dest: protoc-bin
+ only-arches:
+ - x86_64
+
+ # Vendored protoc binary and includes (aarch64)
+ - type: archive
+ url: https://github.com/protocolbuffers/protobuf/releases/download/v33.1/protoc-33.1-linux-aarch_64.zip
+ sha256: 6018147740548e0e0f764408c87f4cd040e6e1c1203e13aeacaf811892b604f3
+ dest: protoc-bin
+ only-arches:
+ - aarch64
+
+ # License file
- type: file
path: ../LICENSE
+
+ # Metainfo file (not in tagged source yet)
+ - type: file
+ path: app.yaak.Yaak.metainfo.xml
diff --git a/flatpak/fix-lockfile.mjs b/flatpak/fix-lockfile.mjs
new file mode 100644
index 00000000..aa3826de
--- /dev/null
+++ b/flatpak/fix-lockfile.mjs
@@ -0,0 +1,73 @@
+#!/usr/bin/env node
+
+// Adds missing `resolved` and `integrity` fields to npm package-lock.json.
+//
+// npm sometimes omits these fields for nested dependencies inside workspace
+// packages. This breaks offline installs and tools like flatpak-node-generator
+// that need explicit tarball URLs for every package.
+//
+// Based on https://github.com/grant-dennison/npm-package-lock-add-resolved
+// (MIT License, Copyright (c) 2024 Grant Dennison)
+
+import { readFile, writeFile } from "node:fs/promises";
+import { get } from "node:https";
+
+const lockfilePath = process.argv[2] || "package-lock.json";
+
+function fetchJson(url) {
+ return new Promise((resolve, reject) => {
+ get(url, (res) => {
+ let data = "";
+ res.on("data", (chunk) => {
+ data += chunk;
+ });
+ res.on("end", () => {
+ if (res.statusCode === 200) {
+ resolve(JSON.parse(data));
+ } else {
+ reject(`${url} returned ${res.statusCode} ${res.statusMessage}`);
+ }
+ });
+ res.on("error", reject);
+ }).on("error", reject);
+ });
+}
+
+async function fillResolved(name, p) {
+ const version = p.version.replace(/^.*@/, "");
+ console.log(`Retrieving metadata for ${name}@${version}`);
+ const metadataUrl = `https://registry.npmjs.com/${name}/${version}`;
+ const metadata = await fetchJson(metadataUrl);
+ p.resolved = metadata.dist.tarball;
+ p.integrity = metadata.dist.integrity;
+}
+
+let changesMade = false;
+
+async function fillAllResolved(packages) {
+ for (const packagePath in packages) {
+ if (packagePath === "") continue;
+ const p = packages[packagePath];
+ if (!p.inBundle && !p.bundled && (!p.resolved || !p.integrity)) {
+ const packageName =
+ p.name ||
+ /^npm:(.+?)@.+$/.exec(p.version)?.[1] ||
+ packagePath.replace(/^.*node_modules\/(?=.+?$)/, "");
+ await fillResolved(packageName, p);
+ changesMade = true;
+ }
+ }
+}
+
+const oldContents = await readFile(lockfilePath, "utf-8");
+const packageLock = JSON.parse(oldContents);
+
+await fillAllResolved(packageLock.packages ?? []);
+
+if (changesMade) {
+ const newContents = JSON.stringify(packageLock, null, 2) + "\n";
+ await writeFile(lockfilePath, newContents);
+ console.log(`Updated ${lockfilePath}`);
+} else {
+ console.log("No changes needed.");
+}
diff --git a/flatpak/generate-sources.sh b/flatpak/generate-sources.sh
new file mode 100755
index 00000000..f656d0aa
--- /dev/null
+++ b/flatpak/generate-sources.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+#
+# Generate offline dependency source files for Flatpak builds.
+#
+# Prerequisites:
+# pip install flatpak-node-generator tomlkit aiohttp
+# Clone https://github.com/flatpak/flatpak-builder-tools (for cargo generator)
+#
+# Usage:
+# ./flatpak/generate-sources.sh
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
+
+# Generate cargo-sources.json
+python3 "$SCRIPT_DIR/flatpak-builder-tools/cargo/flatpak-cargo-generator.py" \
+ -o "$SCRIPT_DIR/cargo-sources.json" "$REPO_ROOT/Cargo.lock"
+
+# Generate node-sources.json from a patched copy of the lockfile.
+# npm omits resolved/integrity for some workspace deps, and
+# flatpak-node-generator can't handle workspace link entries.
+TMPDIR=$(mktemp -d)
+trap 'rm -rf "$TMPDIR"' EXIT
+
+cp "$REPO_ROOT/package-lock.json" "$TMPDIR/package-lock.json"
+cp "$REPO_ROOT/package.json" "$TMPDIR/package.json"
+
+node "$SCRIPT_DIR/fix-lockfile.mjs" "$TMPDIR/package-lock.json"
+
+node -e "
+ const fs = require('fs');
+ const p = process.argv[1];
+ const d = JSON.parse(fs.readFileSync(p, 'utf-8'));
+ for (const [name, info] of Object.entries(d.packages || {})) {
+ if (name && (info.link || !info.resolved)) delete d.packages[name];
+ }
+ fs.writeFileSync(p, JSON.stringify(d, null, 2));
+" "$TMPDIR/package-lock.json"
+
+flatpak-node-generator --no-requests-cache \
+ -o "$SCRIPT_DIR/node-sources.json" npm "$TMPDIR/package-lock.json"
diff --git a/flatpak/update-manifest.sh b/flatpak/update-manifest.sh
index e75c6186..1676365c 100755
--- a/flatpak/update-manifest.sh
+++ b/flatpak/update-manifest.sh
@@ -1,19 +1,19 @@
#!/usr/bin/env bash
#
-# Update the Flatpak manifest with URLs and SHA256 hashes for a given release.
+# Update the Flatpak manifest for a new release.
#
# Usage:
# ./flatpak/update-manifest.sh v2026.2.0
#
# This script:
-# 1. Downloads the x86_64 and aarch64 .deb files from the GitHub release
-# 2. Computes their SHA256 checksums
-# 3. Updates the manifest YAML with the correct URLs and hashes
-# 4. Updates the metainfo.xml with a new entry
+# 1. Updates the git tag and commit in the manifest
+# 2. Regenerates cargo-sources.json and node-sources.json from the tagged lockfiles
+# 3. Adds a new entry to the metainfo
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
MANIFEST="$SCRIPT_DIR/app.yaak.Yaak.yml"
METAINFO="$SCRIPT_DIR/app.yaak.Yaak.metainfo.xml"
@@ -26,57 +26,64 @@ fi
VERSION_TAG="$1"
VERSION="${VERSION_TAG#v}"
-# Only allow stable releases (skip beta, alpha, rc, etc.)
if [[ "$VERSION" == *-* ]]; then
echo "Skipping pre-release version '$VERSION_TAG' (only stable releases are published to Flathub)"
exit 0
fi
REPO="mountain-loop/yaak"
-BASE_URL="https://github.com/$REPO/releases/download/$VERSION_TAG"
+COMMIT=$(git ls-remote "https://github.com/$REPO.git" "refs/tags/$VERSION_TAG" | cut -f1)
-DEB_AMD64="yaak_${VERSION}_amd64.deb"
-DEB_ARM64="yaak_${VERSION}_arm64.deb"
+if [ -z "$COMMIT" ]; then
+ echo "Error: Could not resolve commit for tag $VERSION_TAG"
+ exit 1
+fi
+echo "Tag: $VERSION_TAG"
+echo "Commit: $COMMIT"
+
+# Update git tag and commit in the manifest
+sed -i "s|tag: v.*|tag: $VERSION_TAG|" "$MANIFEST"
+sed -i "s|commit: .*|commit: $COMMIT|" "$MANIFEST"
+echo "Updated manifest tag and commit."
+
+# Regenerate offline dependency sources from the tagged lockfiles
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
-echo "Downloading $DEB_AMD64..."
-curl -fSL "$BASE_URL/$DEB_AMD64" -o "$TMPDIR/$DEB_AMD64"
-SHA_AMD64=$(sha256sum "$TMPDIR/$DEB_AMD64" | cut -d' ' -f1)
-echo " SHA256: $SHA_AMD64"
+echo "Fetching lockfiles from $VERSION_TAG..."
+curl -fsSL "https://raw.githubusercontent.com/$REPO/$VERSION_TAG/Cargo.lock" -o "$TMPDIR/Cargo.lock"
+curl -fsSL "https://raw.githubusercontent.com/$REPO/$VERSION_TAG/package-lock.json" -o "$TMPDIR/package-lock.json"
+curl -fsSL "https://raw.githubusercontent.com/$REPO/$VERSION_TAG/package.json" -o "$TMPDIR/package.json"
-echo "Downloading $DEB_ARM64..."
-curl -fSL "$BASE_URL/$DEB_ARM64" -o "$TMPDIR/$DEB_ARM64"
-SHA_ARM64=$(sha256sum "$TMPDIR/$DEB_ARM64" | cut -d' ' -f1)
-echo " SHA256: $SHA_ARM64"
+echo "Generating cargo-sources.json..."
+python3 "$SCRIPT_DIR/flatpak-builder-tools/cargo/flatpak-cargo-generator.py" \
+ -o "$SCRIPT_DIR/cargo-sources.json" "$TMPDIR/Cargo.lock"
-echo ""
-echo "Updating manifest: $MANIFEST"
+echo "Generating node-sources.json..."
+node "$SCRIPT_DIR/fix-lockfile.mjs" "$TMPDIR/package-lock.json"
-# Update URLs by matching the arch-specific deb filename
-sed -i "s|url: .*amd64\.deb|url: $BASE_URL/$DEB_AMD64|" "$MANIFEST"
-sed -i "s|url: .*arm64\.deb|url: $BASE_URL/$DEB_ARM64|" "$MANIFEST"
+node -e "
+ const fs = require('fs');
+ const p = process.argv[1];
+ const d = JSON.parse(fs.readFileSync(p, 'utf-8'));
+ for (const [name, info] of Object.entries(d.packages || {})) {
+ if (name && (info.link || !info.resolved)) delete d.packages[name];
+ }
+ fs.writeFileSync(p, JSON.stringify(d, null, 2));
+" "$TMPDIR/package-lock.json"
-# Update SHA256 hashes by finding the current ones and replacing
-OLD_SHA_AMD64=$(grep -A2 "amd64\.deb" "$MANIFEST" | grep sha256 | sed 's/.*"\(.*\)"/\1/')
-OLD_SHA_ARM64=$(grep -A2 "arm64\.deb" "$MANIFEST" | grep sha256 | sed 's/.*"\(.*\)"/\1/')
-
-sed -i "s|$OLD_SHA_AMD64|$SHA_AMD64|" "$MANIFEST"
-sed -i "s|$OLD_SHA_ARM64|$SHA_ARM64|" "$MANIFEST"
-
-echo " Manifest updated."
-
-echo "Updating metainfo: $METAINFO"
+flatpak-node-generator --no-requests-cache \
+ -o "$SCRIPT_DIR/node-sources.json" npm "$TMPDIR/package-lock.json"
+# Update metainfo with new release
TODAY=$(date +%Y-%m-%d)
-
-# Insert new release entry after
sed -i "s| | \n |" "$METAINFO"
-
-echo " Metainfo updated."
+echo "Updated metainfo with release $VERSION."
echo ""
echo "Done! Review the changes:"
echo " $MANIFEST"
echo " $METAINFO"
+echo " $SCRIPT_DIR/cargo-sources.json"
+echo " $SCRIPT_DIR/node-sources.json"
diff --git a/package-lock.json b/package-lock.json
index 167cad0f..1946700b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16062,18 +16062,6 @@
"name": "@yaak/auth-oauth2",
"version": "0.1.0"
},
- "plugins/faker": {
- "name": "@yaak/faker",
- "version": "1.1.1",
- "extraneous": true,
- "dependencies": {
- "@faker-js/faker": "^10.1.0"
- },
- "devDependencies": {
- "@types/node": "^25.0.3",
- "typescript": "^5.9.3"
- }
- },
"plugins/filter-jsonpath": {
"name": "@yaak/filter-jsonpath",
"version": "0.1.0",
diff --git a/scripts/vendor-node.cjs b/scripts/vendor-node.cjs
index 1dd29cf0..92160b3a 100644
--- a/scripts/vendor-node.cjs
+++ b/scripts/vendor-node.cjs
@@ -1,4 +1,6 @@
const path = require('node:path');
+const crypto = require('node:crypto');
+const fs = require('node:fs');
const decompress = require('decompress');
const Downloader = require('nodejs-file-downloader');
const { rmSync, cpSync, mkdirSync, existsSync } = require('node:fs');
@@ -41,6 +43,15 @@ const DST_BIN_MAP = {
[WIN_ARM]: 'yaaknode.exe',
};
+const SHA256_MAP = {
+ [MAC_ARM]: 'b05aa3a66efe680023f930bd5af3fdbbd542794da5644ca2ad711d68cbd4dc35',
+ [MAC_X64]: '096081b6d6fcdd3f5ba0f5f1d44a47e83037ad2e78eada26671c252fe64dd111',
+ [LNX_ARM]: '0dc93ec5c798b0d347f068db6d205d03dea9a71765e6a53922b682b91265d71f',
+ [LNX_X64]: '58a5ff5cc8f2200e458bea22e329d5c1994aa1b111d499ca46ec2411d58239ca',
+ [WIN_X64]: '5355ae6d7c49eddcfde7d34ac3486820600a831bf81dc3bdca5c8db6a9bb0e76',
+ [WIN_ARM]: 'ce9ee4e547ebdff355beb48e309b166c24df6be0291c9eaf103ce15f3de9e5b4',
+};
+
const key = `${process.platform}_${process.env.YAAK_TARGET_ARCH ?? process.arch}`;
const destDir = path.join(__dirname, `..`, 'crates-tauri', 'yaak-app', 'vendored', 'node');
@@ -68,6 +79,15 @@ rmSync(tmpDir, { recursive: true, force: true });
timeout: 1000 * 60 * 2,
}).download();
+ // Verify SHA256
+ const expectedHash = SHA256_MAP[key];
+ const fileBuffer = fs.readFileSync(filePath);
+ const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
+ if (actualHash !== expectedHash) {
+ throw new Error(`SHA256 mismatch for ${path.basename(filePath)}\n expected: ${expectedHash}\n actual: ${actualHash}`);
+ }
+ console.log('SHA256 verified:', actualHash);
+
// Decompress to the same directory
await decompress(filePath, tmpDir, {});
diff --git a/scripts/vendor-protoc.cjs b/scripts/vendor-protoc.cjs
index 48a867ee..439bb10c 100644
--- a/scripts/vendor-protoc.cjs
+++ b/scripts/vendor-protoc.cjs
@@ -1,3 +1,5 @@
+const crypto = require('node:crypto');
+const fs = require('node:fs');
const decompress = require('decompress');
const Downloader = require('nodejs-file-downloader');
const path = require('node:path');
@@ -41,6 +43,15 @@ const DST_BIN_MAP = {
[WIN_ARM]: 'yaakprotoc.exe',
};
+const SHA256_MAP = {
+ [MAC_ARM]: 'db7e66ff7f9080614d0f5505a6b0ac488cf89a15621b6a361672d1332ec2e14e',
+ [MAC_X64]: 'e20b5f930e886da85e7402776a4959efb1ed60c57e72794bcade765e67abaa82',
+ [LNX_ARM]: '6018147740548e0e0f764408c87f4cd040e6e1c1203e13aeacaf811892b604f3',
+ [LNX_X64]: 'f3340e28a83d1c637d8bafdeed92b9f7db6a384c26bca880a6e5217b40a4328b',
+ [WIN_X64]: 'd7a207fb6eec0e4b1b6613be3b7d11905375b6fd1147a071116eb8e9f24ac53b',
+ [WIN_ARM]: 'd7a207fb6eec0e4b1b6613be3b7d11905375b6fd1147a071116eb8e9f24ac53b',
+};
+
const dstDir = path.join(__dirname, `..`, 'crates-tauri', 'yaak-app', 'vendored', 'protoc');
const key = `${process.platform}_${process.env.YAAK_TARGET_ARCH ?? process.arch}`;
console.log(`Vendoring protoc ${VERSION} for ${key}`);
@@ -63,6 +74,15 @@ mkdirSync(dstDir, { recursive: true });
// Download GitHub release artifact
const { filePath } = await new Downloader({ url, directory: tmpDir }).download();
+ // Verify SHA256
+ const expectedHash = SHA256_MAP[key];
+ const fileBuffer = fs.readFileSync(filePath);
+ const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
+ if (actualHash !== expectedHash) {
+ throw new Error(`SHA256 mismatch for ${path.basename(filePath)}\n expected: ${expectedHash}\n actual: ${actualHash}`);
+ }
+ console.log('SHA256 verified:', actualHash);
+
// Decompress to the same directory
await decompress(filePath, tmpDir, {});