Skip to content

feat: MVP Phase 8 — i18n, theming, release pipeline#26

Merged
mpiton merged 4 commits intomainfrom
feat/phase8-polish
Apr 12, 2026
Merged

feat: MVP Phase 8 — i18n, theming, release pipeline#26
mpiton merged 4 commits intomainfrom
feat/phase8-polish

Conversation

@mpiton
Copy link
Copy Markdown
Owner

@mpiton mpiton commented Apr 12, 2026

Summary

  • Task 27 (i18n & Theming): react-i18next setup with browser language detection, complete en/fr translations for all UI strings, custom hex accent color picker with live preview, compact mode via CSS variables, locale-aware date formatting (Intl.DateTimeFormat), and backend locale validation in update_config handler.
  • Task 28 (Release & Distribution): GitHub Actions release workflow on vX.Y.Z tags — multi-platform builds (Linux .deb/.rpm, macOS .dmg with notarization, Windows .msi with code signing), Flatpak bundle, Tauri updater with signature verification, systemd user service, Freedesktop .desktop entry, and Winget/Homebrew templates.

Changes

i18n (Task 27)

  • New: src/i18n/ — i18next init + en.json/fr.json translations
  • New: src/hooks/useLanguage.ts, src/hooks/useAppEffects.ts
  • Modified: All SettingsView sections, Sidebar, SearchBar, MediaGrabberDialog, FileInfoSection — hardcoded strings replaced with t() calls
  • Modified: src/index.css — compact mode CSS rules
  • Modified: src-tauri/.../update_config.rs — locale validation (en, fr)
  • New: 17 tests (useLanguage, useAppEffects, translation parity)

Release (Task 28)

  • New: .github/workflows/release.yml — 6-job pipeline with per-job permissions
  • New: contrib/ — systemd unit, .desktop, Flatpak manifest, icon docs, Winget/Homebrew templates
  • Modified: src-tauri/tauri.conf.json — updater plugin config
  • Modified: src-tauri/Cargo.toml — tauri-plugin-updater dependency

Test plan

  • cargo test --workspace — 432 passed, 0 failed
  • npx vitest run — 297 passed, 0 failed
  • cargo clippy -- -D warnings — 0 errors
  • npx oxlint . — 0 warnings/errors
  • npx tsc -b — compilation OK
  • cargo fmt --check — clean
  • Adversarial code review — 9 findings resolved (2 CRITICAL, 5 HIGH, 2 MEDIUM)
  • Manual: verify i18n language switch in browser
  • Manual: verify compact mode toggle in Settings > Appearance
  • Manual: verify release workflow triggers on tag push

Summary by cubic

Adds full i18n (English/French), compact mode and accent color theming, and a cross‑platform release pipeline with signed auto‑updates. Switches the release pipeline to updater‑compatible bundles and macOS universal builds; finalizes Phase 8 (Tasks 27–28).

  • New Features

    • i18n: react-i18next + i18next-browser-languagedetector, complete en/fr strings, useLanguage hook, locale‑aware dates, backend locale validation, and normalized browser locales (en‑US → en) with supported‑set guard.
    • Theming: Hex accent color picker with live preview and validation, compact mode via CSS variables, and useAppEffects to apply body/root changes with cleanup.
    • Releases: Tag‑triggered GitHub Actions builds installers and publishes updater bundles with .sig (Linux: .AppImage, macOS universal: .app.tar.gz, Windows: NSIS .exe); fixes macOS universal output paths and latest.json URLs; enforces CI guard if updater pubkey is a placeholder; integrates tauri-plugin-updater with signature verification; Flatpak builder fixes; systemd user unit, Freedesktop .desktop, Winget/Homebrew templates.
  • Migration

    • Set updater pubkey in src-tauri/tauri.conf.json, add TAURI_SIGNING_PRIVATE_KEY, and host latest.json with .sig for updater bundles (.app.tar.gz/.AppImage/.exe).
    • Configure macOS notarization and Windows code‑signing secrets.
    • Trigger the pipeline by pushing a vX.Y.Z tag.

Written for commit 2ea77e0. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • English/French translations with browser-language detection and persistent switching
    • Appearance: accent color (hex) and compact layout mode
    • In-app updates enabled with signed release manifests, per-platform downloads, and updater metadata
    • Localized UI labels/placeholders and an “added” timestamp in file details
  • Documentation

    • Icon generation guide and desktop/service/packaging helpers (Flatpak, Homebrew, Winget)
  • Chores

    • Automated release pipeline creating signed builds and uploads for Linux, macOS, Windows, Flatpak, and package managers

Task 27: Setup react-i18next with browser language detection,
complete en/fr translations for all UI strings, custom hex accent
color picker with preview, compact mode CSS, locale-aware date
formatting via Intl.DateTimeFormat, and backend locale validation.

Task 28: GitHub Actions release workflow triggered on vX.Y.Z tags
building Linux (.deb/.rpm), macOS (.dmg with notarization), and
Windows (.msi with signing). Tauri updater configured with signature
verification. Flatpak manifest, systemd user service, Freedesktop
.desktop entry, and Winget/Homebrew templates added under contrib/.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 12, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1969d971-d0a7-43cd-9b8f-c4eea0572096

📥 Commits

Reviewing files that changed from the base of the PR and between 8dcb63c and 2ea77e0.

📒 Files selected for processing (1)
  • .github/workflows/release.yml
✅ Files skipped from review due to trivial changes (1)
  • .github/workflows/release.yml

📝 Walkthrough

Walkthrough

Adds i18n (en/fr) and theming, new hooks/tests, integrates Tauri updater, adds multi-platform tag-triggered Release CI that builds/signs/notarizes/uploads artifacts and generates an updater latest.json, and adds distribution manifests and packaging helpers.

Changes

