feat: MVP Phase 8 — i18n, theming, release pipeline#26
Conversation
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/.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds 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 Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
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 | 🟡 MinorLocalize 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 examplesettings.network.proxyUrlPlaceholder) and render viat(...).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 | 🟡 MinorPreset 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
enlocale 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"andsha256 "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.
LOCALESandsupportedLangsduplicate language source-of-truth already owned byuseLanguage(). 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'sUpdateConfigCommandvalidates against["en", "fr", "de", "es", "ja", "zh"]. This inconsistency means:
- Users cannot select languages the backend already supports (de, es, ja, zh)
- 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
⛔ Files ignored due to path filters (5)
package-lock.jsonis excluded by!**/package-lock.jsonsrc-tauri/Cargo.lockis excluded by!**/*.locksrc-tauri/gen/schemas/acl-manifests.jsonis excluded by!**/gen/**src-tauri/gen/schemas/desktop-schema.jsonis excluded by!**/gen/**src-tauri/gen/schemas/linux-schema.jsonis excluded by!**/gen/**
📒 Files selected for processing (37)
.github/workflows/release.ymlCHANGELOG.mdcontrib/flatpak/org.vortex.Vortex.ymlcontrib/homebrew/vortex.rbcontrib/icons/README.mdcontrib/vortex.desktopcontrib/vortex.servicecontrib/winget/Vortex.yamlpackage.jsonsrc-tauri/Cargo.tomlsrc-tauri/src/application/commands/update_config.rssrc-tauri/tauri.conf.jsonsrc/App.tsxsrc/hooks/__tests__/useAppEffects.test.tssrc/hooks/__tests__/useLanguage.test.tssrc/hooks/useAppEffects.tssrc/hooks/useLanguage.tssrc/i18n/__tests__/translations.test.tssrc/i18n/i18n.tssrc/i18n/locales/en.jsonsrc/i18n/locales/fr.jsonsrc/index.csssrc/layouts/AppLayout.tsxsrc/layouts/Sidebar.tsxsrc/lib/format.tssrc/test-setup.tssrc/types/layout.tssrc/views/DownloadDetailsPanel/FileInfoSection.tsxsrc/views/DownloadsView/SearchBar.tsxsrc/views/LinkGrabberView/MediaGrabberDialog/MediaGrabberDialog.tsxsrc/views/SettingsView/AppearanceSection.tsxsrc/views/SettingsView/BrowserSection.tsxsrc/views/SettingsView/DownloadsSection.tsxsrc/views/SettingsView/GeneralSection.tsxsrc/views/SettingsView/NetworkSection.tsxsrc/views/SettingsView/RemoteAccessSection.tsxsrc/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" |
There was a problem hiding this comment.
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 SummaryThis 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 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:
Confidence Score: 4/5i18n 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
Sequence DiagramsequenceDiagram
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)
|
.github/workflows/release.yml
Outdated
| - 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 "") |
There was a problem hiding this comment.
.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| 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: |
There was a problem hiding this comment.
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-darwinThen 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).
| 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 |
There was a problem hiding this comment.
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:
- Run
flatpak-builderwith--share=network(acceptable for non-Flathub CI builds) and drop the--offlineflag from the manifest's build command. - Pre-generate the sources with
flatpak-node-generator package-lock.json -o contrib/flatpak/node-sources.jsonand add aflatpak-builder-toolssource entry referencing it.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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>
| "https://github.com/mpiton/vortex/releases/latest/download/latest.json" | ||
| ], | ||
| "dialog": true, | ||
| "pubkey": "REPLACE_BEFORE_FIRST_RELEASE__run_npx_tauri_signer_generate" |
There was a problem hiding this comment.
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>
- 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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
.github/workflows/release.yml (1)
271-305:⚠️ Potential issue | 🔴 CriticalThe macOS
latest.jsonentries 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.gzbundle. Lines 272-273 and 295-301 instead assume per-arch*.dmg.sigand.dmgassets. With a single universal macOS build, either publish one universal updater bundle and query it via a custom target likemacos-universal, or point bothdarwin-*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 mockedi18n.languagestateful to avoid masking hook behavior.Line 25 pins
languageto"en"permanently. Even afterchangeLanguage()is called in tests,i18n.languageremains"en", which prevents tests from verifying thatuseLanguage'scurrentproperty actually updates. The test suite callssetLanguage('fr')but only checks thatupdateConfigwas invoked—it never asserts thatcurrentchanged, 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
📒 Files selected for processing (14)
.github/workflows/release.ymlcontrib/flatpak/org.vortex.Vortex.ymlcontrib/homebrew/vortex.rbsrc-tauri/src/lib.rssrc/hooks/__tests__/useLanguage.test.tssrc/hooks/useAppEffects.tssrc/hooks/useLanguage.tssrc/i18n/__tests__/translations.test.tssrc/i18n/locales/en.jsonsrc/i18n/locales/fr.jsonsrc/index.csssrc/test-setup.tssrc/views/SettingsView/AppearanceSection.tsxsrc/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
| .plugin(tauri_plugin_notification::init()) | ||
| .plugin(tauri_plugin_updater::Builder::new().build()); |
There was a problem hiding this comment.
🧩 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 -20Repository: 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 -60Repository: 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 -60Repository: 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 -40Repository: 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 -10Repository: 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.rsRepository: 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
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
.github/workflows/release.yml (1)
168-192:⚠️ Potential issue | 🔴 CriticalUse the universal macOS output directory in these steps.
tauri build --target universal-apple-darwinwrites bundles undersrc-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_PASSWORDis 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
📒 Files selected for processing (2)
.github/workflows/release.ymlsrc/hooks/useLanguage.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/hooks/useLanguage.ts
.github/workflows/release.yml
Outdated
| - 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 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the release workflow file to see all mentioned sections
wc -l .github/workflows/release.ymlRepository: 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" -iRepository: 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 -20Repository: 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 jsonRepository: 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:
- 1: https://v2.tauri.app/plugin/updater/
- 2: https://v2.tauri.app/ko/plugin/updater/
- 3: https://github.com/tauri-apps/tauri-docs/blob/v2/src/content/docs/plugin/updater.mdx
- 4: https://v2.tauri.app/plugin/updater
🌐 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:
- 1: https://v2.tauri.app/plugin/updater/
- 2: https://v2.tauri.app/plugin/updater
- 3: https://github.com/tauri-apps/tauri-docs/blob/v2/src/content/docs/plugin/updater.mdx
🏁 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).
There was a problem hiding this comment.
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
Summary
Changes
i18n (Task 27)
src/i18n/— i18next init + en.json/fr.json translationssrc/hooks/useLanguage.ts,src/hooks/useAppEffects.tst()callssrc/index.css— compact mode CSS rulessrc-tauri/.../update_config.rs— locale validation (en, fr)Release (Task 28)
.github/workflows/release.yml— 6-job pipeline with per-job permissionscontrib/— systemd unit, .desktop, Flatpak manifest, icon docs, Winget/Homebrew templatessrc-tauri/tauri.conf.json— updater plugin configsrc-tauri/Cargo.toml— tauri-plugin-updater dependencyTest plan
cargo test --workspace— 432 passed, 0 failednpx vitest run— 297 passed, 0 failedcargo clippy -- -D warnings— 0 errorsnpx oxlint .— 0 warnings/errorsnpx tsc -b— compilation OKcargo fmt --check— cleanSummary 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
react-i18next+i18next-browser-languagedetector, complete en/fr strings,useLanguagehook, locale‑aware dates, backend locale validation, and normalized browser locales (en‑US → en) with supported‑set guard.useAppEffectsto apply body/root changes with cleanup..sig(Linux:.AppImage, macOS universal:.app.tar.gz, Windows: NSIS.exe); fixes macOS universal output paths andlatest.jsonURLs; enforces CI guard if updaterpubkeyis a placeholder; integratestauri-plugin-updaterwith signature verification; Flatpak builder fixes; systemd user unit, Freedesktop.desktop, Winget/Homebrew templates.Migration
pubkeyinsrc-tauri/tauri.conf.json, addTAURI_SIGNING_PRIVATE_KEY, and hostlatest.jsonwith.sigfor updater bundles (.app.tar.gz/.AppImage/.exe).vX.Y.Ztag.Written for commit 2ea77e0. Summary will update on new commits.
Summary by CodeRabbit
New Features
Documentation
Chores