Cohort / File(s) Summary
Release workflow
\.github/workflows/release.yml
New tag-triggered GitHub Actions workflow: creates GitHub Release (extracts changelog), runs parallel Linux/macOS/Windows builds with signing/notarization, uploads artifacts and signatures, and generates/uploads latest.json updater manifest.
i18n infra & resources
src/i18n/i18n.ts, src/i18n/locales/en.json, src/i18n/locales/fr.json, package.json
Adds i18next/react-i18next with browser detection/localStorage; includes English and French translation files and adds i18n deps to package.json.
Hooks, tests & test setup
src/hooks/useLanguage.ts, src/hooks/useAppEffects.ts, src/hooks/__tests__/*, src/i18n/__tests__/translations.test.ts, src/test-setup.ts
Adds useLanguage and useAppEffects, comprehensive i18n/translation/unit tests, and a react-i18next test mock.
UI localization across views
src/views/.../*.tsx, src/views/DownloadDetailsPanel/FileInfoSection.tsx, src/views/DownloadsView/SearchBar.tsx, src/views/LinkGrabberView/.../MediaGrabberDialog.tsx
Replaces hardcoded UI strings with translation keys and uses localized date formatting via new formatDate.
App init, layout, and routes
src/App.tsx, src/layouts/AppLayout.tsx, src/layouts/Sidebar.tsx, src/types/layout.ts
Initializes i18n, loads settings into store, applies app effects, renames RouteConfig.labellabelKey and updates ROUTES; Sidebar uses t(labelKey).
Styling & formatting helpers
src/index.css, src/lib/format.ts
Adds body.compact-mode CSS rules and exports formatDate(timestampMs, locale) for localized timestamps.
Settings UI and validation
src/views/SettingsView/*, src-tauri/src/application/commands/update_config.rs, src-tauri/tauri.conf.json
Localizes settings views, adds accent color input handling, uses useLanguage to persist locale, restricts server-side locale validation to en/fr, and enables Tauri updater in config (pubkey placeholder).
Tauri plugin registration & deps
src-tauri/Cargo.toml, src-tauri/src/lib.rs, src-tauri/tauri.conf.json
Adds tauri-plugin-updater dependency and registers the updater plugin in app init; updater enabled in Tauri config.
Distribution manifests & helpers
contrib/flatpak/org.vortex.Vortex.yml, contrib/homebrew/vortex.rb, contrib/winget/Vortex.yaml, contrib/vortex.desktop, contrib/vortex.service, contrib/icons/README.md
Adds Flatpak manifest, Homebrew cask, Winget manifest (placeholders), desktop entry, systemd unit, and icon regeneration documentation.
Testable UI changes
src/layouts/AppLayout.tsx, src/hooks/__tests__/*, src/views/*
Wires settings fetch into store, applies DOM effects via hook, and adds/updates tests to cover language/effects and translation parity.

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer (push tag)
    participant GH as GitHub
    participant Actions as GitHub Actions
    participant Linux as Linux runner
    participant Mac as macOS runner
    participant Win as Windows runner
    participant Notary as Apple NotaryService
    participant Release as GitHub Release
    participant Updater as Update-Updater job

    Dev->>GH: push semver tag
    GH->>Actions: trigger Release workflow
    Actions->>Release: create GitHub Release (extract changelog)
    Actions->>Linux: start build-tauri-linux
    Actions->>Mac: start build-tauri-macos
    Actions->>Win: start build-tauri-windows
    Linux->>Release: upload .deb/.rpm and signatures
    Mac->>Notary: submit .dmg for notarization
    Notary-->>Mac: notarization result
    Mac->>Release: upload notarized .dmg and signatures
    Win->>Release: upload .msi and signatures
    Actions->>Updater: run update-updater job after builds
    Updater->>Release: download signatures, generate latest.json
    Updater->>Release: upload latest.json to Release
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

ui

Poem

🐰
I hopped through keys and strings today,
en and fr now lead the way.
Compact hops and colors bright,
CI builds hum through the night.
Packages packed — go share the play!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the three main themes of this changeset: i18n (internationalization), theming (accent colors, compact mode), and the release pipeline (CI/CD workflow and distribution).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/phase8-polish

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/views/SettingsView/NetworkSection.tsx (1)

64-70: ⚠️ Potential issue | 🟡 Minor

Localize the proxy URL placeholder too.

placeholder="http://proxy:8080" is still hardcoded English, so this field remains partially untranslated. Move it to a locale key (for example settings.network.proxyUrlPlaceholder) and render via t(...).

Suggested patch
-              placeholder="http://proxy:8080"
+              placeholder={t('settings.network.proxyUrlPlaceholder')}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/SettingsView/NetworkSection.tsx` around lines 64 - 70, The
placeholder for the proxy URL is hardcoded; change it to use i18n by adding a
locale key (e.g. settings.network.proxyUrlPlaceholder) and pass
t('settings.network.proxyUrlPlaceholder') to the Input placeholder prop instead
of the literal "http://proxy:8080". Update the Input usage that references
proxyUrlDraft/setProxyUrlDraft and onBlur -> handleChange('proxyUrl',
proxyUrlDraft || null) to use the translated placeholder so the field is fully
localized.
src/views/SettingsView/AppearanceSection.tsx (1)

98-111: ⚠️ Potential issue | 🟡 Minor

Preset selection should clear stale hex validation error.

After an invalid hex input, clicking a preset (Line 110) updates color but does not reset hexError, so the invalid message can persist incorrectly.

💡 Proposed fix
                 onClick={() => handleChange('accentColor', preset.value)}
+                onClick={() => {
+                  handleChange('accentColor', preset.value);
+                  setHexDraft('');
+                  setHexError(false);
+                }}

Also applies to: 127-131

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/SettingsView/AppearanceSection.tsx` around lines 98 - 111, When a
preset button from ACCENT_PRESETS is clicked the color updates via
handleChange('accentColor', ...), but the hexError state isn’t cleared so a
prior invalid-hex error can remain; update the code to clear hexError when
selecting a preset — either by calling the hex error setter (e.g.
setHexError(false) or setHexError('')) in the onClick handler for the preset
buttons or by modifying handleChange to reset hexError whenever the key is
'accentColor'; apply the same change to the other preset block referenced (lines
127-131) so selecting any preset clears the stale hex validation error.
🧹 Nitpick comments (4)
src/test-setup.ts (1)

7-138: Prefer using the canonical locale JSON in the mock.

This inlined translation map is large and likely to drift. Importing the real en locale file in test setup will keep test behavior aligned with production translations with less maintenance burden.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test-setup.ts` around lines 7 - 138, Replace the large inlined
translations object (the const named "en" in test-setup.ts) with an import of
the canonical English locale JSON used in production (import the real locale
module and re-export or assign it to "en"), so tests use the same source of
truth; update any references that rely on the in-file "en" to use the imported
value and remove the inlined object to avoid duplication and drift.
contrib/homebrew/vortex.rb (1)

1-7: Keep template placeholders from being mistaken as production-ready cask.

version "TODO" and sha256 "TODO" are fine for a template, but this file name/path looks consumable as-is.

Would you like me to draft a template-safe variant (e.g., vortex.template.rb) plus a short README note for the replacement process?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contrib/homebrew/vortex.rb` around lines 1 - 7, The cask contains template
placeholders (version "TODO" and sha256 "TODO") and should not be published as a
consumable file; rename the file from vortex.rb to vortex.template.rb (or
similar) and update its top-level comment to explain it is a template and list
the replacement steps for version and sha256, and add a short README entry that
instructs maintainers to copy/rename the template to vortex.rb and fill in
version and sha256 before submitting to Homebrew; reference the identifiers
version "TODO", sha256 "TODO", and the cask name "vortex" when making these
changes.
src/views/SettingsView/AppearanceSection.tsx (1)

21-24: Avoid duplicating supported language definitions in this component.

LOCALES and supportedLangs duplicate language source-of-truth already owned by useLanguage(). This can drift when adding new locales.

♻️ Refactor direction
-const LOCALES = [
-  { value: 'en', label: 'English' },
-  { value: 'fr', label: 'Français' },
-] as const;
+const LOCALE_LABELS: Record<Language, string> = {
+  en: 'English',
+  fr: 'Français',
+};

 export function AppearanceSection({ config }: AppearanceSectionProps) {
   const { t } = useTranslation();
-  const { setLanguage } = useLanguage();
+  const { setLanguage, availableLanguages } = useLanguage();
@@
-            onValueChange={(v) => {
-              const supportedLangs = ['en', 'fr'] as const;
-              const lang = supportedLangs.includes(v as Language) ? (v as Language) : 'en';
-              setLanguage(lang);
-            }}
+            onValueChange={(v) => {
+              const lang = availableLanguages.includes(v as Language) ? (v as Language) : 'en';
+              setLanguage(lang);
+            }}
@@
-              {LOCALES.map((loc) => (
-                <SelectItem key={loc.value} value={loc.value}>
-                  {loc.label}
+              {availableLanguages.map((lang) => (
+                <SelectItem key={lang} value={lang}>
+                  {LOCALE_LABELS[lang]}
                 </SelectItem>
               ))}

Also applies to: 39-40, 157-160

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/views/SettingsView/AppearanceSection.tsx` around lines 21 - 24, Remove
the hardcoded LOCALES array and any local supportedLangs usage and instead
source the language list from useLanguage(); call useLanguage() (or import its
selector) inside AppearanceSection to get the canonical supportedLangs and
labels, then map that result where LOCALES or supportedLangs were used (e.g., in
the locale dropdown and the blocks at lines ~39-40 and ~157-160). Ensure types
align with the existing component props/state (convert to the same {value,label}
shape if necessary) and remove the now-unused LOCALES constant and related local
variables.
src/hooks/useLanguage.ts (1)

4-5: Frontend language list is more restrictive than backend validation.

The frontend restricts available languages to ['en', 'fr'], but per CHANGELOG line 199, the backend's UpdateConfigCommand validates against ["en", "fr", "de", "es", "ja", "zh"]. This inconsistency means:

  1. Users cannot select languages the backend already supports (de, es, ja, zh)
  2. When translations for those languages are added, only this constant needs updating

This appears intentional for MVP (only en/fr translations exist), but consider adding a comment documenting this.

📝 Suggested documentation
-const AVAILABLE_LANGUAGES = ['en', 'fr'] as const;
+// MVP: Only en/fr have translations. Backend accepts: en, fr, de, es, ja, zh.
+// Expand this list when adding new locale JSON files.
+const AVAILABLE_LANGUAGES = ['en', 'fr'] as const;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useLanguage.ts` around lines 4 - 5, The frontend's
AVAILABLE_LANGUAGES constant and derived Language type in useLanguage.ts
intentionally restrict languages to ['en','fr'] while the backend
UpdateConfigCommand accepts more (de, es, ja, zh); add a concise comment above
AVAILABLE_LANGUAGES explaining this is an intentional MVP limitation (only en/fr
translations exist), reference the backend validation (e.g.,
UpdateConfigCommand) and note that AVAILABLE_LANGUAGES must be expanded when
additional translations are added so maintainers know why the lists differ and
when to update it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 271-276: The workflow currently silences missing .sig files by
falling back to empty strings for LINUX_SIG, MACOS_SIG, MACOS_ARM_SIG, and
WINDOWS_SIG which leads to invalid/empty signatures in the generated
latest.json; change the release step in .github/workflows/release.yml to
validate those signature variables after reading them and fail the job (exit
non‑zero with a clear error message) if any expected signature is empty or
missing rather than using || echo ""; ensure the check references the same
variables (LINUX_SIG, MACOS_SIG, MACOS_ARM_SIG, WINDOWS_SIG) and prints which
signature(s) are missing before exiting so the release cannot proceed with empty
signatures.
- Around line 115-117: The workflow currently only adds the Rust macOS ARM64
target (uses: dtolnay/rust-toolchain@stable with targets: aarch64-apple-darwin)
but the update-updater job produces latest.json entries for both darwin-x86_64
and darwin-aarch64; either add the Intel macOS target by including
x86_64-apple-darwin in the rust-toolchain targets so you build both
architectures (or build a universal binary) or change the update-updater logic
that writes latest.json to only emit darwin-aarch64 if Intel builds are not
produced (remove darwin-x86_64 entry); update the workflow step referencing
targets and the update-updater job’s latest.json generation to stay consistent.
- Around line 231-238: The Flatpak build step is missing the --repo flag on the
flatpak-builder invocation, so no OSTree repo is created and the subsequent
flatpak build-bundle (which expects a repo path) fails; update the
flatpak-builder command (the invocation labeled "flatpak-builder --force-clean
--user --install-deps-from=flathub _flatpak_build
contrib/flatpak/org.vortex.Vortex.yml") to include --repo pointing to the
repository used by the later flatpak build-bundle (e.g.
--repo=~/.local/share/flatpak/repo), and ensure the repo directory exists
(create it with mkdir -p before running flatpak-builder) so the build-bundle has
a valid OSTree repository as its first argument.

In `@contrib/flatpak/org.vortex.Vortex.yml`:
- Around line 36-45: The build sets CARGO_TARGET_DIR to
/run/build/vortex/cargo-target so cargo outputs the binary there, but the
install step references src-tauri/target/release/vortex; update the install step
to use the actual output path (/run/build/vortex/cargo-target/release/vortex) or
remove/adjust the CARGO_TARGET_DIR setting so that cargo build (in
src-tauri/Cargo.toml) and the install command (install -Dm755 ...) point to the
same output location (referencing CARGO_TARGET_DIR and the install command).

In `@contrib/homebrew/vortex.rb`:
- Line 8: The cask currently hardcodes the Intel artifact URL
("Vortex_#{version}_x64.dmg") causing Apple Silicon installs to get the wrong
binary; update contrib/homebrew/vortex.rb to use architecture-specific blocks
(on_intel and on_arm) and supply the correct sha256 and url inside each block
(use "Vortex_#{version}_x64.dmg" and its intel sha256 in on_intel, and
"Vortex_#{version}_aarch64.dmg" and its arm sha256 in on_arm) so Homebrew
installs the matching artifact for the machine architecture.

In `@src-tauri/Cargo.toml`:
- Line 49: The tauri-plugin-updater crate was added to Cargo.toml but not
registered with the Tauri runtime; open src-tauri/src/lib.rs (or the file that
builds the Tauri app) and register the plugin on the tauri::Builder instance by
calling the updater plugin initializer from the tauri_plugin_updater crate
(e.g., add an appropriate use/import for tauri_plugin_updater and call its init
function in the Builder chain before .build()), ensuring the plugin is included
in the builder that returns the Tauri application.

In `@src-tauri/tauri.conf.json`:
- Line 43: The tauri config still contains the placeholder public key value
"REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate" under the "pubkey"
field which must be replaced before any release; add a CI check that searches
the repo for that exact placeholder string and fails the pipeline if found, and
update the check to run during release/merge pipelines (ensure the check treats
a non-zero grep/ripgrep exit as failure so the job fails when the placeholder
exists); finally, document that the real key should be injected/generated and
the "pubkey" field updated (or populated from a secrets-managed artifact) before
tagging a release.

In `@src/hooks/__tests__/useLanguage.test.ts`:
- Around line 33-35: The test calls result.current.setLanguage inside an async
act callback but doesn’t await it, causing potential race conditions; update the
test to await the call (e.g., await result.current.setLanguage('fr')) inside the
async act for the case at lines where setLanguage('fr') is used and do the same
fix for the other occurrence referenced (lines 43-45) so the async work
completes deterministically before assertions.

In `@src/hooks/useLanguage.ts`:
- Around line 22-24: The returned current value can be a full locale (e.g.,
"en-US") from i18n.language which doesn't match availableLanguages; in
useLanguage normalize the language to its base code before returning (e.g.,
derive base = i18n.language.split('-')[0] || i18n.language) and return current:
base (and ensure any comparisons with availableLanguages use the same
normalization). Update places referencing current (and any logic in setLanguage
if it reads i18n.language) to use the same base-normalization to keep the
selector consistent.

In `@src/i18n/__tests__/translations.test.ts`:
- Around line 34-42: The tests 'should have non-empty values for all English
keys' and 'should have non-empty values for all French keys' only assert key
counts; update them to iterate the keys returned by getAllKeys(en as
NestedRecord) and getAllKeys(fr as NestedRecord) and assert that each
corresponding translation value is defined and not an empty string (e.g., value
!== undefined && value !== null && value.trim().length > 0). Locate the tests
using getAllKeys, en, fr and NestedRecord and replace the single length
assertions with per-key checks that fetch the value for each key and assert it's
non-empty.

In `@src/index.css`:
- Around line 101-105: The compact-mode CSS rule body.compact-mode currently
triggers a stylelint declaration-empty-line-before error because there is no
blank line between the custom property declaration (--spacing-unit) and the
subsequent property; update the block (body.compact-mode) to insert a single
blank line before the font-size declaration so the order is: --spacing-unit,
blank line, font-size, line-height to satisfy the rule.

---

Outside diff comments:
In `@src/views/SettingsView/AppearanceSection.tsx`:
- Around line 98-111: When a preset button from ACCENT_PRESETS is clicked the
color updates via handleChange('accentColor', ...), but the hexError state isn’t
cleared so a prior invalid-hex error can remain; update the code to clear
hexError when selecting a preset — either by calling the hex error setter (e.g.
setHexError(false) or setHexError('')) in the onClick handler for the preset
buttons or by modifying handleChange to reset hexError whenever the key is
'accentColor'; apply the same change to the other preset block referenced (lines
127-131) so selecting any preset clears the stale hex validation error.

In `@src/views/SettingsView/NetworkSection.tsx`:
- Around line 64-70: The placeholder for the proxy URL is hardcoded; change it
to use i18n by adding a locale key (e.g. settings.network.proxyUrlPlaceholder)
and pass t('settings.network.proxyUrlPlaceholder') to the Input placeholder prop
instead of the literal "http://proxy:8080". Update the Input usage that
references proxyUrlDraft/setProxyUrlDraft and onBlur -> handleChange('proxyUrl',
proxyUrlDraft || null) to use the translated placeholder so the field is fully
localized.

---

Nitpick comments:
In `@contrib/homebrew/vortex.rb`:
- Around line 1-7: The cask contains template placeholders (version "TODO" and
sha256 "TODO") and should not be published as a consumable file; rename the file
from vortex.rb to vortex.template.rb (or similar) and update its top-level
comment to explain it is a template and list the replacement steps for version
and sha256, and add a short README entry that instructs maintainers to
copy/rename the template to vortex.rb and fill in version and sha256 before
submitting to Homebrew; reference the identifiers version "TODO", sha256 "TODO",
and the cask name "vortex" when making these changes.

In `@src/hooks/useLanguage.ts`:
- Around line 4-5: The frontend's AVAILABLE_LANGUAGES constant and derived
Language type in useLanguage.ts intentionally restrict languages to ['en','fr']
while the backend UpdateConfigCommand accepts more (de, es, ja, zh); add a
concise comment above AVAILABLE_LANGUAGES explaining this is an intentional MVP
limitation (only en/fr translations exist), reference the backend validation
(e.g., UpdateConfigCommand) and note that AVAILABLE_LANGUAGES must be expanded
when additional translations are added so maintainers know why the lists differ
and when to update it.

In `@src/test-setup.ts`:
- Around line 7-138: Replace the large inlined translations object (the const
named "en" in test-setup.ts) with an import of the canonical English locale JSON
used in production (import the real locale module and re-export or assign it to
"en"), so tests use the same source of truth; update any references that rely on
the in-file "en" to use the imported value and remove the inlined object to
avoid duplication and drift.

In `@src/views/SettingsView/AppearanceSection.tsx`:
- Around line 21-24: Remove the hardcoded LOCALES array and any local
supportedLangs usage and instead source the language list from useLanguage();
call useLanguage() (or import its selector) inside AppearanceSection to get the
canonical supportedLangs and labels, then map that result where LOCALES or
supportedLangs were used (e.g., in the locale dropdown and the blocks at lines
~39-40 and ~157-160). Ensure types align with the existing component props/state
(convert to the same {value,label} shape if necessary) and remove the now-unused
LOCALES constant and related local variables.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5a3609c2-0bf2-44ed-bffa-dde4c7b8910f

📥 Commits

Reviewing files that changed from the base of the PR and between 208310d and 6243aab.

⛔ Files ignored due to path filters (5)
  • package-lock.json is excluded by !**/package-lock.json
  • src-tauri/Cargo.lock is excluded by !**/*.lock
  • src-tauri/gen/schemas/acl-manifests.json is excluded by !**/gen/**
  • src-tauri/gen/schemas/desktop-schema.json is excluded by !**/gen/**
  • src-tauri/gen/schemas/linux-schema.json is excluded by !**/gen/**
📒 Files selected for processing (37)
  • .github/workflows/release.yml
  • CHANGELOG.md
  • contrib/flatpak/org.vortex.Vortex.yml
  • contrib/homebrew/vortex.rb
  • contrib/icons/README.md
  • contrib/vortex.desktop
  • contrib/vortex.service
  • contrib/winget/Vortex.yaml
  • package.json
  • src-tauri/Cargo.toml
  • src-tauri/src/application/commands/update_config.rs
  • src-tauri/tauri.conf.json
  • src/App.tsx
  • src/hooks/__tests__/useAppEffects.test.ts
  • src/hooks/__tests__/useLanguage.test.ts
  • src/hooks/useAppEffects.ts
  • src/hooks/useLanguage.ts
  • src/i18n/__tests__/translations.test.ts
  • src/i18n/i18n.ts
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/index.css
  • src/layouts/AppLayout.tsx
  • src/layouts/Sidebar.tsx
  • src/lib/format.ts
  • src/test-setup.ts
  • src/types/layout.ts
  • src/views/DownloadDetailsPanel/FileInfoSection.tsx
  • src/views/DownloadsView/SearchBar.tsx
  • src/views/LinkGrabberView/MediaGrabberDialog/MediaGrabberDialog.tsx
  • src/views/SettingsView/AppearanceSection.tsx
  • src/views/SettingsView/BrowserSection.tsx
  • src/views/SettingsView/DownloadsSection.tsx
  • src/views/SettingsView/GeneralSection.tsx
  • src/views/SettingsView/NetworkSection.tsx
  • src/views/SettingsView/RemoteAccessSection.tsx
  • src/views/SettingsView/SettingsView.tsx

"https://github.com/mpiton/vortex/releases/latest/download/latest.json"
],
"dialog": true,
"pubkey": "REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace placeholder updater key before any release tagging.

Line 43 still contains a placeholder pubkey; if this ships, updater signature verification is not production-ready.

Add a CI guard that fails when placeholder text exists:

#!/bin/bash
# Verify placeholder updater key is not present in release-ready config.
# Expected result: no output and exit code 1 from rg should be treated as pass in CI logic.
rg -n 'REPLACE_BEFORE_FIRST_RELEASE' src-tauri/tauri.conf.json
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src-tauri/tauri.conf.json` at line 43, The tauri config still contains the
placeholder public key value
"REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate" under the "pubkey"
field which must be replaced before any release; add a CI check that searches
the repo for that exact placeholder string and fails the pipeline if found, and
update the check to run during release/merge pipelines (ensure the check treats
a non-zero grep/ripgrep exit as failure so the job fails when the placeholder
exists); finally, document that the real key should be injected/generated and
the "pubkey" field updated (or populated from a secrets-managed artifact) before
tagging a release.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 12, 2026

Greptile Summary

This PR adds react-i18next (en/fr) with browser language detection, compact mode via CSS variables, an accent color picker, locale-aware date formatting, backend locale validation, and a 6-job GitHub Actions release pipeline producing .deb, .rpm, .dmg, .msi, and Flatpak bundles.

The i18n and theming work is well-structured, but the release pipeline has several P1 defects that would cause every release to silently ship a broken auto-updater:

  • No Tauri signing private key is passed to any build job, so no .sig files are generated and the latest.json produced by update-updater will contain empty signatures.
  • The .sig files are also never uploaded (each upload step only includes the installer artifact), so gh release download --pattern '*.sig' in the final job always finds nothing.
  • The macOS job installs the aarch64-apple-darwin Rust target but runs npx tauri build without --target, building only for the runner's native arch; latest.json then references both darwin-x86_64 and darwin-aarch64 where one will always 404.
  • The publish-flatpak job runs npm ci --offline inside the Flatpak sandbox without pre-generated offline node module sources, so it will fail on every tag push.

Confidence Score: 4/5

i18n and theming changes are safe to merge; the release pipeline has four P1 defects that must be resolved before the first tag push.

The frontend i18n work (hooks, locale files, component updates, tests) and the backend locale validation are clean, well-tested, and low-risk. However, the release pipeline has compounding P1 issues: no Tauri signing key is passed to build jobs so .sig files are never generated; .sig files are not included in any upload step so the update-updater job downloads nothing; the macOS job builds only one architecture while latest.json declares two; and the Flatpak job will fail every run due to npm ci --offline without pre-generated offline sources.

.github/workflows/release.yml and src-tauri/tauri.conf.json require attention before any release tag is pushed.

Important Files Changed

Filename Overview
.github/workflows/release.yml 6-job release pipeline with three P1 defects: Tauri signing key not passed to build jobs so no .sig files are produced or uploaded; macOS builds only one arch while latest.json references two; Flatpak job uses npm ci --offline without offline sources and will always fail.
src-tauri/tauri.conf.json Adds updater plugin config with a literal placeholder pubkey that must be replaced with a real Ed25519 key before any release tag is pushed; leaving it as-is will break the build or signature verification.
src-tauri/src/application/commands/update_config.rs Adds locale validation (en
src/i18n/i18n.ts Initialises react-i18next with createInstance(), browser language detection (localStorage → navigator), and bundled en/fr resources; correct and idiomatic.
src/hooks/useAppEffects.ts Applies compact-mode class and --color-accent CSS variable from config; two separate effects both keyed on config — functional and well-tested.
src/hooks/useLanguage.ts Exposes language switching by coordinating i18n.changeLanguage and backend updateConfig; clean and well-tested.
src/views/SettingsView/AppearanceSection.tsx Adds theme, accent color (preset buttons + hex picker with validation), compact mode toggle, and locale selector; hex normalisation and validation are correct.
src/i18n/locales/en.json Complete English translation file; parity with fr.json is verified by the translation test suite.
src/i18n/locales/fr.json Complete French translation file; keys match en.json verified by tests.
contrib/flatpak/org.vortex.Vortex.yml Flatpak manifest uses npm ci --offline without accompanying offline node-module source files; will fail in the Flatpak sandbox as part of the release workflow.
src/lib/format.ts Adds locale-aware formatDate using Intl.DateTimeFormat alongside existing formatEta, formatSpeed, formatBytes helpers; straightforward and correct.

Sequence Diagram

sequenceDiagram
    participant Tag as Git Tag push
    participant CR as create-release
    participant LX as build-tauri-linux
    participant MX as build-tauri-macos
    participant WN as build-tauri-windows
    participant FP as publish-flatpak
    participant UU as update-updater

    Tag->>CR: trigger on vX.Y.Z
    CR->>CR: extract changelog, create GitHub Release
    CR-->>LX: release_id / upload_url
    CR-->>MX: release_id / upload_url
    CR-->>WN: release_id / upload_url
    CR-->>FP: release_id / upload_url

    LX->>LX: tauri build (no TAURI_SIGNING_PRIVATE_KEY ⚠)
    LX->>CR: upload .deb + .rpm (no .sig ⚠)

    MX->>MX: tauri build native arch only (no --target flag ⚠)
    MX->>MX: notarize .dmg
    MX->>CR: upload .dmg (one arch only, no .sig ⚠)

    WN->>WN: tauri build (no TAURI_SIGNING_PRIVATE_KEY ⚠)
    WN->>CR: upload .msi (no .sig ⚠)

    FP->>FP: npm ci --offline (no offline sources ⚠ → FAIL)
    FP--xCR: flatpak upload fails

    UU->>CR: gh release download --pattern '*.sig' → 0 files ⚠
    UU->>UU: generate latest.json with empty signatures ⚠
    UU->>CR: upload latest.json (broken updater manifest)
Loading

Comments Outside Diff (1)

  1. src-tauri/tauri.conf.json, line 1 (link)

    P1 Updater pubkey is a literal placeholder — tauri build may reject it or the updater will fail signature verification

    tauri.conf.json contains:

    "pubkey": "REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate"

    This is not a valid Ed25519 public key. Tauri v2 validates the pubkey field when building with the updater plugin enabled; an invalid key format will either cause tauri build to fail or cause the runtime updater to reject all signature checks. This must be replaced with a real public key (generated via npx tauri signer generate) before any release tag is pushed, and the corresponding private key must be stored as TAURI_SIGNING_PRIVATE_KEY in repository secrets.

    Fix in Claude Code

Fix All in Claude Code

Reviews (1): Last reviewed commit: "feat(ui,ci): add i18n (en/fr), compact m..." | Re-trigger Greptile

Comment on lines +253 to +276
- uses: actions/checkout@v4

- name: Download signature files from release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.ref_name }}
run: |
mkdir -p sigs
gh release download "$TAG_NAME" --pattern '*.sig' --dir sigs || true

- name: Generate latest.json
env:
TAG_NAME: ${{ github.ref_name }}
run: |
VERSION="${TAG_NAME#v}"
PUB_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
NOTES=$(sed -n "/^## \[$VERSION\]/,/^## \[/{ /^## \[$VERSION\]/d; /^## \[/d; p }" CHANGELOG.md | head -20)
REPO="https://github.com/mpiton/vortex"

# Read signatures from .sig files uploaded by tauri build
LINUX_SIG=$(cat sigs/*amd64*.sig 2>/dev/null || echo "")
MACOS_SIG=$(cat sigs/*x64*.dmg.sig 2>/dev/null || echo "")
MACOS_ARM_SIG=$(cat sigs/*aarch64*.dmg.sig 2>/dev/null || echo "")
WINDOWS_SIG=$(cat sigs/*x64*.msi.sig 2>/dev/null || echo "")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 .sig files are never generated or uploaded — latest.json will always have empty signatures

No build job sets TAURI_SIGNING_PRIVATE_KEY / TAURI_KEY_PASSWORD, so tauri build never produces .sig files. Even if it did, each upload step only publishes the installer artifact (.deb, .dmg, .msi) — there is no files: **/*.sig upload anywhere. As a result gh release download --pattern '*.sig' on line 261 downloads nothing, every cat sigs/…sig falls back to echo "", and latest.json is published with empty signature strings. The Tauri updater plugin rejects empty signatures, so auto-update is silently broken for every release produced by this pipeline.

Fix: Add a repo secret (e.g. TAURI_SIGNING_PRIVATE_KEY) and pass it to each build job, and include the .sig files in the upload step:

# In each build job:
- name: Build Tauri app
  env:
    TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
    TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
  run: npx tauri build

# Upload both the installer AND its .sig (linux example):
- name: Upload .deb and signature to release
  uses: softprops/action-gh-release@v2
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    files: |
      src-tauri/target/release/bundle/deb/*.deb
      src-tauri/target/release/bundle/deb/*.sig

Fix in Claude Code

Comment on lines +113 to +150
run: npm ci

- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-darwin

- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri

- name: Import code signing certificate
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_KEYCHAIN_PASSWD: ${{ secrets.MACOS_KEYCHAIN_PASSWD }}
MACOS_CERTIFICATE_PASSWD: ${{ secrets.MACOS_CERTIFICATE_PASSWD }}
run: |
echo "$MACOS_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p "$MACOS_KEYCHAIN_PASSWD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWD" build.keychain
security import certificate.p12 \
-k build.keychain \
-P "$MACOS_CERTIFICATE_PASSWD" \
-T /usr/bin/codesign
security set-key-partition-list \
-S apple-tool:,apple: \
-s -k "$MACOS_KEYCHAIN_PASSWD" build.keychain
rm certificate.p12

- name: Build Tauri app
env:
APPLE_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
run: npx tauri build

- name: Notarize .dmg
env:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 macOS builds only one architecture; latest.json references both

The Rust toolchain is configured with targets: aarch64-apple-darwin, but npx tauri build is called without --target, so it builds only for the runner's native architecture. On macos-latest (Apple Silicon), only an aarch64 DMG is produced. The update-updater job then emits a latest.json that includes both darwin-x86_64 and darwin-aarch64 entries, but the x86_64 URL will 404 and its signature will be empty.

To produce both artifacts in a single job, use the universal target:

- name: Build Tauri app (universal)
  env:
    APPLE_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
    APPLE_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWD }}
    APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
  run: npx tauri build --target universal-apple-darwin

Then update the latest.json generation to reference a single universal URL, or split into two separate jobs (one on macos-13 for x86_64, one on macos-latest for aarch64).

Fix in Claude Code

Comment on lines +218 to +244
needs: create-release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4

- name: Install Flatpak tools
run: |
sudo apt-get update
sudo apt-get install -y flatpak flatpak-builder
flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo

- name: Build Flatpak bundle
run: |
flatpak-builder --force-clean --user --install-deps-from=flathub \
_flatpak_build contrib/flatpak/org.vortex.Vortex.yml
flatpak build-bundle \
~/.local/share/flatpak/repo \
vortex.flatpak \
org.vortex.Vortex

- name: Upload .flatpak to release
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: vortex.flatpak
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 npm ci --offline inside Flatpak sandbox will always fail

The Flatpak manifest runs npm ci --offline, but the module definition contains no pre-generated offline npm sources (produced by flatpak-node-generator). Inside the Flatpak build sandbox there is no network access and no pre-populated npm cache, so npm ci --offline exits with an error — the publish-flatpak job will fail on every tag push.

The comment in the manifest acknowledges this ("For Flathub submission, use flatpak-node-generator…"), but the CI workflow doesn't guard against it. Options:

  1. Run flatpak-builder with --share=network (acceptable for non-Flathub CI builds) and drop the --offline flag from the manifest's build command.
  2. Pre-generate the sources with flatpak-node-generator package-lock.json -o contrib/flatpak/node-sources.json and add a flatpak-builder-tools source entry referencing it.

Fix in Claude Code

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8 issues found across 42 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="contrib/flatpak/org.vortex.Vortex.yml">

<violation number="1" location="contrib/flatpak/org.vortex.Vortex.yml:42">
P1: `npm ci --offline` conflicts with the current source setup and can fail builds in clean environments because dependencies cannot be fetched.</violation>

<violation number="2" location="contrib/flatpak/org.vortex.Vortex.yml:45">
P1: The install step points to the wrong Cargo output path after overriding `CARGO_TARGET_DIR`, which will break the Flatpak build.</violation>
</file>

<file name="src/i18n/__tests__/translations.test.ts">

<violation number="1" location="src/i18n/__tests__/translations.test.ts:36">
P2: This test only asserts that at least one key exists; it does not verify that English translation values are non-empty strings.</violation>
</file>

<file name=".github/workflows/release.yml">

<violation number="1" location=".github/workflows/release.yml:273">
P1: Missing updater signatures are silently replaced with empty strings, which can publish an invalid `latest.json` instead of failing the release job.</violation>
</file>

<file name="src-tauri/tauri.conf.json">

<violation number="1" location="src-tauri/tauri.conf.json:43">
P1: Updater `pubkey` is still a placeholder string. Replace it with the generated updater public key before merging, otherwise update signature verification is not correctly configured.</violation>
</file>

<file name="src/test-setup.ts">

<violation number="1" location="src/test-setup.ts:7">
P2: The test setup hardcodes a duplicate English locale map that has already drifted from `src/i18n/locales/en.json`, making tests validate different text than production.</violation>
</file>

<file name="src/hooks/useAppEffects.ts">

<violation number="1" location="src/hooks/useAppEffects.ts:15">
P2: Add effect cleanup for `compact-mode` so the global body class is removed when the hook unmounts.</violation>

<violation number="2" location="src/hooks/useAppEffects.ts:25">
P3: Clean up `--color-accent` in the effect cleanup to avoid leaking global style state after unmount.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

build-commands:
# NOTE: For Flathub submission, use flatpak-node-generator to create
# offline npm sources. Local builds can use --share=network instead.
- npm ci --offline
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: npm ci --offline conflicts with the current source setup and can fail builds in clean environments because dependencies cannot be fetched.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At contrib/flatpak/org.vortex.Vortex.yml, line 42:

<comment>`npm ci --offline` conflicts with the current source setup and can fail builds in clean environments because dependencies cannot be fetched.</comment>

<file context>
@@ -0,0 +1,51 @@
+    build-commands:
+      # NOTE: For Flathub submission, use flatpak-node-generator to create
+      # offline npm sources. Local builds can use --share=network instead.
+      - npm ci --offline
+      - npm run build
+      - cargo build --release --manifest-path src-tauri/Cargo.toml
</file context>
Fix with Cubic

"https://github.com/mpiton/vortex/releases/latest/download/latest.json"
],
"dialog": true,
"pubkey": "REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate"
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Updater pubkey is still a placeholder string. Replace it with the generated updater public key before merging, otherwise update signature verification is not correctly configured.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src-tauri/tauri.conf.json, line 43:

<comment>Updater `pubkey` is still a placeholder string. Replace it with the generated updater public key before merging, otherwise update signature verification is not correctly configured.</comment>

<file context>
@@ -33,5 +33,14 @@
+        "https://github.com/mpiton/vortex/releases/latest/download/latest.json"
+      ],
+      "dialog": true,
+      "pubkey": "REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate"
+    }
   }
</file context>
Fix with Cubic

- macOS: build universal binary (ARM+x64) instead of ARM-only
- Validate updater .sig files exist, fail release if missing
- Fix Flatpak builder: add --repo flag, fix CARGO_TARGET_DIR path
- Register tauri-plugin-updater in lib.rs builder chain
- Homebrew cask: add on_intel/on_arm architecture blocks
- Import canonical en.json in test-setup instead of inlined copy
- Strengthen translation tests to check non-empty values per key
- Normalize i18n.language to base code (en-US → en)
- Add effect cleanup for compact-mode and accent-color
- Clear hex validation error when selecting accent preset
- Use availableLanguages from useLanguage hook in AppearanceSection
- Translate proxy URL placeholder in NetworkSection
- Await setLanguage in test act() blocks
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 14 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/hooks/useLanguage.ts">

<violation number="1" location="src/hooks/useLanguage.ts:27">
P2: Unsafe type assertion can return unsupported locales as `Language`; guard `base` against `AVAILABLE_LANGUAGES` before assigning `current`.</violation>
</file>

<file name=".github/workflows/release.yml">

<violation number="1" location=".github/workflows/release.yml:277">
P1: The new signature validation hard-fails `update-updater`, but this workflow never uploads `.sig` assets to the release, so the job will fail on every run.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
.github/workflows/release.yml (1)

271-305: ⚠️ Potential issue | 🔴 Critical

The macOS latest.json entries are wired to DMGs instead of updater bundles.

Tauri’s static updater manifest expects the generated update package URL/signature, and on macOS that package is the signed .app.tar.gz bundle. Lines 272-273 and 295-301 instead assume per-arch *.dmg.sig and .dmg assets. With a single universal macOS build, either publish one universal updater bundle and query it via a custom target like macos-universal, or point both darwin-* keys at the same universal updater asset. (v2.tauri.app)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 271 - 305, The macOS entries in
the latest.json generation are pointing to .dmg and .dmg.sig (MACOS_SIG /
MACOS_ARM_SIG and the "darwin-x86_64"/"darwin-aarch64" platform entries) but
Tauri's updater expects the signed updater bundle (e.g., .app.tar.gz and its
signature); update the script to read the macOS updater asset signature variable
(rename or add e.g. MACOS_UPDATER_SIG) and set the "darwin-x86_64" and
"darwin-aarch64" platform url/signature fields to the actual updater bundle
URL/signature (or point both darwin keys at the same universal updater asset)
instead of the .dmg/.dmg.sig values so the manifest provides the .app.tar.gz
updater package and matching signature.
🧹 Nitpick comments (1)
src/test-setup.ts (1)

25-30: Make mocked i18n.language stateful to avoid masking hook behavior.

Line 25 pins language to "en" permanently. Even after changeLanguage() is called in tests, i18n.language remains "en", which prevents tests from verifying that useLanguage's current property actually updates. The test suite calls setLanguage('fr') but only checks that updateConfig was invoked—it never asserts that current changed, allowing regressions in the hook's language derivation logic to go undetected.

♻️ Proposed refactor
 vi.mock("react-i18next", async () => {
   const en = await import("./i18n/locales/en.json");
+  let language = "en";

   function lookupKey(obj: Record<string, unknown>, key: string): string {
@@
   const t = (key: string) => lookupKey(en.default as unknown as Record<string, unknown>, key);
+  const i18n = {
+    get language() {
+      return language;
+    },
+    changeLanguage: vi.fn(async (next: string) => {
+      language = next;
+    }),
+  };

   return {
     useTranslation: () => ({
       t,
-      i18n: {
-        language: "en",
-        changeLanguage: vi.fn().mockResolvedValue(undefined),
-      },
+      i18n,
       ready: true,
     }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/test-setup.ts` around lines 25 - 30, The mock for useTranslation pins
i18n.language to "en" so changeLanguage() calls don't update it; make the mocked
i18n stateful by introducing a mutable currentLang variable used for
i18n.language and implement changeLanguage (the mock for i18n.changeLanguage) to
update that variable (returning a resolved promise), so when tests call
setLanguage / changeLanguage the i18n.language observed by useLanguage/current
actually changes and tests can assert the updated value; update the
useTranslation mock (the object returned by useTranslation, including t and
i18n) and ensure changeLanguage's mockImplementation updates currentLang.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 147-167: The workflow uses the wrong output path for the universal
macOS build: update the Notarize .dmg step and the Upload .dmg to release step
so they look for the DMG under src-tauri/target/universal-apple-darwin/release/
instead of src-tauri/target/release/; specifically adjust the DMG discovery
command (the DMG=$(find ... ) in the "Notarize .dmg" job) and the files pattern
in the softprops/action-gh-release "Upload .dmg to release" step to reference
the universal-apple-darwin/release directory so notarization and upload find the
generated .dmg for the universal-apple-darwin target.
- Around line 83-96: CI runs call "npx tauri build" without exporting
TAURI_SIGNING_PRIVATE_KEY and the release upload steps only publish installers,
so signature files (*.sig) are never generated or uploaded; set the
TAURI_SIGNING_PRIVATE_KEY env var in each platform job before invoking tauri
build (ensure the secret is read from secrets.TAURI_SIGNING_PRIVATE_KEY), update
the upload steps that use softprops/action-gh-release to also include the
corresponding .sig patterns (e.g., src-tauri/target/release/bundle/*/*.sig or
the platform-specific bundle paths used for .deb/.rpm/.dmg/.msi), and confirm
tauri.conf.json includes the public key and createUpdaterArtifacts: true so
updater artifacts are produced and the downstream gh release download --pattern
'*.sig' and validation loop can succeed.

In `@src-tauri/src/lib.rs`:
- Around line 46-47: The updater plugin is being initialized with
tauri_plugin_updater::Builder::new().build() while the app config contains the
placeholder public key; update the initialization to explicitly set a real
public key (or load and validate it from configuration/env) by calling the
Builder::pubkey(...) method before build (e.g.,
tauri_plugin_updater::Builder::new().pubkey(your_public_key_string).build()),
and add a guard that fails or logs an error if the value still equals the
placeholder token so signed updates won't be used until a generated key replaces
it.

---

Duplicate comments:
In @.github/workflows/release.yml:
- Around line 271-305: The macOS entries in the latest.json generation are
pointing to .dmg and .dmg.sig (MACOS_SIG / MACOS_ARM_SIG and the
"darwin-x86_64"/"darwin-aarch64" platform entries) but Tauri's updater expects
the signed updater bundle (e.g., .app.tar.gz and its signature); update the
script to read the macOS updater asset signature variable (rename or add e.g.
MACOS_UPDATER_SIG) and set the "darwin-x86_64" and "darwin-aarch64" platform
url/signature fields to the actual updater bundle URL/signature (or point both
darwin keys at the same universal updater asset) instead of the .dmg/.dmg.sig
values so the manifest provides the .app.tar.gz updater package and matching
signature.

---

Nitpick comments:
In `@src/test-setup.ts`:
- Around line 25-30: The mock for useTranslation pins i18n.language to "en" so
changeLanguage() calls don't update it; make the mocked i18n stateful by
introducing a mutable currentLang variable used for i18n.language and implement
changeLanguage (the mock for i18n.changeLanguage) to update that variable
(returning a resolved promise), so when tests call setLanguage / changeLanguage
the i18n.language observed by useLanguage/current actually changes and tests can
assert the updated value; update the useTranslation mock (the object returned by
useTranslation, including t and i18n) and ensure changeLanguage's
mockImplementation updates currentLang.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26427ffa-dd52-43ae-a96d-b7db641abafa

📥 Commits

Reviewing files that changed from the base of the PR and between 6243aab and c81fe16.

📒 Files selected for processing (14)
  • .github/workflows/release.yml
  • contrib/flatpak/org.vortex.Vortex.yml
  • contrib/homebrew/vortex.rb
  • src-tauri/src/lib.rs
  • src/hooks/__tests__/useLanguage.test.ts
  • src/hooks/useAppEffects.ts
  • src/hooks/useLanguage.ts
  • src/i18n/__tests__/translations.test.ts
  • src/i18n/locales/en.json
  • src/i18n/locales/fr.json
  • src/index.css
  • src/test-setup.ts
  • src/views/SettingsView/AppearanceSection.tsx
  • src/views/SettingsView/NetworkSection.tsx
✅ Files skipped from review due to trivial changes (7)
  • src/views/SettingsView/NetworkSection.tsx
  • src/index.css
  • src/views/SettingsView/AppearanceSection.tsx
  • src/i18n/locales/fr.json
  • contrib/flatpak/org.vortex.Vortex.yml
  • contrib/homebrew/vortex.rb
  • src/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/hooks/useAppEffects.ts
  • src/hooks/tests/useLanguage.test.ts
  • src/i18n/tests/translations.test.ts
  • src/hooks/useLanguage.ts

Comment on lines +46 to +47
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_updater::Builder::new().build());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's check if the files exist and examine the updater configuration
git ls-files | grep -E "(tauri\.conf\.json|lib\.rs)" | head -20

Repository: mpiton/vortex

Length of output: 256


🏁 Script executed:

# Let's also check the structure of the src-tauri directory
fd -t f "tauri.conf.json" src-tauri/

Repository: mpiton/vortex

Length of output: 82


🏁 Script executed:

# Read the tauri.conf.json file, focusing on the updater section
cat -n src-tauri/tauri.conf.json | head -60

Repository: mpiton/vortex

Length of output: 1426


🏁 Script executed:

# Also read the relevant section of lib.rs around lines 46-47
cat -n src-tauri/src/lib.rs | head -60

Repository: mpiton/vortex

Length of output: 3283


🏁 Script executed:

# Verify the updater plugin behavior by checking if there's documentation or examples
rg "tauri_plugin_updater" --type rust -A 5 src-tauri/ | head -40

Repository: mpiton/vortex

Length of output: 371


🏁 Script executed:

# Check if there's a build/release configuration that might set this
fd -t f "build" src-tauri/ | head -10

Repository: mpiton/vortex

Length of output: 75


🏁 Script executed:

# Read the build.rs file to see if it handles pubkey configuration
cat -n src-tauri/build.rs

Repository: mpiton/vortex

Length of output: 116


The updater is enabled with a placeholder public key.

src-tauri/tauri.conf.json:43 contains REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate, and the builder at src-tauri/src/lib.rs:47 does not override it. The updater plugin will use the configured value when the builder leaves pubkey unset. Signed updates will not verify until that placeholder is replaced with an actual generated public key.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src-tauri/src/lib.rs` around lines 46 - 47, The updater plugin is being
initialized with tauri_plugin_updater::Builder::new().build() while the app
config contains the placeholder public key; update the initialization to
explicitly set a real public key (or load and validate it from
configuration/env) by calling the Builder::pubkey(...) method before build
(e.g.,
tauri_plugin_updater::Builder::new().pubkey(your_public_key_string).build()),
and add a guard that fails or logs an error if the value still equals the
placeholder token so signed updates won't be used until a generated key replaces
it.

- Add TAURI_SIGNING_PRIVATE_KEY to all build jobs so .sig files
  are generated by tauri build
- Upload .sig files to GitHub Release from each build job
- Add CI guard in create-release that fails if updater pubkey
  is still the placeholder value
- Guard useLanguage base locale against AVAILABLE_LANGUAGES
  to prevent unsafe type assertion
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
.github/workflows/release.yml (1)

168-192: ⚠️ Potential issue | 🔴 Critical

Use the universal macOS output directory in these steps.

tauri build --target universal-apple-darwin writes bundles under src-tauri/target/universal-apple-darwin/release/..., so Line 174, Line 186, and Line 192 are still looking in the non-universal tree. That will make notarization miss the DMG and prevent the macOS DMG/signatures from being uploaded. (v2.tauri.app)

Suggested fix
       - name: Notarize .dmg
         env:
           APPLE_ID: ${{ secrets.APPLE_ID }}
           APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
           APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
         run: |
-          DMG=$(find src-tauri/target/release/bundle/dmg -name '*.dmg' | head -1)
+          DMG=$(find src-tauri/target/universal-apple-darwin/release/bundle/dmg -name '*.dmg' | head -1)
           xcrun notarytool submit "$DMG" \
             --apple-id "$APPLE_ID" \
             --password "$APPLE_ID_PASSWORD" \
             --team-id "$APPLE_TEAM_ID" \
             --wait
           xcrun stapler staple "$DMG"

       - name: Upload .dmg to release
         uses: softprops/action-gh-release@v2
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
-          files: src-tauri/target/release/bundle/dmg/*.dmg
+          files: src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg

       - name: Upload .sig files to release
         uses: softprops/action-gh-release@v2
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
-          files: src-tauri/target/release/bundle/**/*.sig
+          files: src-tauri/target/universal-apple-darwin/release/bundle/**/*.sig
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 168 - 192, The workflow is using
the non-universal build output paths so notarization and uploads will miss the
DMG and .sig files; update the three steps "Notarize .dmg", "Upload .dmg to
release", and "Upload .sig files to release" to point at the universal output
tree created by tauri (--target universal-apple-darwin) by changing the DMG
discovery and file globs from src-tauri/target/release/... (and
src-tauri/target/release/bundle/...) to
src-tauri/target/universal-apple-darwin/release/... (e.g., update the DMG find
command and the softprops action with files:
src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg and files:
src-tauri/target/universal-apple-darwin/release/bundle/**/*.sig).
🧹 Nitpick comments (1)
.github/workflows/release.yml (1)

92-95: Prefer the current Tauri password env var name.

Current Tauri v2 docs use TAURI_SIGNING_PRIVATE_KEY_PASSWORD; TAURI_KEY_PASSWORD is the older name. Renaming it here avoids depending on a deprecated alias across all three build jobs. (v2.tauri.app)

Also applies to: 160-165, 225-230

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 92 - 95, Update the Tauri
password environment variable to the current name by replacing
TAURI_KEY_PASSWORD with TAURI_SIGNING_PRIVATE_KEY_PASSWORD in each build job env
block where TAURI_SIGNING_PRIVATE_KEY is set (the same block that contains the
run: npx tauri build); ensure all three occurrences are changed so the workflow
uses TAURI_SIGNING_PRIVATE_KEY_PASSWORD instead of the deprecated alias.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 97-113: Replace the current upload steps named "Upload .deb to
release", "Upload .rpm to release", and "Upload .sig files to release" so the
workflow uploads the Linux AppImage(s) and macOS .app.tar.gz archives instead of
.deb/.rpm/.dmg (adjust the files glob under src-tauri/target/release/bundle/* to
match *.AppImage and *.app.tar.gz and upload their corresponding .sig files),
remove the .deb/.rpm/.dmg upload actions, and ensure .sig upload pattern matches
generated signatures; enable "createUpdaterArtifacts": true in tauri.conf.json
bundle config so signatures are produced; finally, rebuild latest.json so the
linux entry points to the AppImage URL and both darwin-x86_64 and darwin-aarch64
entries point to the same .app.tar.gz URL (unless you intentionally build
per-arch macOS bundles).

---

Duplicate comments:
In @.github/workflows/release.yml:
- Around line 168-192: The workflow is using the non-universal build output
paths so notarization and uploads will miss the DMG and .sig files; update the
three steps "Notarize .dmg", "Upload .dmg to release", and "Upload .sig files to
release" to point at the universal output tree created by tauri (--target
universal-apple-darwin) by changing the DMG discovery and file globs from
src-tauri/target/release/... (and src-tauri/target/release/bundle/...) to
src-tauri/target/universal-apple-darwin/release/... (e.g., update the DMG find
command and the softprops action with files:
src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg and files:
src-tauri/target/universal-apple-darwin/release/bundle/**/*.sig).

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 92-95: Update the Tauri password environment variable to the
current name by replacing TAURI_KEY_PASSWORD with
TAURI_SIGNING_PRIVATE_KEY_PASSWORD in each build job env block where
TAURI_SIGNING_PRIVATE_KEY is set (the same block that contains the run: npx
tauri build); ensure all three occurrences are changed so the workflow uses
TAURI_SIGNING_PRIVATE_KEY_PASSWORD instead of the deprecated alias.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8934ad36-fa0f-439e-a71b-63e96cefb019

📥 Commits

Reviewing files that changed from the base of the PR and between c81fe16 and 8dcb63c.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • src/hooks/useLanguage.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/hooks/useLanguage.ts

Comment on lines +97 to +113
- name: Upload .deb to release
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/release/bundle/deb/*.deb

- name: Upload .rpm to release
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/release/bundle/rpm/*.rpm

- name: Upload .sig files to release
uses: softprops/action-gh-release@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/release/bundle/**/*.sig
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the release workflow file to see all mentioned sections
wc -l .github/workflows/release.yml

Repository: mpiton/vortex

Length of output: 90


🏁 Script executed:

# Read the relevant sections of the release.yml file
cat -n .github/workflows/release.yml | sed -n '97,113p'

Repository: mpiton/vortex

Length of output: 782


🏁 Script executed:

# Also check lines 182-192 and 303-338 mentioned in "Also applies to"
cat -n .github/workflows/release.yml | sed -n '182,192p'

Repository: mpiton/vortex

Length of output: 539


🏁 Script executed:

cat -n .github/workflows/release.yml | sed -n '303,338p'

Repository: mpiton/vortex

Length of output: 1923


🏁 Script executed:

# Check the src-tauri/src/lib.rs file for updater configuration around lines 42-51
cat -n src-tauri/src/lib.rs | sed -n '1,60p'

Repository: mpiton/vortex

Length of output: 3283


🏁 Script executed:

# Search for any updater-related configuration in the codebase
fd -e rs | xargs rg -l "updater" -i

Repository: mpiton/vortex

Length of output: 77


🏁 Script executed:

# Check if there's a Tauri configuration file
fd -e toml -e json | grep -iE "(tauri|vite)" | head -20

Repository: mpiton/vortex

Length of output: 340


🏁 Script executed:

# Look for tauri.conf.json or src-tauri/tauri.conf.json
fd -name "tauri.conf.json"

Repository: mpiton/vortex

Length of output: 287


🏁 Script executed:

# Search for any bundle configuration in the codebase
rg -i "bundle" src-tauri --type toml --type json

Repository: mpiton/vortex

Length of output: 50369


🏁 Script executed:

# Check the actual Tauri configuration
find . -name "tauri.conf.json" -exec cat {} \;

Repository: mpiton/vortex

Length of output: 1104


🌐 Web query:

Tauri v2 updater supported bundle formats AppImage dmg msi

💡 Result:

In Tauri v2, the updater supports the following bundle formats: Linux: AppImage (re-used directly by updater, with .sig file) macOS: .app bundle (archived as .app.tar.gz for updater, with .sig) Windows: MSI (.msi with .sig) and NSIS (.exe with .sig) AppImage, DMG, and MSI are supported as follows: - AppImage: Yes (Linux, standard bundle re-used). - DMG: No, macOS uses .app.tar.gz (DMG is a separate bundler target not used for updates). - MSI: Yes (Windows). Enable by setting "bundle": {"createUpdaterArtifacts": true} in tauri.conf.json, generate signing keys with tauri signer generate, set TAURI_SIGNING_PRIVATE_KEY env var, and add updater plugin.

Citations:


🌐 Web query:

Tauri v2 updater latest.json format platform requirements

💡 Result:

The latest.json format for Tauri v2 updater (static JSON file mode) is as follows: { "version": "1.0.0", // Required: SemVer string (with or without leading 'v') "notes": "Optional release notes string", "pub_date": "Optional RFC 3339 formatted date string, e.g. '2026-04-12T00:00:00Z'", "platforms": { "linux-x86_64": { "signature": "base64 signature content from .sig file", // Required "url": "https://example.com/linux-x86_64-installer.AppImage" // Required, direct download URL }, // Other platforms... } } Required top-level keys: "version", and for the client's platform, "platforms.[target].url" and "platforms.[target].signature". Platform keys format: OS-ARCH, where OS is 'linux', 'darwin', or 'windows'; ARCH is 'x86_64', 'aarch64', 'i686', or 'armv7'. Examples: - windows-x86_64 (.msi, .exe) - darwin-x86_64 (.dmg Intel) - darwin-aarch64 (.dmg ARM) - linux-x86_64 (.AppImage, .deb, .rpm, .tar.gz) Tauri validates the entire file before version check, so all listed platforms must have valid url and signature. Configuration requirements in tauri.conf.json: - "bundle": { "createUpdaterArtifacts": true } to generate bundles and .sig files during build. - "plugins": { "updater": { "pubkey": "your-public-key-content", "endpoints": ["https://yourhost.com/latest.json"] } } Permissions: Add "updater:default" to capabilities (e.g. src-tauri/capabilities/default.json). Generate keys: tauri signer generate. For GitHub Releases, use https://github.com/user/repo/releases/latest/download/latest.json as endpoint; tauri-action generates it automatically with includeUpdaterJson: true. Dynamic server mode uses single-object format (no platforms), but latest.json refers to static multi-platform JSON.

Citations:


🏁 Script executed:

# Check the full bundle configuration in tauri.conf.json
cat src-tauri/tauri.conf.json | grep -A 20 '"bundle"'

Repository: mpiton/vortex

Length of output: 521


Fix updater artifact types: upload AppImage and .app.tar.gz instead of .deb/.rpm/.dmg, and rebuild latest.json accordingly.

Tauri v2's updater plugin expects Linux AppImage bundles and macOS .app.tar.gz archives, but the workflow currently uploads .deb/.rpm installers (Linux) and .dmg installers (macOS), then references these in latest.json. The updater will fail to download and verify these artifact types. Additionally, tauri.conf.json does not enable "createUpdaterArtifacts": true in the bundle configuration, so .sig signature files won't be auto-generated. Without custom updater targets in the configuration, Tauri's defaults apply: both darwin-x86_64 and darwin-aarch64 entries should reference the same .app.tar.gz bundle URL unless platform-specific builds are intentionally created. As configured, Linux and macOS auto-updates will fail. (v2.tauri.app/plugin/updater)

Also applies to: 182-192, 303-338

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 97 - 113, Replace the current
upload steps named "Upload .deb to release", "Upload .rpm to release", and
"Upload .sig files to release" so the workflow uploads the Linux AppImage(s) and
macOS .app.tar.gz archives instead of .deb/.rpm/.dmg (adjust the files glob
under src-tauri/target/release/bundle/* to match *.AppImage and *.app.tar.gz and
upload their corresponding .sig files), remove the .deb/.rpm/.dmg upload
actions, and ensure .sig upload pattern matches generated signatures; enable
"createUpdaterArtifacts": true in tauri.conf.json bundle config so signatures
are produced; finally, rebuild latest.json so the linux entry points to the
AppImage URL and both darwin-x86_64 and darwin-aarch64 entries point to the same
.app.tar.gz URL (unless you intentionally build per-arch macOS bundles).

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/release.yml">

<violation number="1" location=".github/workflows/release.yml:113">
P1: Uploading all `*.sig` files makes signature selection in `update-updater` ambiguous; the Linux signature value can contain the wrong/concatenated signature for the `.deb` URL.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

- Fix macOS universal build output paths (target/universal-apple-darwin/)
- Upload .app.tar.gz updater bundle for macOS (not .dmg)
- Upload .AppImage updater bundle for Linux
- Upload .exe updater bundle for Windows (NSIS)
- Fix latest.json URLs to reference updater-compatible formats
- Use specific .sig file patterns per platform instead of glob
- Remove darwin-x86_64/darwin-aarch64 split in favor of darwin-universal
@mpiton mpiton merged commit d6a8f9f into main Apr 12, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant