Compare commits

...

246 Commits

Author SHA1 Message Date
github-actions[bot]
7d618f12d8 apply version updates (#12518)
Co-authored-by: amrbashir <48618675+amrbashir@users.noreply.github.com>
2025-02-26 12:45:31 -03:00
Amr Bashir
385a41dea2 enhance(windows): disable our in-client resizing for undecorated window with shadows (#12817)
* enhance(windows): disable our in-client resizing for undecorated window with shadows

ref: https://github.com/tauri-apps/tao/pull/1052

* skip hittesting for undeceorated windows
2025-02-26 17:21:46 +02:00
Fabian-Lars
955832e56b ci: Build win-arm64 cli with rustls (#12813) 2025-02-25 21:47:46 +01:00
Fabian-Lars
c116dfcdee fix(cli): Hide updater bundle target in help output (#12801)
ref https://github.com/tauri-apps/tauri/issues/3251#issuecomment-2677139820
2025-02-25 20:25:28 +02:00
Fabian-Lars
d6520a21ce chore(deps): wry@0.50 muda@0.16 tray-icon@0.20 windows@0.60 webview2-com@0.36 objc@0.6 window-vibrancy@0.6 tao@0.32 (#12541)
* chore(deps): Update windows to 0.59. Update webview2-com to 0.35

* wry and other crates and objc2

* window-vibrancy 0.6

* Update windows059-webview035.md

* win compile error

* tao

* tao 0.32.1

* updatus maximus

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-02-25 19:41:00 +02:00
Simon Laux
ab81adb71b docs: improve documentation around incognito and data store (#12806) 2025-02-25 06:17:20 +02:00
kandrelczyk
6e417c9435 fix(linux): Add missing RPM signature (#12786) 2025-02-24 14:52:06 +01:00
Kotkoroid
ddc469367a style: fix Vite and React branding (#12768) 2025-02-22 11:30:31 +01:00
Sean Wang
d7b998fe71 fix(tauri): deprecate Manager::unmanage to fix use-after-free (#12723)
close #12721
2025-02-21 02:38:31 +02:00
renovate[bot]
d9a07e66af chore(deps): update dependency globals to v16 (#12750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-20 15:26:14 +01:00
renovate[bot]
0adeb4e7c5 chore(deps): update rust crate infer to 0.19 (dev) (#12599)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-02-20 14:36:44 +01:00
Jim
70d8557cc3 docs(testing): inform reader about 'tauri://localhost' (#12623) 2025-02-20 12:48:33 +01:00
Tony
95fc3cd424 chore: cleanup a few unneeded clones (#12733) 2025-02-18 18:35:40 +08:00
renovate[bot]
4633705da7 chore(deps): update dependency rollup to v4.34.8 (#12727)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 20:12:00 +08:00
Tony
3f680588cd chore: update prettier to 3.5.1 and enable experimentalOperatorPosition (#12715) 2025-02-16 20:34:19 +08:00
Sean Wang
7d8252679d feat(tauri): export struct tauri::ExitRequestApi (#12701) 2025-02-15 16:21:06 +01:00
renovate[bot]
ee95c1b1ed chore(deps): update dependency rollup to v4.34.7 (#12702)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-15 10:19:48 +08:00
Ege Güngördü
741e44b45c fix: fix incorrect example for WebviewBuilder::from_config (#12695) 2025-02-13 00:04:29 +02:00
renovate[bot]
8e9339e880 chore(deps): update rust crate jsonschema to 0.29 (dev) (#12660)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-09 14:19:32 +01:00
renovate[bot]
053b57c1df chore(deps): update dependency rollup to v4.34.6 (dev) (#12647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 20:07:55 +01:00
renovate[bot]
b6a56f3616 chore(deps): update dependency rollup to v4.34.5 (#12641)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 10:18:38 +01:00
dependabot[bot]
11945e561c chore(deps-dev): bump vitest from 3.0.3 to 3.0.5 (#12637)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-06 18:48:57 +01:00
renovate[bot]
b6ad316460 chore(deps): update dependency rollup to v4.34.4 (dev) (#12604)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-02-06 18:32:56 +01:00
Nazar Antoniuk
5eba0785c4 feat: add Ukrainian translation for the custom tauri messages in the nsis bundle (#12605) 2025-02-06 17:44:48 +02:00
dependabot[bot]
6038f09d85 chore(deps): bump openssl from 0.10.68 to 0.10.70 (#12606)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-03 21:30:48 +01:00
renovate[bot]
e3b0260871 chore(deps): update dependency rollup to v4.34.0 (#12593)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-02 19:54:02 +08:00
Antony Kurniawan
a6ada76a9f docs: update devUrl on config schema (#12592) 2025-02-01 23:45:13 +02:00
renovate[bot]
bfc71e845b chore(deps): update rust crate ureq to v3 (dev) (#12524)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-01-29 23:23:38 +01:00
renovate[bot]
0a11b8741a chore(deps): update rust crate tauri-winres to 0.3 (dev) (#12559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-29 21:51:24 +01:00
renovate[bot]
6b70fbcc84 chore(deps): update dependency rollup to v4.32.1 (dev) (#12548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <github@fabianlars.de>
2025-01-28 14:46:04 +01:00
Amr Bashir
e9c9c4d6f6 feat: derive Clone for PathResolver (#12529)
closes #12528
2025-01-26 20:11:15 +02:00
Sean Wang
abdd558075 feat(tauri/Emitter): add emit_str* method to emit serialized data directly (#12460)
* feat(tauri/Emitter): add `emit_str*` method to emit serialized data

* style: rust fmt
2025-01-26 17:58:21 +02:00
Sean Wang
3dbcbe7685 fix(tauri): Webview::navigate unnecessarily borrows &mut self (#12461) 2025-01-25 22:21:04 +01:00
Bastian Kistner
a2d36b8c34 feat: disable background throttling (#12181)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-25 22:20:31 +01:00
github-actions[bot]
5a3647bdfe Apply Version Updates From Current Changes (#12515)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-25 20:51:13 +01:00
Fabian-Lars
477e9c0496 fix(core): Use safe_block_on in mobile proxy (#12514)
fixes #12513
2025-01-25 20:18:46 +01:00
github-actions[bot]
82d634f4a9 Apply Version Updates From Current Changes (#12512)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-01-25 18:23:32 +01:00
Fabian-Lars
8e9134c4a2 fix(cli): Apply --bins flag on build instead of dev (#12511) 2025-01-25 17:50:46 +01:00
github-actions[bot]
dc1997b77d apply version updates (#12439)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-01-25 15:29:40 +01:00
bradleat
1a86974aa3 fix(cli): let xcode handle building for ios build --open (#12406)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-25 13:42:20 +01:00
bicarlsen
fb294af8e3 fix(tauri-driver): Parse ms:edgeOptions separately (#12383)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-25 13:13:13 +01:00
Fabian-Lars
46c7b16111 ci(renovate): Disable oxc_ PRs 2025-01-25 12:10:59 +01:00
Fabian-Lars
9dac2863af fix(bundler): Don't self-sign dmg (#12323) 2025-01-25 11:46:16 +01:00
renovate[bot]
9a9d1205b0 chore(deps): update dependency rollup to v4.32.0 (dev) (#12502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 11:45:16 +01:00
Fabian-Lars
27096cdc05 fix(cli): don't force native-tls feature on desktop (#12445) 2025-01-25 11:27:29 +01:00
sftse
6cbfc4878d refactor: document Emitter/Listner traits panics, refactor check into internal struct (#12444) 2025-01-24 04:17:17 +02:00
Andzej Korovacki
f5a59b93bf fix(bundler): change build metadata verification into a warning (#12136)
Co-authored-by: akorovacki <andzej.korovacki@geniussports.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-23 15:25:11 +01:00
renovate[bot]
5432752e51 chore(deps): update dependency vitest to v3 (dev) (#12421)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-23 14:03:40 +01:00
renovate[bot]
bf912b8e08 chore(deps): update rust crate dirs to v6 (dev) (#12372)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-23 13:58:00 +01:00
Trey Smith
9a30bed98c fix(macos): frameworks being signed with entitlements unnecessarily (#12423) 2025-01-23 12:14:33 +01:00
dependabot[bot]
9d02c18ac2 chore(deps-dev): bump vite from 6.0.7 to 6.0.9 (#12469)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.0.7 to 6.0.9.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.0.9/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 17:06:41 +08:00
anatawa12
de8600b4d9 fix(bundler): Bump nsis tauri utils to 0.4.2 (#12471)
* chore: bump nsis-tauri-utils

* docs(changelog)): Bump nsis-tauri-utils to 0.4.2
2025-01-22 03:01:04 +02:00
Mads Marquart
0ea8894579 chore(deps): prepare for objc2 frameworks v0.3 (#12468)
These will have a bunch of default features enabled, so let's
pre-emptively disable them.
2025-01-21 22:57:45 +02:00
Fabian-Lars
fbe7c9ead7 fix(bundler): fix injectedbundle search path (#12466) 2025-01-21 16:52:41 +02:00
Tony
b8eb28877f enhance(nsis): clean up reg keys in uninstaller (#12427) 2025-01-20 22:33:54 +02:00
Tomas Tamadamas
90c6546faf chore: Rewrite simply "Localhost free term" in README.md within #10510 (#12415) 2025-01-20 16:22:22 +02:00
renovate[bot]
4ed2ab76e2 chore(deps): update dependency rollup to v4.31.0 (dev) (#12446)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-19 17:59:01 +01:00
Fabian-Lars
bc43c738ba fix(cli): Add clipboard-manager to known plugins (#12442)
* fix(cli): Add `clipboard-manager` to known plugins

fixes #12440

* Update plugins.rs
2025-01-19 14:19:10 +02:00
Jim
0b79af7114 enhance(cli): log message to inform user of DevCommand being used (#12438) 2025-01-18 22:21:21 +02:00
github-actions[bot]
a70e690fe7 apply version updates (#12425)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-01-18 10:07:28 +01:00
Felix Eckhardt
72748cc45c fix(windows): Resolve broken installation directory handling in MSI & NSIS, preventing duplicate installations during updates (#12365) 2025-01-17 21:25:15 +01:00
Fabian-Lars
cf771bf69a fix(bundler/wix): Prevent dlls from overwriting root resources (#12402) 2025-01-17 16:41:31 +01:00
Fabian-Lars
07ccdc499c fix(bundler/nsis): Include WebView2Loader.dll if found to match msi (#12324)
* fix(bundler/nsis): Include WebView2Loader.dll if found to match msi behavior

* Update fix-nsis-webviewloaderdll.md

* only include dll in gnu builds
2025-01-17 17:12:58 +02:00
mattyg
d2c8f0eb5c fix: run tauri's internal init scripts before user's scripts (#12424) 2025-01-17 03:37:41 +02:00
Fabian-Lars
b643dcc1c4 docs(utils): Fix typo in useLocalToolsDir (#12409) 2025-01-15 14:59:34 +02:00
renovate[bot]
cd7d08b63f chore(deps): update dependency eslint-config-prettier to v10 (#12386)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-15 10:54:33 +08:00
github-actions[bot]
4c3f047735 Apply Version Updates From Current Changes (#12377)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-01-13 13:08:29 +01:00
Fabian-Lars
61e69db9e4 chore: Add changefile for #12371 (#12376) 2025-01-13 12:23:17 +01:00
Oscar Beaumont
75d56e8364 fix: fix Specta integration (#12371) 2025-01-13 04:03:36 +02:00
github-actions[bot]
a8aca70151 Apply Version Updates From Current Changes (#12355)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-01-11 18:13:55 +01:00
Fabian-Lars
cad5504455 fix(cli): Fix npm package name in tauri add (#12354) 2025-01-11 17:28:54 +01:00
renovate[bot]
f8e50e8e5b chore(deps): update rust crate tauri-winres to 0.2 (dev) (#12341)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-10 20:19:48 +01:00
renovate[bot]
cfe1af2848 chore(deps): update rust crate notify to v8 (dev) (#12342)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-10 20:02:53 +01:00
github-actions[bot]
89c6f08e82 Apply Version Updates From Current Changes (#12218)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-01-10 15:08:21 +01:00
Fabian-Lars
cde0ff7798 chore: Fix clippy 1.84 warnings (#12328) 2025-01-10 13:47:37 +01:00
renovate[bot]
b0d7527250 chore(deps): update rust crate windows-registry to 0.4 (dev) (#12301)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 23:04:03 +01:00
renovate[bot]
a28b5013c5 chore(deps): update rust crate ico to 0.4 (dev) (#12298)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 22:38:43 +01:00
Fabian-Lars
b9a99a5c69 fix(cli): Skip File Access events in dev server (#12297) 2025-01-07 21:11:24 +01:00
Marco Mengelkoch
98f62e65a2 fix(cli): tauri add NPM packages for community plugins (#12246)
It currently isn't possible to simply add a community plugin the same was as adding official plugins.
Trying to perform  `npm run tauri add tauri-plugin-python` is trying to install npm package `@tauri-apps/plugin-python`.
But the npm scope `@tauri-apps/` is reserved for official tauri plugins.

The official documentation recommends to name the npm package `tauri-plugin-{name}-api` and it should be possible to have a parameter that makes it possible to install that package.

- closes #12217

This changes the command to check if the plugin is an official tauri plugin or not, using the appropriate npm package name format

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2025-01-07 10:16:35 -03:00
renovate[bot]
c130af6b06 chore(deps): update dependency rollup to v4.30.1 (dev) (#12291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-07 13:48:40 +01:00
Lucas Fernandes Nogueira
ef21ed9ac1 fix(cli): iOS build crashing when development team has spaces (#12290)
Even though I couldn't even get the build to succeed when using the team name as the "developmentTeam" configuration (instead of the team ID), I've received reports that our processing of that value is broken and only works when it is escaped using `\"`.
2025-01-07 09:41:56 -03:00
Tony
cd1d026f97 fix: fails to build if the project path contains glob characters (#11961)
* Escape glob patterns

* Add change file

* Reword the change
2025-01-07 09:22:43 -03:00
Aurélien Jacobs
848d0e060e chore(deps): update cargo_toml for edition 2024 [fix #10412] (#12270)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-06 20:34:49 +01:00
Max Katz
ae75a353d0 chore: update copyright year (#12170)
Co-authored-by: Max Katz <zrxmax@icloud.com>
2025-01-06 13:46:06 +01:00
gerald
70f96e3222 chore(deps/driver): update hyper to version 1 (#12240)
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-06 11:12:08 +01:00
renovate[bot]
3acf679c87 chore(deps): update dependency rollup to v4.30.0 (dev) (#12263)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 10:47:03 +01:00
renovate[bot]
22d5852208 chore(deps): update dependency rollup to v4.29.2 (dev) (#12238)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-05 14:58:18 +01:00
renovate[bot]
701778a195 chore(deps): update dependency fast-glob to v3.3.3 (dev) (#12233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-05 12:46:30 +01:00
Patrick F.
a0f2c84d51 feat(cli): Add 64x64 png to default tauri icon command (#12204) 2025-01-04 23:26:18 +01:00
github-actions[bot]
f86e2387c9 apply version updates (#12183)
Co-authored-by: FabianLars <30730186+FabianLars@users.noreply.github.com>
2025-01-04 01:17:49 +01:00
Fabian-Lars
26fc9558fe fix(cli): Re-add TriggeredKill in dev watcher logic (#12178) 2025-01-04 00:39:07 +01:00
renovate[bot]
90dc7b19fc chore(deps): update rust crate which to v7 (dev) (#12144)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-03 23:28:10 +01:00
renovate[bot]
c681d835d5 chore(deps): update dependency vite to v6 (dev) (#12133)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-03 23:05:29 +01:00
Fabian-Lars
208d8968ce ci(deps): Update repository-dispatch to v3 (#12169) 2025-01-03 22:34:55 +01:00
Fabian-Lars
aaa7d9bb13 ci: Switch to upstream create-pull-request (#12123)
* ci: Switch to upstream create-pull-request

* what the actual f

* fix version comment

---------

Co-authored-by: Tillmann <28728469+tweidinger@users.noreply.github.com>
2025-01-03 22:48:50 +02:00
github-actions[bot]
d150a40b09 Apply Version Updates From Current Changes (#12165)
Co-authored-by: FabianLars <FabianLars@users.noreply.github.com>
2025-01-03 15:34:54 +01:00
Fabian-Lars
881729448c fix(cli): Ignore file access events (#12164) 2025-01-03 15:00:31 +01:00
github-actions[bot]
cd841d8e33 Apply Version Updates From Current Changes (#11659)
Co-authored-by: FabianLars <FabianLars@users.noreply.github.com>
2025-01-02 19:29:17 +01:00
renovate[bot]
3d8a39aa4a chore(deps): update rust crate resvg to 0.44.0 (dev) (#12126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 16:55:09 +01:00
renovate[bot]
50e92d097b chore(deps): update rust crate worker to 0.5 (dev) (#12127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-02 16:44:07 +01:00
renovate[bot]
bc4dfcd798 chore(deps): update rust crate pico-args to 0.5 (dev) (#12125)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-02 16:12:18 +01:00
renovate[bot]
832ad10e3a chore(deps): update rust crate cargo_metadata to 0.19 (dev) (#12113)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-02 15:54:39 +01:00
renovate[bot]
0198354961 chore(deps): update rust crate oxc crates to 0.38 (dev) (#12122)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-02 14:56:23 +01:00
renovate[bot]
c0a5a10cff chore(deps): update rust crate notify-debouncer-mini to 0.5 (dev) (#12118)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-02 14:08:50 +01:00
Dmitry Dygalo
d7f48cb324 chore(deps): Update jsonschema to 0.28 (#12089) 2025-01-02 13:22:08 +01:00
Tony
a16796a555 fix(api): use array for channel queueing (#12069) 2025-01-02 12:19:50 +01:00
renovate[bot]
231e9a5ee1 chore(deps): update rust crate image to 0.25.5 (dev) (#12105)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-02 01:11:42 +01:00
renovate[bot]
b0ddee8992 chore(deps): update rust crate wry to 0.48 (dev) (#12101)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-02 01:01:31 +01:00
renovate[bot]
5319325886 chore(deps): update dependency @napi-rs/cli to v2.18.4 (dev) (#12097)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 23:41:07 +01:00
renovate[bot]
ccce63f8c8 chore(config): migrate renovate config (#12099)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-01 23:35:57 +01:00
renovate[bot]
da958395ff chore(config): migrate renovate config (#12096)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Fabian-Lars <github@fabianlars.de>
2025-01-01 23:28:38 +01:00
renovate[bot]
1737df3e33 chore(deps) Update Tauri Bundler (dev) (#11669)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: FabianLars <fabianlars@fabianlars.de>
2025-01-01 23:12:29 +01:00
renovate[bot]
2df426ed1d chore(deps) Update Rust crate syn to v2.0.94 (dev) (#12094)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 22:27:28 +01:00
renovate[bot]
361fdb3585 chore(deps) Update Rust crate syn to v2.0.94 (dev) (#12088)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 22:16:42 +01:00
renovate[bot]
838b2b8b3b chore(deps) Update Tauri Build (#12087)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 22:24:34 +08:00
renovate[bot]
f87e0485ca chore(deps) Update Tauri API Definitions (#12084)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 22:21:46 +08:00
renovate[bot]
b794ca4a27 chore(deps) Update Rust crate syn to v2.0.93 (#12083)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 22:21:08 +08:00
Dimitris Apostolou
8f282c6305 deps: fix crate vulnerabilities (#12080) 2025-01-01 00:20:15 +02:00
Lieke
ed118da266 fix: fix typo in AndroidConfig docstring (#12003) 2024-12-30 01:25:29 +02:00
Shaun Hamilton
c8700656be fix(tauri-cli): prevent accidental object permission rm (#11985) 2024-12-30 01:18:50 +02:00
renovate[bot]
cdd1ebf81f chore(deps) Update Tauri Codegen (#12049)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 11:55:57 +08:00
renovate[bot]
253b1872f1 chore(deps) Update Tauri API Definitions (#12050)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 11:40:36 +08:00
renovate[bot]
3ac76bec77 chore(deps) Update Tauri Build to v1.0.95 (#12047)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 11:37:27 +08:00
renovate[bot]
faeec8e965 chore(deps) Update Rust crate syn to v2.0.91 (#12046)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-27 11:36:28 +08:00
stringhandler
e349dfe572 fix: fix panic caused by removed_resource.is_none (fix #11955) (#12000) 2024-12-23 16:11:31 +02:00
Andrew Ferreira
fdaf48fc4a chore: fix crates.io link (#12018)
* chore: fix crates.io link

* chore: format file
2024-12-21 02:46:37 +02:00
renovate[bot]
7d38ee987e chore(deps) Update Tauri Codegen (dev) (#11999)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 16:22:59 +01:00
renovate[bot]
f1167143cd chore(deps) Update dependency @types/node to v22.10.2 (dev) (#11943)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 13:28:50 +01:00
renovate[bot]
2601811cef chore(deps) Update Tauri Build to v1.0.24 (dev) (#11998)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-19 13:22:40 +01:00
Amr Bashir
ca7f025fd8 fix(core): return an error when accessing unmanaged state in command (#11958)
closes #11949
2024-12-12 23:05:32 +02:00
Tony
17bcec8abe chore(deps): update all js dev dependencies (#11941)
- Update vite to v6
- Update svelte to v5
- Bump nanoid to fix audit
- Align api and cli to both use the same node types version
2024-12-11 12:55:38 +02:00
renovate[bot]
f5eacf3283 chore(deps) Update Tauri API Definitions (#11936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 13:45:49 +08:00
renovate[bot]
6bbf3649f9 chore(deps) Update Tauri Codegen (#11939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 13:45:08 +08:00
renovate[bot]
22b26a2e36 chore(deps) Update Tauri Build to v1.0.94 (#11938)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 13:43:10 +08:00
renovate[bot]
2a120bdcc0 chore(deps) Update Rust crate syn to v2.0.90 (#11935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-11 12:58:54 +08:00
简静凡
0ae06c5ca8 fix: the exclude path in file Cargo.toml of plugin template generated by cli (#11914) 2024-12-09 15:36:27 +01:00
Amr Bashir
afad8067d1 chore(deps): update tao to 0.31 (#11906) 2024-12-09 12:16:33 +02:00
John Carmack
b37741da6a fix(feature/specta): Resolve error when using latest version of specta with tauri specta feature (#11871)
* Update specta feature to use latest specta version; add specta-util dependency (required in specta v2 rc.20)

* Add .changes file

* Update crates/tauri/Cargo.toml

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2024-12-05 08:56:16 -03:00
Fabian-Lars
2b8a981050 refactor(cli): use cargo run for tauri dev (#11694)
* x

* todo

* lint

* lint

* rename fn

* lint

* lint

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-12-05 07:38:06 -03:00
Fabian-Lars
1f65fd2bb7 fix(cli): Statically compile msvc runtime (#11769)
* fix(cli): Statically compile msvc runtime

fixes https://github.com/tauri-apps/tauri/issues/11642
ref https://github.com/swc-project/swc/pull/7965

i only added it for x64 for now but we should monitor x32 (swc removed it for this one again) and aarch64 (swc never added it).
x32 is fairly rare as a dev system and aarch64 didn't seem much testing in general (as a dev system) so i'd prefer to wait and see if it's needed.

note that i don't know if any other tooling (rust etc) need the dyn runtime so that's also something to monitor

* 32bit and arm64
2024-12-04 15:32:09 -03:00
renovate[bot]
9f51bbf1de chore(deps) Update Tauri Codegen (#11866)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 14:51:30 +08:00
renovate[bot]
475597f97c chore(deps) Update Rust crate syn to v2.0.90 (#11865)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 14:49:44 +08:00
renovate[bot]
e61225bcc4 chore(deps) Update Rust crate anyhow to v1.0.94 (#11864)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 14:49:21 +08:00
renovate[bot]
01c74f4424 chore(deps) Update Tauri API Definitions (#11867)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-04 14:48:45 +08:00
Fabian-Lars
882b0aded1 chore: apply clippy suggestions. (#11849)
* chore: apply clippy suggestions.

* .

* .
2024-12-02 20:36:06 -03:00
Fabian-Lars
89e30ef20d fix(core): Remove os check on data_store_identifier (#11817)
* fix(core): Remove os check on `data_store_identifier`

* typo
2024-12-02 20:32:02 -03:00
Fabian-Lars
a692c8937f fix(bundler): create tauri tools dir first. make tools executable. (#11852)
* not sure what's going on yet

* .

* fix(bundler): try to create tauri tools dir

* Discard changes to .github/workflows/test-cli-js.yml

* fix
2024-12-02 20:29:42 -03:00
Fabian-Lars
8ba5e16384 ci: msrv compliant lockfile. test cli with 1.77.2 instead of stable. (#11851) 2024-12-02 20:28:45 -03:00
Fabian-Lars
b50a1ac0ef fix(core): Add background color permissions (#11850)
fixes #11848
2024-12-02 20:27:25 -03:00
Shi Yan
0e2f0b29cd docs: Add missing info regarding the drop position offset. (#11406)
* Add missing document regarding the drop position offset.

* Update webview.ts

---------

Co-authored-by: Fabian-Lars <github@fabianlars.de>
2024-12-02 20:40:56 +01:00
renovate[bot]
983634a41f chore(deps) Update dependency @types/node to v22.9.1 (#11750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 19:48:33 +01:00
renovate[bot]
2c3e000f73 chore(deps) Update Rust crate url to v2.5.4 (#11745)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 15:38:10 +01:00
Elias Sjödin
a6e84f7d2c fix(api.js): wrong command for isAbsolute (#11835) 2024-11-30 22:04:48 +01:00
Fabian-Lars
d6bed20a0e refactor(bundler): Move appimage bundler script logic into rust (#11758) 2024-11-28 04:52:24 +02:00
lars-berger
18bd639f6e feat(macos): Add with_data_store_identifier to WebviewBuilder (#11798) 2024-11-28 04:43:34 +02:00
Fabian-Lars
53f808674b feat(bundler): add option to disable CI for just the dmg bundler (#11799) 2024-11-28 04:41:19 +02:00
renovate[bot]
9f0d902f6b chore(deps) Update Tauri Build to v1.0.215 (#11744)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 13:18:33 +08:00
renovate[bot]
df27b4d94c chore(deps) Update Tauri Macros (#11803)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 13:10:41 +08:00
Fabian-Lars
5188c0fae2 feat(core): Add Scope::is_forbidden (#11767) 2024-11-26 14:31:48 +02:00
lars-berger
ba6f370147 fix(core): Initialize command cache when a capability is added (#11785) 2024-11-26 14:18:55 +02:00
Amr Bashir
f884bae75b feat: add tauri::Builder::on_tray_icon_event (#11742) 2024-11-21 15:06:57 +02:00
Amr Bashir
fc30b20bea feat(api/tray): add TrayIcon.setShowMenuOnLeftClick method (#11726) 2024-11-21 14:50:41 +02:00
Liigo Zhuang
7a9b920c3e docs: Remove header images from inline docs (#11749) 2024-11-21 14:48:49 +02:00
Amr Bashir
12a48d1e26 fix(api): transform icon when creating icon menu item and predefined about menu item with icon (#11741) 2024-11-21 14:22:51 +02:00
AHQ
020ea05561 feat: Implement Badging API (#11661) 2024-11-20 02:59:28 +02:00
Amr Bashir
a09e48e396 fix(core): manually simplify patterns for fs scope (#11730)
closes #11614

Remove UNC manually, instead of `dunce::simplified` because `path` could have `*` in it and that's not allowed on Windows and `dunce::simplified` will check that and return `path` as is without simplification resulting in a missing pattern in scope

for the scope pattern `\\?\C:\path\to\dir\**`, we expect the scope to have:
- `\\?\C:\path\to\dir\**`
- `C:\path\to\dir\**`

but if we use `dunce::simplified`, it will see `**` as invalid path component on Windows and will not simplify the path resulting in a scope that only has `\\?\C:\path\to\dir\**`
2024-11-20 00:50:10 +02:00
renovate[bot]
b37c208d61 chore(deps) Update Tauri API Definitions (#11668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-19 13:13:44 +08:00
Fabian-Lars
72feaf99fc refactor(bundler): remove unused fs utils, add http utils (#11716) 2024-11-19 01:23:20 +02:00
Sander Cox
d86aaccb0b enhance: allow show_menu_on_left_click on Windows (#11729) 2024-11-19 00:25:20 +02:00
Kévin TURMEL
93a3a043d3 fix(windows): add Portuguese language in nsis bundle (fix #11725) (#11727) 2024-11-19 00:11:16 +02:00
Fabian-Lars
2bc46b061c docs(examples): fix file-assoc readme cargo build command (#11711)
closes #11703
2024-11-18 19:50:41 +02:00
Fabian-Lars
b63262cd4d fix(api.js): Change cb type in mockIPC to return unknown (#11724) 2024-11-18 17:50:39 +02:00
Amr Bashir
f2814ed538 fix(cli/info): use XDG_SESSION_DESKTOP instead of DESKTOP_SESSION (#11663) 2024-11-14 18:02:22 +02:00
renovate[bot]
ff39ad93b7 chore(deps) Update Tauri Codegen (#11667)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-13 13:36:14 +08:00
renovate[bot]
9cb6cad284 chore(deps) Update Rust crate serde to v1.0.215 (#11666)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-13 13:21:07 +08:00
jLynx
cccb308c7b feat(bundler): create signature for .deb bundle (#11562) 2024-11-13 03:12:06 +02:00
SpikeHD
dc4d794776 feat(windows, linux): add WebviewWindowBuilder/WebviewBuilder::extensions_path (#11628) 2024-11-13 01:20:06 +02:00
Tony
46935212b6 fix: parse json5 capability files when config-json5 is enabled (#11658) 2024-11-12 16:44:37 +02:00
Amr Bashir
74212d40d8 feat(cli): include linux DE and session type in tauri info (#11653) 2024-11-12 15:59:47 +02:00
Del
c3b1fced38 docs: correct payload property for DragDropEvent.over example (#11648) 2024-11-11 17:14:36 +01:00
github-actions[bot]
ef2592b5a8 Apply Version Updates From Current Changes (#11646)
Co-authored-by: amrbashir <amrbashir@users.noreply.github.com>
2024-11-11 16:25:14 +02:00
amrbashir
7f81f05236 chore: rename change file 2024-11-11 15:45:22 +02:00
Amr Bashir
e8a50f6d76 fix(core): hard code BaseDirectory integer values to avoid regressions when reordering the variants (#11645)
closes #11633
2024-11-11 14:58:16 +02:00
Daniel Gerhardt
5e94354875 fix(api/dpi): fix toLogical and toPhysical for positions (#11639) 2024-11-11 14:21:25 +02:00
Kornel
0fcef3f941 docs: document vanilla JS import alternative (#11632) 2024-11-11 14:21:08 +02:00
github-actions[bot]
86f22f0ec9 apply version updates (#11440)
Co-authored-by: amrbashir <amrbashir@users.noreply.github.com>
2024-11-09 15:47:39 +02:00
Amr Bashir
3f6f07a1b8 chore(deps): update wry to 0.47 and tao to 0.30.6 (#11627) 2024-11-09 04:14:22 +02:00
Lucas Fernandes Nogueira
60e86d5f6e fix(cli): android dev not working on Windows without --host (#11624)
ref https://discord.com/channels/616186924390023171/1291159454397628477
2024-11-09 02:57:36 +02:00
renovate[bot]
b28435860c chore(deps) Update Rust crate thiserror to v2 (dev) (#11604)
* chore(deps) Update Rust crate thiserror to v2

* thiserror v2 on all crates

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-11-08 08:21:38 -03:00
Amr Bashir
229d7f8e22 fix(core): fix child webviews on macOS and Windows treated as full webview window (#11616)
* fix(core): fix child webviews on macOS and Windows treated as full webview window

closes #11452

* Update .changes/child-windows-macos.md
2024-11-08 08:04:14 -03:00
Ville Säävuori
c561786844 docs: fix typos in drag&drop event.payload (#11620)
* fix: typo in drag&drop event.payload

'hover' -> 'over'

* fix: another typo

'hover' -> 'over'
2024-11-08 09:50:10 +08:00
Shaun Hamilton
6bf917941f feat(cli): add tauri remove command (#11322) 2024-11-07 19:58:25 +02:00
Tony
8e8312bb82 ci: unpin ravif (#11608) 2024-11-07 09:09:05 +01:00
renovate[bot]
f550a3f471 chore(deps) Update Tauri Bundler (dev) (#11601)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 13:25:49 -03:00
Amr Bashir
4d545ab3ca feat: background color APIs (#11486)
* feat: background color APIs

closes #10519
closes #1564

* clippy

* git branch

* bundle

* fix hex color schema pattern

* add missing `^`

* fix iOS

* revert test

* revert apple-codesign bump

* fmt

* add change files

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-11-06 13:21:47 -03:00
39zde
fabc2f283e feat: add HeaderConfig to SecurityConfig (#11485) 2024-11-06 16:16:46 +02:00
renovate[bot]
6b3c82aa90 chore(deps) Update Tauri macOSSign (dev) (#11541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 10:41:34 -03:00
griffi-gh
3781429147 enhance: always check parent directory (fix #8679) (#11429)
* enhance: always check parent directory (fix #8679)

* Update crates/tauri-utils/src/platform.rs

* add change file

* fix crate

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2024-11-06 10:41:13 -03:00
renovate[bot]
15d6515eb1 chore(deps) Update dependency @types/node to v22 (dev) (#11545)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 09:54:52 -03:00
renovate[bot]
b63353bd61 chore(deps) Update Tauri API Definitions (dev) (#11597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 07:49:59 -03:00
renovate[bot]
e8c0c57909 chore(deps) Update Tauri Codegen (dev) (#11596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 07:49:38 -03:00
renovate[bot]
58392a5221 chore(deps) Update Rust crate handlebars to v6.2.0 (dev) (#11544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 07:49:18 -03:00
renovate[bot]
8061fb2da8 chore(deps) Update Rust crate anyhow to v1.0.93 (#11598)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 16:04:51 +08:00
renovate[bot]
e835751d4f chore(deps) Update Rust crate syn to v2.0.87 (#11595)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 11:40:30 +08:00
renovate[bot]
0a170d0716 chore(deps) Update Rust crate anyhow to v1.0.92 (#11594)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 11:39:56 +08:00
renovate[bot]
03828587b5 chore(deps) Update Tauri API Definitions (#11543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-06 11:31:40 +08:00
Amr Bashir
5c4b830843 feat(api): add SERIALIZE_TO_IPC_FN const and implement it for dpi types, add more constructors (#11191) 2024-11-05 23:30:17 +02:00
Amr Bashir
cbc095ec5f feat: add WebviewWindow/Webview::devtools (#11451)
* feat: add `WebviewWindow/Webview::devtools`

closes #10849

* clippy

* fix ToTokens

* document default behavior

* move builder usage

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-11-05 16:00:48 -03:00
Amr Bashir
2a75c64b54 feat(core): add window_class name API on Windows (#11469)
* On Windows, set name of Window Class, closes #7498
allow to customize it instead of current value hard coded "Window Class"

* feat(windows): add window_classname, closes #7498
allow to customize the window class name instead of current value hard coded "Window Class"

    * feat: add window_classname, closes #7498

    * add changes file

    * Update core/tauri-config-schema/schema.json

    * Update tooling/cli/schema.json

* missing pieces after merge

* clippy

---------

Co-authored-by: Géraud-Loup <47665233+geraudloup@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-11-05 15:18:47 -03:00
Amr Bashir
1b6b2cfaa1 feat(cli): process bundle > windows > wix > fragmentPaths with Handlebars (#11521)
* feat(cli): process `bundle > windows > wix > fragmentPaths` with Handlebars

closes #11520

* remove unneeded register_template_string

* Update crates/tauri-bundler/src/bundle/windows/msi/mod.rs

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2024-11-05 14:46:54 -03:00
Amr Bashir
17c6952aec enhance(core): use diagnostic::on_unimplemented on rustc 1.78 and newer for async commands with references (#11522)
* enhance(core): use `diagnostic::on_unimplemented` on rustc 1.78 and newer for async commands with references

* change file

* clippy

* clippy

* add TODO
2024-11-05 14:20:30 -03:00
Amr Bashir
7af01ff2ce fix(cli): fix tauri migrate failing to install NPM deps when running from Deno (#11523)
* fix(cli): fix `tauri migrate` failing to install NPM deps when running from Deno

* clippy
2024-11-05 14:16:32 -03:00
Amr Bashir
100a4455aa fix(cli): fix yarn berry detection (#11529)
closes #11495
2024-11-05 14:15:48 -03:00
Amr Bashir
4191a7a53d fix(tray): build tray on main thread (#11583) 2024-11-05 17:42:08 +02:00
Amr Bashir
f37e97d410 feat: add use_https_scheme for Windows and Android (#11477)
* feat: add `use_https_scheme` for Windows and Android

closes #11252

* fix compilation

* Apply suggestions from code review

Co-authored-by: Fabian-Lars <github@fabianlars.de>

* change wording

* add migrations

* migrate `dangerousUseHttpScheme`

* fmt

* infer AssetResolver::get https scheme config

* fix tests

---------

Co-authored-by: Fabian-Lars <github@fabianlars.de>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-11-05 09:48:59 -03:00
Amr Bashir
058c0db72f feat(bundler): add option to configure RPM compression (#11584) 2024-11-05 14:08:08 +02:00
Mikkel Tønder
f8994b214e fix(event): handle AnyLabel listeners in emit_to (#11581)
closes #11561
2024-11-04 14:31:18 +02:00
Kornel
c33bbf4574 enhance: include the path in ACL I/O errors (#11575) 2024-11-04 14:30:43 +02:00
Amr Bashir
129414faa4 fix: fix webview not focused by default (#11569)
* fix: fix webview not focused by default

closes #10746

* fix compile

* typo

* fix compile again

* clippy
2024-11-04 13:58:47 +02:00
renovate[bot]
12ffc19ce0 chore(deps) Update Rust crate regex to v1.11.1 (#11542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-30 12:19:31 +08:00
renovate[bot]
9f472591cc chore(deps) Update Rust crate serde to v1.0.214 (#11540)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-30 12:00:51 +08:00
Andrii Bodnar
d25dd9e31d docs: fix capability configuration examples (#11537) 2024-10-30 00:33:42 +03:00
Lucas Fernandes Nogueira
c43d5df158 feat(cli): associate newly created capability with the main window (#11512)
without this change the capability is not applied to any context and it might be hard for users to figure out why
2024-10-28 14:05:24 +03:00
Alex Adewole
1065f632f2 feat(cli): remove UNC path prefix in TAURI_APP_PATH and TAURI_FRONTEND_PATH (#11514) 2024-10-28 12:21:42 +03:00
Vincent Esche
ac22950f39 fix(cli): Fix mixup of TAURI_APP_PATH and TAURI_FRONTEND_PATH (#11492)
* Fix mixup of `env_tauri_app_path()` and `env_tauri_frontend_path()` in tauri's path resolutions

* Rename functions in `app_paths` to match their corresponding, publicly exposed env var keys

* Rename `app_dir`/`app_path` variables that deal with the frontend app's directory to `frontend_dir

* Rename `APP_DIR` to `FRONTEND_DIR`

* Improve comment on meaning of tauri path env vars
2024-10-27 17:55:23 -03:00
阿良仔
a8105eccb2 docs: fix wrongly rendered links on docs.rs (#11483) 2024-10-24 16:02:49 +03:00
renovate[bot]
888277ec40 chore(deps) Update Rust crate syn to v2.0.85 (#11472)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 13:23:24 +08:00
renovate[bot]
0014f9ae69 chore(deps) Update dependency @types/node to v20.17.0 (#11470)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 13:15:04 +08:00
Amr Bashir
8036c78e08 feat(core/path): add PathResolver::home_dir on Android (#11455)
ref: https://github.com/tauri-apps/tauri/issues/10478#issuecomment-2383754176
2024-10-24 06:43:02 +03:00
renovate[bot]
516c7d9f63 chore(deps) Update Rust crate syn to v2.0.83 (#11473)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 10:39:59 +08:00
Amr Bashir
54cbf59b5a fix(api/menu): fix submenus when created using an object in items field in the object passed to Menu/Submenu.new (#11441)
* fix(api/menu): fix submenus when created using an object in `items` field in the object passed to `Menu/Submenu.new`

closes #11435

also closes #11422 as I included the docs in this PR

* Update .changes/js-submenu-in-options.md

* Update packages/api/src/menu/base.ts

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2024-10-23 10:47:32 -03:00
renovate[bot]
ce864cebfd chore(deps) Update Rust crate image to v0.25.4 (dev) (#11457)
* chore(deps) Update Rust crate image to v0.25.4

* Also bump json-patch and resvg

* Just json-patch for now

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Tony <legendmastertony@gmail.com>
2024-10-23 17:46:59 +08:00
renovate[bot]
ed78f52cd1 chore(deps) Update Rust crate json-patch to v3 (#11466)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 17:25:16 +08:00
renovate[bot]
ccf20eb3ff chore(deps) Update Rust crate json-patch to v3 (#11467)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 17:25:08 +08:00
renovate[bot]
d0c1189b91 chore(deps) Update Tauri API Definitions (#11464)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 17:04:56 +08:00
renovate[bot]
0d31fe99c7 chore(deps) Update Rust crate thiserror to v1.0.65 (#11463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 16:57:41 +08:00
renovate[bot]
d4b3659523 chore(deps) Update Rust crate proc-macro2 to v1.0.89 (#11459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 16:25:22 +08:00
renovate[bot]
8e083c99c8 chore(deps) Update Tauri Build to v1.0.91 (#11456)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-23 12:25:08 +08:00
Amr Bashir
8c6d1e8e6c fix(runtime-wry): run cursor_position getter on main thread (#11401)
* fix(runtime-wry): run `cursor_position` getter on main thread

closes #10340

* clippy

* clippy

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-10-22 09:59:21 -03:00
Amr Bashir
6dea12a067 feat(bundler): add recommends for deb and rpm (#11402)
closes #10351
2024-10-21 15:38:14 -03:00
Amr Bashir
1f311832ab enhance(cli): add context to public/secret key decoding errors (#11405)
* enhance(cli): add context to public/secret key decoding errors

closes #10488

* Update .changes/cli-updater-errorr.md

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
2024-10-21 15:37:28 -03:00
Czxck001
e0d1307d3f feat(cli) Make tauri migrate update $schema in tauri.conf.json (#11414)
* Make `tauri migrate` update $schema in tauri.conf.json

* add change file

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
2024-10-21 15:24:59 -03:00
Lucas Fernandes Nogueira
f0da0bde87 feat(core): add WebviewWindow::resolve_command_scope (#11439)
* feat(core): add WebviewWindow::resolve_command_scope

This new functionality exposes the `CommandScope` resolution as a function (currently only commands can resolve them as a dependency injection via CommandItem)

This function is useful to validate the configuration at runtime (do some asserts at setup phase to ensure capabilities are properly configured) and to resolve scopes in a separate thread or context

* adjust return type
2024-10-21 15:16:08 -03:00
Lucas Nogueira
a5bf48eab0 fix: CLI version on metadata file 2024-10-21 09:24:34 -03:00
Lucas Nogueira
7a1a3276c4 chore: bump tauri-utils dependency versions 2024-10-21 07:30:06 -03:00
Lucas Nogueira
8ca0e4dd2c chore(ci): bump tauri-utils with WiX version change 2024-10-20 11:13:40 -03:00
267 changed files with 11204 additions and 7925 deletions

View File

@@ -34,7 +34,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'

View File

@@ -44,7 +44,9 @@ jobs:
if: needs.changes.outputs.api == 'true'
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'

View File

@@ -23,7 +23,9 @@ jobs:
with:
fetch-depth: 0
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- uses: actions/setup-node@v4
with:
node-version: 20
@@ -73,7 +75,9 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- uses: actions/setup-node@v4
with:
node-version: 20
@@ -111,7 +115,7 @@ jobs:
- name: Create Pull Request With Versions Bumped
if: steps.covector.outputs.commandRan == 'version'
uses: tauri-apps/create-pull-request@v3
uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # 7.0.6
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: release/version-updates
@@ -124,7 +128,7 @@ jobs:
if: |
steps.covector.outputs.successfulPublish == 'true' &&
steps.covector.outputs.packagesPublished != ''
uses: peter-evans/repository-dispatch@v1
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # 3.0.0
with:
token: ${{ secrets.ORG_TAURI_BOT_PAT }}
repository: tauri-apps/tauri-docs

View File

@@ -66,7 +66,9 @@ jobs:
with:
targets: ${{ matrix.target.name }}
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: Setup node
uses: actions/setup-node@v4
with:

View File

@@ -26,7 +26,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'

View File

@@ -19,7 +19,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
@@ -31,7 +33,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'

View File

@@ -43,7 +43,7 @@ jobs:
- host: windows-latest
architecture: x64
target: aarch64-pc-windows-msvc
build: pnpm build --target aarch64-pc-windows-msvc --features native-tls-vendored --cargo-flags="--no-default-features"
build: pnpm build --target aarch64-pc-windows-msvc
- host: ubuntu-20.04
target: x86_64-unknown-linux-gnu
docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
@@ -92,7 +92,9 @@ jobs:
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: Setup node
uses: actions/setup-node@v4
if: ${{ !matrix.settings.docker }}
@@ -108,7 +110,6 @@ jobs:
- uses: Swatinem/rust-cache@v1
with:
key: ${{ matrix.settings.target }}
working-directory: 'crates/tauri-cli/'
if: ${{ matrix.settings.docker }}
- name: Setup toolchain
run: ${{ matrix.settings.setup }}
@@ -203,7 +204,9 @@ jobs:
runs-on: ${{ matrix.settings.host }}
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: Setup node
uses: actions/setup-node@v4
with:
@@ -234,7 +237,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: Setup node
uses: actions/setup-node@v4
with:
@@ -271,7 +276,9 @@ jobs:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: Setup node
uses: actions/setup-node@v4
with:
@@ -362,7 +369,9 @@ jobs:
id-token: write # npm provenance
steps:
- uses: actions/checkout@v4
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: Setup node
uses: actions/setup-node@v4
with:

View File

@@ -37,7 +37,7 @@ jobs:
- os: windows-latest
rust_target: aarch64-pc-windows-msvc
ext: '.exe'
args: '--no-default-features --features native-tls-vendored'
args: ''
steps:
- uses: actions/checkout@v4

View File

@@ -33,8 +33,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: install Rust 1.77.2
uses: dtolnay/rust-toolchain@1.77.2
- name: install Linux dependencies
if: matrix.platform == 'ubuntu-latest'
@@ -42,7 +42,9 @@ jobs:
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.1
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: setup node
uses: actions/setup-node@v4
with:

View File

@@ -11,6 +11,7 @@ on:
pull_request:
paths:
- '.github/workflows/test-cli-js.yml'
- 'packages/cli/**'
# currently` @tauri-apps/cli` only tests the template
- 'crates/tauri-cli/templates/app/**'
@@ -37,7 +38,9 @@ jobs:
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
- run: corepack enable
- run: |
npm i -g --force corepack
corepack enable
- name: setup node
uses: actions/setup-node@v4
with:
@@ -48,7 +51,7 @@ jobs:
if: matrix.platform == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev
sudo apt-get install -y webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev
- uses: Swatinem/rust-cache@v2

View File

@@ -44,7 +44,7 @@ jobs:
- uses: actions/checkout@v4
- name: 'Setup Rust'
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@1.77.2
with:
targets: ${{ matrix.platform.target }}
@@ -52,7 +52,7 @@ jobs:
if: matrix.platform.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.1 libayatana-appindicator3-dev
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.1 libayatana-appindicator3-dev librsvg2-dev
- uses: Swatinem/rust-cache@v2

View File

@@ -88,16 +88,6 @@ jobs:
sudo apt-get update
sudo apt-get install -y webkit2gtk-4.1 libxdo-dev libayatana-appindicator3-dev
- name: downgrade crates with MSRV conflict
run: |
cargo update -p ravif --precise 0.11.5
cargo update -p aws-config --precise 1.5.5
cargo update -p aws-sdk-ssooidc --precise 1.40.0
cargo update -p aws-sdk-s3 --precise 1.46.0
cargo update -p aws-sdk-sts --precise 1.39.0
cargo update -p aws-sdk-sso --precise 1.39.0
cargo update -p bitstream-io --precise 2.3.0
- uses: Swatinem/rust-cache@v2
with:
prefix-key: v2

View File

@@ -1,5 +1,6 @@
{
"singleQuote": true,
"semi": false,
"trailingComma": "none"
"trailingComma": "none",
"experimentalOperatorPosition": "start"
}

View File

@@ -28,8 +28,8 @@ const ignore = [
async function checkFile(file) {
if (
extensions.some((e) => file.endsWith(e)) &&
!ignore.some((i) => file.includes(`/${i}/`) || path.basename(file) == i)
extensions.some((e) => file.endsWith(e))
&& !ignore.some((i) => file.includes(`/${i}/`) || path.basename(file) == i)
) {
const fileStream = fs.createReadStream(file)
const rl = readline.createInterface({
@@ -42,11 +42,11 @@ async function checkFile(file) {
for await (let line of rl) {
// ignore empty lines, allow shebang and bundler license
if (
line.length === 0 ||
line.startsWith('#!') ||
line.startsWith('// swift-tools-version:') ||
line === bundlerLicense ||
line === denoLicense
line.length === 0
|| line.startsWith('#!')
|| line.startsWith('// swift-tools-version:')
|| line === bundlerLicense
|| line === denoLicense
) {
continue
}

3795
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ PackageSupplier: Organization: The Tauri Programme in the Commons Conservancy
PackageHomePage: https://tauri.app
PackageLicenseDeclared: Apache-2.0
PackageLicenseDeclared: MIT
PackageCopyrightText: 2019-2024, The Tauri Programme in the Commons Conservancy
PackageCopyrightText: 2019-2025, The Tauri Programme in the Commons Conservancy
PackageSummary: <text>Tauri is a rust project that enables developers to make secure
and small desktop applications using a web frontend.
</text>

View File

@@ -35,7 +35,7 @@ The list of Tauri's features includes, but is not limited to:
- Built-in self updater (desktop only)
- System tray icons
- Native notifications
- [Localhost free (🔥)](https://github.com/tauri-apps/tauri/issues/10510)
- Native WebView Protocol (tauri doesn't create a localhost http(s) server to serve the WebView contents)
- GitHub action for streamlined CI
- VS Code extension

View File

@@ -9,11 +9,11 @@ description = "Cross-platform WebView rendering library"
repository = "https://github.com/tauri-apps/wry"
[dependencies]
anyhow = "1.0.40"
anyhow = "1"
time = { version = "0.3", features = ["formatting"] }
tempfile = "3.2.0"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tempfile = "3"
serde_json = "1"
serde = { version = "1", features = ["derive"] }
[[bin]]
name = "run_benchmark"

View File

@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
//!
//! This Rust binary runs on CI and provides internal metrics results of Tauri. To learn more see [benchmark_results](https://github.com/tauri-apps/benchmark_results) repository.
//!
//! ***_Internal use only_**

View File

@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
//!
//! This Rust binary runs on CI and provides internal metrics results of Tauri. To learn more see [benchmark_results](https://github.com/tauri-apps/benchmark_results) repository.
//!
//! ***_Internal use only_**

View File

@@ -1,6 +1,7 @@
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica,
Arial, sans-serif;
font-family:
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
sans-serif;
margin: auto;
max-width: 38rem;
padding: 2rem;

View File

@@ -11,6 +11,6 @@ tauri-build = { path = "../../../../crates/tauri-build", features = [
] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
serde = { version = "1", features = ["derive"] }
tauri = { path = "../../../../crates/tauri", features = [] }

View File

@@ -11,6 +11,6 @@ tauri-build = { path = "../../../../crates/tauri-build", features = [
] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
serde = { version = "1", features = ["derive"] }
tauri = { path = "../../../../crates/tauri", features = [] }

View File

@@ -18,7 +18,7 @@ async fn read_file<R: Runtime>(app: AppHandle<R>) -> Result<Response, String> {
.path()
.resolve(".tauri_3mb.json", BaseDirectory::Home)
.map_err(|e| e.to_string())?;
let contents = read(&path).map_err(|e| e.to_string())?;
let contents = read(path).map_err(|e| e.to_string())?;
Ok(Response::new(contents))
}

View File

@@ -11,6 +11,6 @@ tauri-build = { path = "../../../../crates/tauri-build", features = [
] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
serde = { version = "1", features = ["derive"] }
tauri = { path = "../../../../crates/tauri", features = [] }

View File

@@ -1,5 +1,39 @@
# Changelog
## \[2.0.6]
### Dependencies
- Upgraded to `tauri-utils@2.2.0`
- Upgraded to `tauri-codegen@2.0.5`
## \[2.0.5]
### Bug Fixes
- [`848d0e060`](https://www.github.com/tauri-apps/tauri/commit/848d0e060e6eb3c8e9e8175adc7896587b5a947d) ([#12270](https://www.github.com/tauri-apps/tauri/pull/12270) by [@aurelj](https://www.github.com/tauri-apps/tauri/../../aurelj)) Update `cargo_toml` to `0.21.0`. This adds compatibility with Rust's 2024 Edition.
- [`cd1d026f9`](https://www.github.com/tauri-apps/tauri/commit/cd1d026f9799c26b04acb64f49e7ee0a8b193049) ([#11961](https://www.github.com/tauri-apps/tauri/pull/11961) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix tauri fails to build if the project path contains glob characters
## \[2.0.4]
### Dependencies
- Upgraded to `tauri-utils@2.1.1`
- Upgraded to `tauri-codegen@2.0.4`
## \[2.0.3]
### Dependencies
- Upgraded to `tauri-utils@2.1.0`
- Upgraded to `tauri-codegen@2.0.3`
## \[2.0.2]
### Dependencies
- Upgraded to `tauri-utils@2.0.2`
## \[2.0.1]
### What's Changed

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-build"
version = "2.0.1"
version = "2.0.6"
description = "build time code to pair with https://crates.io/crates/tauri"
exclude = ["CHANGELOG.md", "/target"]
readme = "README.md"
@@ -28,22 +28,23 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
anyhow = "1"
quote = { version = "1", optional = true }
tauri-codegen = { version = "2.0.1", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.0.1", path = "../tauri-utils", features = [
tauri-codegen = { version = "2.0.5", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "2.2.0", path = "../tauri-utils", features = [
"build",
"resources",
] }
cargo_toml = "0.17"
cargo_toml = "0.21"
serde = "1"
serde_json = "1"
heck = "0.5"
json-patch = "3.0"
json-patch = "3"
walkdir = "2"
tauri-winres = "0.1"
tauri-winres = "0.3"
semver = "1"
dirs = "5"
dirs = "6"
glob = "0.3"
toml = "0.8"
# Our code requires at least 0.8.18 so don't simplify this to 0.8
schemars = { version = "0.8.18", features = ["preserve_order"] }
[features]

View File

@@ -199,7 +199,9 @@ permissions = [{default_permissions}]
}
tauri_utils::acl::build::define_permissions(
&plugin_out_dir.join("*").to_string_lossy(),
&PathBuf::from(glob::Pattern::escape(&plugin_out_dir.to_string_lossy()))
.join("*")
.to_string_lossy(),
name,
&plugin_out_dir,
|_| true,
@@ -222,10 +224,12 @@ permissions = [{default_permissions}]
);
}
permission_files.extend(tauri_utils::acl::build::define_permissions(
&default_permissions_path
.join("**")
.join("*")
.to_string_lossy(),
&PathBuf::from(glob::Pattern::escape(
&default_permissions_path.to_string_lossy(),
))
.join("**")
.join("*")
.to_string_lossy(),
name,
&plugin_out_dir,
|_| true,

View File

@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
//!
//! This applies the macros at build-time in order to rig some special features needed by `cargo`.
#![doc(
@@ -70,7 +68,7 @@ fn copy_binaries(
.to_string_lossy()
.replace(&format!("-{target_triple}"), "");
if package_name.map_or(false, |n| n == &file_name) {
if package_name == Some(&file_name) {
return Err(anyhow::anyhow!(
"Cannot define a sidecar with the same name as the Cargo package name `{}`. Please change the sidecar name in the filesystem and the Tauri configuration.",
file_name
@@ -508,19 +506,8 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
cfg_alias("dev", is_dev());
let ws_path = get_workspace_dir()?;
let mut manifest =
Manifest::<cargo_toml::Value>::from_slice_with_metadata(&fs::read("Cargo.toml")?)?;
if let Ok(ws_manifest) = Manifest::from_path(ws_path.join("Cargo.toml")) {
Manifest::complete_from_path_and_workspace(
&mut manifest,
Path::new("Cargo.toml"),
Some((&ws_manifest, ws_path.as_path())),
)?;
} else {
Manifest::complete_from_path(&mut manifest, Path::new("Cargo.toml"))?;
}
let cargo_toml_path = Path::new("Cargo.toml").canonicalize()?;
let mut manifest = Manifest::<cargo_toml::Value>::from_path_with_metadata(cargo_toml_path)?;
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
@@ -626,7 +613,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
if let Some(version_str) = &config.version {
if let Ok(v) = Version::parse(version_str) {
let version = v.major << 48 | v.minor << 32 | v.patch << 16;
let version = (v.major << 48) | (v.minor << 32) | (v.patch << 16);
res.set_version_info(VersionInfo::FILEVERSION, version);
res.set_version_info(VersionInfo::PRODUCTVERSION, version);
}
@@ -688,7 +675,7 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
}
}
"msvc" => {
if env::var("STATIC_VCRUNTIME").map_or(false, |v| v == "true") {
if env::var("STATIC_VCRUNTIME").is_ok_and(|v| v == "true") {
static_vcruntime::build();
}
}
@@ -703,23 +690,3 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
Ok(())
}
#[derive(serde::Deserialize)]
struct CargoMetadata {
workspace_root: PathBuf,
}
fn get_workspace_dir() -> Result<PathBuf> {
let output = std::process::Command::new("cargo")
.args(["metadata", "--no-deps", "--format-version", "1"])
.output()?;
if !output.status.success() {
return Err(anyhow::anyhow!(
"cargo metadata command exited with a non zero exit code: {}",
String::from_utf8(output.stderr)?
));
}
Ok(serde_json::from_slice::<CargoMetadata>(&output.stdout)?.workspace_root)
}

View File

@@ -1,5 +1,70 @@
# Changelog
## \[2.2.4]
### Enhancements
- [`5eba0785c`](https://www.github.com/tauri-apps/tauri/commit/5eba0785c461a0d0bec47653eaf6ccdf5f05d347) ([#12605](https://www.github.com/tauri-apps/tauri/pull/12605) by [@niusia-ua](https://www.github.com/tauri-apps/tauri/../../niusia-ua)) Added Ukrainian translation for the custom tauri messages in the nsis bundle
### Dependencies
- Upgraded to `tauri-utils@2.2.0`
- Upgraded to `tauri-macos-sign@2.1.0`
## \[2.2.3]
### Bug Fixes
- [`de8600b4d`](https://www.github.com/tauri-apps/tauri/commit/de8600b4d9a04e809e078c8aea61825d1328201f) ([#12471](https://www.github.com/tauri-apps/tauri/pull/12471) by [@anatawa12](https://www.github.com/tauri-apps/tauri/../../anatawa12)) Bumped `nsis-tauri-utils` to `0.4.2` which fixes the following bugs:
- Fixed launch on start checkbox in nsis installer does not work well with applications that require elevated permissions
- Fixed nsis installer may fail to install if launched by updater plugin
- [`fbe7c9ead`](https://www.github.com/tauri-apps/tauri/commit/fbe7c9ead76e71ca258c6f48bbb62185fcc37b1c) ([#12466](https://www.github.com/tauri-apps/tauri/pull/12466) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the compiled AppImage to miss webkitgtk's internal `libwebkit2gtkinjectedbundle.so` file.
- [`f5a59b93b`](https://www.github.com/tauri-apps/tauri/commit/f5a59b93bfefb43ff131a7870b3c5d5e48c1ca1e) ([#12136](https://www.github.com/tauri-apps/tauri/pull/12136) by [@unknovvn](https://www.github.com/tauri-apps/tauri/../../unknovvn)) The NSIS bundler will now replace non-numeric build metadata with `0` instead of returning an error.
- [`9dac2863a`](https://www.github.com/tauri-apps/tauri/commit/9dac2863afa70fb0bcddf859b284afba917f28ae) ([#12323](https://www.github.com/tauri-apps/tauri/pull/12323) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Skip signing the .dmg if self signing via `"signingIdentity": "-"` is used.
- [`b8eb28877`](https://www.github.com/tauri-apps/tauri/commit/b8eb28877fe822dbe17999fc8af98ed7d0983679) ([#12427](https://www.github.com/tauri-apps/tauri/pull/12427) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Clean up `Software\${MANUFACTURER}\${PRODUCTNAME}` registry key in the NSIS uninstaller if "Delete application data" option is checked when uninstalling.
## \[2.2.2]
### Bug Fixes
- [`72748cc45`](https://www.github.com/tauri-apps/tauri/commit/72748cc45cf670dd03c86c8deceb5942598f5ad9) ([#12365](https://www.github.com/tauri-apps/tauri/pull/12365) by [@don41382](https://www.github.com/tauri-apps/tauri/../../don41382)) Fixed an issue that caused the `.msi` installer not to lookup the `INSTALLDIR` set in the `nsis` installer.
- [`cf771bf69`](https://www.github.com/tauri-apps/tauri/commit/cf771bf69aa26b62d11a54a69131c631505d8c55) ([#12402](https://www.github.com/tauri-apps/tauri/pull/12402) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the .msi installer to not contain root resources when there were .dll files present in the target directory.
- [`07ccdc499`](https://www.github.com/tauri-apps/tauri/commit/07ccdc499c3240e7240be3abf95ef2d7d00b2dc7) ([#12324](https://www.github.com/tauri-apps/tauri/pull/12324) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue leading to NSIS based installers to not contain the `WebView2Loader.dll` file when targetting `windows-gnu`.
## \[2.2.1]
### Bug Fixes
- [`cd1d026f9`](https://www.github.com/tauri-apps/tauri/commit/cd1d026f9799c26b04acb64f49e7ee0a8b193049) ([#11961](https://www.github.com/tauri-apps/tauri/pull/11961) by [@Legend-Master](https://www.github.com/tauri-apps/tauri/../../Legend-Master)) Fix tauri fails to build if the project path contains glob characters
## \[2.2.0]
### New Features
- [`cccb308c7`](https://www.github.com/tauri-apps/tauri/commit/cccb308c7b559b0838138d6cea280665f060c925) ([#11562](https://www.github.com/tauri-apps/tauri/pull/11562) by [@jLynx](https://www.github.com/tauri-apps/tauri/../../jLynx)) Generate signature for `.deb` packages when `createUpdaterArtifacts` option is enabled.
### Enhancements
- [`93a3a043d`](https://www.github.com/tauri-apps/tauri/commit/93a3a043d39cc96515d51d98beeb14261d3a246b) ([#11727](https://www.github.com/tauri-apps/tauri/pull/11727) by [@Kiyozz](https://www.github.com/tauri-apps/tauri/../../Kiyozz)) Add support for `Portuguese` language for NSIS windows installer.
- [`53f808674`](https://www.github.com/tauri-apps/tauri/commit/53f808674b2c0012bc44a41ced90e742afbb41e8) ([#11799](https://www.github.com/tauri-apps/tauri/pull/11799) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) The bundler now reads the `TAURI_BUNDLER_DMG_IGNORE_CI` env var to decide whether to check for `CI: true` when building DMG files.
### Dependencies
- Upgraded to `tauri-utils@2.1.1`
## \[2.1.0]
### New Features
- [`1b6b2cfaa`](https://www.github.com/tauri-apps/tauri/commit/1b6b2cfaa14ab1d418c676cedbf942a812377a30) ([#11521](https://www.github.com/tauri-apps/tauri/pull/11521) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Process `bundle > windows > wix > fragmentPaths` with Handlebars to interpolate expressions within it.
- [`6dea12a06`](https://www.github.com/tauri-apps/tauri/commit/6dea12a0677a905cb1f14969fe05c53e7cd717c6) ([#11402](https://www.github.com/tauri-apps/tauri/pull/11402) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `bundle > linux > deb > recommends` and `bundle > linux > rpm > recommends` fields to declare a strong, but not absolute, dependency for your `.deb` and `.rpm` packages.
- [`058c0db72`](https://www.github.com/tauri-apps/tauri/commit/058c0db72f43fbe1574d0db654560e693755cd7e) ([#11584](https://www.github.com/tauri-apps/tauri/pull/11584) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `bundle > linux > rpm > compression` config option to control RPM bundle compression type and level.
### Dependencies
- Upgraded to `tauri-utils@2.1.0`
## \[2.0.4]
### New Features

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-bundler"
version = "2.0.4"
version = "2.2.4"
authors = [
"George Burton <burtonageo@gmail.com>",
"Tauri Programme within The Commons Conservancy",
@@ -15,32 +15,30 @@ rust-version = "1.77.2"
exclude = ["CHANGELOG.md", "/target", "rustfmt.toml"]
[dependencies]
tauri-utils = { version = "2.0.1", path = "../tauri-utils", features = [
tauri-utils = { version = "2.2.0", path = "../tauri-utils", features = [
"resources",
] }
image = "0.25.0"
flate2 = "1.0"
anyhow = "1.0"
thiserror = "1.0"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
strsim = "0.11.0"
tar = "0.4.40"
image = "0.25"
flate2 = "1"
anyhow = "1"
thiserror = "2"
serde_json = "1"
serde = { version = "1", features = ["derive"] }
strsim = "0.11"
tar = "0.4"
walkdir = "2"
handlebars = "6"
tempfile = "3.10.1"
tempfile = "3"
log = { version = "0.4.21", features = ["kv"] }
dirs = "5"
dirs = "6"
os_pipe = "1"
ureq = { version = "2.9.6", default-features = false, features = [
"socks-proxy",
] }
ureq = { version = "3", default-features = false, features = ["socks-proxy"] }
native-tls = { version = "0.2", optional = true }
hex = "0.4"
semver = "1"
sha1 = "0.10"
sha2 = "0.10"
zip = { version = "2.0", default-features = false, features = ["deflate"] }
zip = { version = "2", default-features = false, features = ["deflate"] }
dunce = "1"
url = "2"
uuid = { version = "1", features = ["v4", "v5"] }
@@ -48,7 +46,7 @@ regex = "1"
[target."cfg(target_os = \"windows\")".dependencies]
bitness = "0.4"
windows-registry = "0.3.0"
windows-registry = "0.5"
glob = "0.3"
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
@@ -59,13 +57,13 @@ features = ["Win32_System_SystemInformation", "Win32_System_Diagnostics_Debug"]
icns = { package = "tauri-icns", version = "0.1" }
time = { version = "0.3", features = ["formatting"] }
plist = "1"
tauri-macos-sign = { version = "2.0.1", path = "../tauri-macos-sign" }
tauri-macos-sign = { version = "2.1.0", path = "../tauri-macos-sign" }
[target."cfg(target_os = \"linux\")".dependencies]
heck = "0.5"
ar = "0.9.0"
md5 = "0.7.0"
rpm = "0.15.0"
ar = "0.9"
md5 = "0.7"
rpm = { version = "0.16", features = ["bzip2-compression"] }
[lib]
name = "tauri_bundler"
@@ -75,4 +73,4 @@ path = "src/lib.rs"
default = ["rustls"]
native-tls = ["ureq/native-tls"]
native-tls-vendored = ["native-tls", "native-tls/vendored"]
rustls = ["ureq/tls"]
rustls = ["ureq/rustls"]

View File

@@ -4,12 +4,10 @@
// SPDX-License-Identifier: MIT
mod category;
mod common;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "macos")]
mod macos;
mod path_utils;
mod platform;
mod settings;
mod updater_bundle;
@@ -72,8 +70,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
// Sign the sidecar binaries
for bin in settings.external_binaries() {
let path = bin?;
let skip =
std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").map_or(false, |v| v == "true");
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").is_ok_and(|v| v == "true");
if skip {
continue;
}
@@ -151,6 +148,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
| PackageType::MacOsBundle
| PackageType::Nsis
| PackageType::WindowsMsi
| PackageType::Deb
)
} else {
matches!(package_type, PackageType::MacOsBundle)
@@ -166,7 +164,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
// Self contained updater, no need to zip
matches!(
package_type,
PackageType::AppImage | PackageType::Nsis | PackageType::WindowsMsi
PackageType::AppImage | PackageType::Nsis | PackageType::WindowsMsi | PackageType::Deb
)
})
{

View File

@@ -249,7 +249,7 @@ struct AppCategoryVisitor {
did_you_mean: Option<&'static str>,
}
impl<'d> serde::de::Visitor<'d> for AppCategoryVisitor {
impl serde::de::Visitor<'_> for AppCategoryVisitor {
type Value = AppCategory;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@@ -0,0 +1,265 @@
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::debian;
use crate::{
bundle::settings::Arch,
utils::{fs_utils, http_utils::download, CommandExt},
Settings,
};
use anyhow::Context;
use std::{
fs,
path::{Path, PathBuf},
process::Command,
};
/// Bundles the project.
/// Returns a vector of PathBuf that shows where the AppImage was created.
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
// generate the deb binary name
let appimage_arch: &str = match settings.binary_arch() {
Arch::X86_64 => "amd64",
Arch::X86 => "i386",
Arch::AArch64 => "aarch64",
Arch::Armhf => "armhf",
target => {
return Err(crate::Error::ArchError(format!(
"Unsupported architecture: {:?}",
target
)));
}
};
let tools_arch = settings.target().split('-').next().unwrap();
let output_path = settings.project_out_directory().join("bundle/appimage");
if output_path.exists() {
fs::remove_dir_all(&output_path)?;
}
let tools_path = settings
.local_tools_directory()
.map(|d| d.join(".tauri"))
.unwrap_or_else(|| {
dirs::cache_dir().map_or_else(|| output_path.to_path_buf(), |p| p.join("tauri"))
});
fs::create_dir_all(&tools_path)?;
let linuxdeploy_path = prepare_tools(&tools_path, tools_arch)?;
let package_dir = settings.project_out_directory().join("bundle/appimage_deb");
let main_binary = settings.main_binary()?;
let product_name = settings.product_name();
let mut settings = settings.clone();
if main_binary.name().contains(' ') {
let main_binary_path = settings.binary_path(main_binary);
let project_out_directory = settings.project_out_directory();
let main_binary_name_kebab = heck::AsKebabCase(main_binary.name()).to_string();
let new_path = project_out_directory.join(&main_binary_name_kebab);
fs::copy(main_binary_path, new_path)?;
let main_binary = settings.main_binary_mut()?;
main_binary.set_name(main_binary_name_kebab);
}
// generate deb_folder structure
let (data_dir, icons) = debian::generate_data(&settings, &package_dir)
.with_context(|| "Failed to build data folders and files")?;
fs_utils::copy_custom_files(&settings.appimage().files, &data_dir)
.with_context(|| "Failed to copy custom files")?;
fs::create_dir_all(&output_path)?;
let app_dir_path = output_path.join(format!("{}.AppDir", settings.product_name()));
let appimage_filename = format!(
"{}_{}_{}.AppImage",
settings.product_name(),
settings.version_string(),
appimage_arch
);
let appimage_path = output_path.join(&appimage_filename);
fs_utils::create_dir(&app_dir_path, true)?;
fs::create_dir_all(&tools_path)?;
let larger_icon = icons
.iter()
.filter(|i| i.width == i.height)
.max_by_key(|i| i.width)
.expect("couldn't find a square icon to use as AppImage icon");
let larger_icon_path = larger_icon
.path
.strip_prefix(package_dir.join("data"))
.unwrap()
.to_string_lossy()
.to_string();
log::info!(action = "Bundling"; "{} ({})", appimage_filename, appimage_path.display());
let app_dir_usr = app_dir_path.join("usr/");
let app_dir_usr_bin = app_dir_usr.join("bin/");
let app_dir_usr_lib = app_dir_usr.join("lib/");
fs_utils::copy_dir(&data_dir.join("usr/"), &app_dir_usr)?;
// Using create_dir_all for a single dir so we don't get errors if the path already exists
fs::create_dir_all(&app_dir_usr_bin)?;
fs::create_dir_all(app_dir_usr_lib)?;
// Copy bins and libs that linuxdeploy doesn't know about
// we also check if the user may have provided their own copy already
// xdg-open will be handled by the `files` config instead
if settings.deep_link_protocols().is_some() && !app_dir_usr_bin.join("xdg-open").exists() {
fs::copy("/usr/bin/xdg-mime", app_dir_usr_bin.join("xdg-mime"))
.context("xdg-mime binary not found")?;
}
// we also check if the user may have provided their own copy already
if settings.appimage().bundle_xdg_open && !app_dir_usr_bin.join("xdg-open").exists() {
fs::copy("/usr/bin/xdg-open", app_dir_usr_bin.join("xdg-open"))
.context("xdg-open binary not found")?;
}
let search_dirs = [
match settings.binary_arch() {
Arch::X86_64 => "/usr/lib/x86_64-linux-gnu/",
Arch::X86 => "/usr/lib/i386-linux-gnu/",
Arch::AArch64 => "/usr/lib/aarch64-linux-gnu/",
Arch::Armhf => "/usr/lib/arm-linux-gnueabihf/",
_ => unreachable!(),
},
"/usr/lib64",
"/usr/lib",
"/usr/libexec",
];
for file in [
"WebKitNetworkProcess",
"WebKitWebProcess",
"injected-bundle/libwebkit2gtkinjectedbundle.so",
] {
for source in search_dirs.map(PathBuf::from) {
// TODO: Check if it's the same dir name on all systems
let source = source.join("webkit2gtk-4.1").join(file);
if source.exists() {
fs_utils::copy_file(
&source,
&app_dir_path.join(source.strip_prefix("/").unwrap()),
)?;
}
}
}
fs::copy(
tools_path.join(format!("AppRun-{tools_arch}")),
app_dir_path.join("AppRun"),
)?;
fs::copy(
app_dir_path.join(larger_icon_path),
app_dir_path.join(format!("{product_name}.png")),
)?;
std::os::unix::fs::symlink(
app_dir_path.join(format!("{product_name}.png")),
app_dir_path.join(".DirIcon"),
)?;
std::os::unix::fs::symlink(
app_dir_path.join(format!("usr/share/applications/{product_name}.desktop")),
app_dir_path.join(format!("{product_name}.desktop")),
)?;
let log_level = match settings.log_level() {
log::Level::Error => "3",
log::Level::Warn => "2",
log::Level::Info => "1",
_ => "0",
};
let mut cmd = Command::new(linuxdeploy_path);
cmd.env("OUTPUT", &appimage_path);
cmd.args([
"--appimage-extract-and-run",
"--verbosity",
log_level,
"--appdir",
&app_dir_path.display().to_string(),
"--plugin",
"gtk",
]);
if settings.appimage().bundle_media_framework {
cmd.args(["--plugin", "gstreamer"]);
}
cmd.args(["--output", "appimage"]);
// Linuxdeploy logs everything into stderr so we have to ignore the output ourselves here
if settings.log_level() == log::Level::Error {
log::debug!(action = "Running"; "Command `linuxdeploy {}`", cmd.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
if !cmd.output()?.status.success() {
return Err(crate::Error::GenericError(
"failed to run linuxdeploy".to_string(),
));
}
} else {
cmd.output_ok()?;
}
fs::remove_dir_all(&package_dir)?;
Ok(vec![appimage_path])
}
// returns the linuxdeploy path to keep linuxdeploy_arch contained
fn prepare_tools(tools_path: &Path, arch: &str) -> crate::Result<PathBuf> {
let apprun = tools_path.join(format!("AppRun-{arch}"));
if !apprun.exists() {
let data = download(&format!(
"https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-{arch}"
))?;
write_and_make_executable(&apprun, data)?;
}
let linuxdeploy_arch = if arch == "i686" { "i383" } else { arch };
let linuxdeploy = tools_path.join(format!("linuxdeploy-{linuxdeploy_arch}.AppImage"));
if !linuxdeploy.exists() {
let data = download(&format!("https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy/linuxdeploy-{linuxdeploy_arch}.AppImage"))?;
write_and_make_executable(&linuxdeploy, data)?;
}
let gtk = tools_path.join("linuxdeploy-plugin-gtk.sh");
if !gtk.exists() {
let data = download("https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh")?;
write_and_make_executable(&gtk, data)?;
}
let gstreamer = tools_path.join("linuxdeploy-plugin-gstreamer.sh");
if !gstreamer.exists() {
let data = download("https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh")?;
write_and_make_executable(&gstreamer, data)?;
}
// This should prevent linuxdeploy to be detected by appimage integration tools
let _ = Command::new("dd")
.args([
"if=/dev/zero",
"bs=1",
"count=3",
"seek=8",
"conv=notrunc",
&format!("of={}", linuxdeploy.display()),
])
.output();
Ok(linuxdeploy)
}
fn write_and_make_executable(path: &Path, data: Vec<u8>) -> std::io::Result<()> {
use std::os::unix::fs::PermissionsExt;
fs::write(path, data)?;
fs::set_permissions(path, fs::Permissions::from_mode(0o770))?;
Ok(())
}

View File

@@ -1,86 +0,0 @@
#!/usr/bin/env bash
# Copyright 2019-2024 Tauri Programme within The Commons Conservancy
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: MIT
set -euxo pipefail
export ARCH={{arch}}
APPIMAGE_BUNDLE_XDG_OPEN=${APPIMAGE_BUNDLE_XDG_OPEN-0}
APPIMAGE_BUNDLE_XDG_MIME=${APPIMAGE_BUNDLE_XDG_MIME-0}
APPIMAGE_BUNDLE_GSTREAMER=${APPIMAGE_BUNDLE_GSTREAMER-0}
TAURI_TRAY_LIBRARY_PATH=${TAURI_TRAY_LIBRARY_PATH-0}
if [ "$ARCH" == "i686" ]; then
linuxdeploy_arch="i386"
else
linuxdeploy_arch="$ARCH"
fi
mkdir -p "{{product_name}}.AppDir"
cp -r ../appimage_deb/data/usr "{{product_name}}.AppDir"
cd "{{product_name}}.AppDir"
mkdir -p "usr/bin"
mkdir -p "usr/lib"
if [[ "$APPIMAGE_BUNDLE_XDG_OPEN" != "0" ]] && [[ -f "/usr/bin/xdg-open" ]]; then
echo "Copying /usr/bin/xdg-open"
cp /usr/bin/xdg-open usr/bin
fi
if [[ "$APPIMAGE_BUNDLE_XDG_MIME" != "0" ]] && [[ -f "/usr/bin/xdg-mime" ]]; then
echo "Copying /usr/bin/xdg-mime"
cp /usr/bin/xdg-mime usr/bin
fi
if [[ "$TAURI_TRAY_LIBRARY_PATH" != "0" ]]; then
echo "Copying appindicator library ${TAURI_TRAY_LIBRARY_PATH}"
cp ${TAURI_TRAY_LIBRARY_PATH} usr/lib
# It looks like we're practicing good hygiene by adding the ABI version.
# But for compatibility we'll symlink this file to what we did before.
# Specifically this prevents breaking libappindicator-sys v0.7.1 and v0.7.2.
if [[ "$TAURI_TRAY_LIBRARY_PATH" == *.so.1 ]]; then
readonly soname=$(basename "$TAURI_TRAY_LIBRARY_PATH")
readonly old_name=$(basename "$TAURI_TRAY_LIBRARY_PATH" .1)
echo "Adding compatibility symlink ${old_name} -> ${soname}"
ln -s ${soname} usr/lib/${old_name}
fi
fi
# Copy WebKit files. Follow symlinks in case `/usr/lib64` is a symlink to `/usr/lib`
find -L /usr/lib* -name WebKitNetworkProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true
find -L /usr/lib* -name WebKitWebProcess -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true
find -L /usr/lib* -name libwebkit2gtkinjectedbundle.so -exec mkdir -p "$(dirname '{}')" \; -exec cp --parents '{}' "." \; || true
( cd "{{tauri_tools_path}}" && ( wget -q -4 -N https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-${ARCH} || wget -q -4 -N https://github.com/AppImage/AppImageKit/releases/download/12/AppRun-${ARCH} ) )
chmod +x "{{tauri_tools_path}}/AppRun-${ARCH}"
# We need AppRun to be installed as {{product_name}}.AppDir/AppRun.
# Otherwise the linuxdeploy scripts will default to symlinking our main bin instead and will crash on trying to launch.
cp "{{tauri_tools_path}}/AppRun-${ARCH}" AppRun
cp "{{icon_path}}" .DirIcon
ln -sf "{{icon_path}}" "{{product_name}}.png"
ln -sf "usr/share/applications/{{product_name}}.desktop" "{{product_name}}.desktop"
cd ..
if [[ "$APPIMAGE_BUNDLE_GSTREAMER" != "0" ]]; then
gst_plugin="--plugin gstreamer"
wget -q -4 -N "https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh"
chmod +x linuxdeploy-plugin-gstreamer.sh
else
gst_plugin=""
fi
( cd "{{tauri_tools_path}}" && wget -q -4 -N https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh )
( cd "{{tauri_tools_path}}" && wget -q -4 -N https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy/linuxdeploy-${linuxdeploy_arch}.AppImage )
chmod +x "{{tauri_tools_path}}/linuxdeploy-plugin-gtk.sh"
chmod +x "{{tauri_tools_path}}/linuxdeploy-${linuxdeploy_arch}.AppImage"
dd if=/dev/zero bs=1 count=3 seek=8 conv=notrunc of="{{tauri_tools_path}}/linuxdeploy-${linuxdeploy_arch}.AppImage"
OUTPUT="{{appimage_filename}}" "{{tauri_tools_path}}/linuxdeploy-${linuxdeploy_arch}.AppImage" --appimage-extract-and-run --appdir "{{product_name}}.AppDir" --plugin gtk ${gst_plugin} --output appimage

View File

@@ -1,139 +0,0 @@
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::{
super::{
common::{self, CommandExt},
path_utils,
},
debian,
};
use crate::{bundle::settings::Arch, Settings};
use anyhow::Context;
use handlebars::Handlebars;
use std::{
collections::BTreeMap,
fs,
path::PathBuf,
process::{Command, Stdio},
};
/// Bundles the project.
/// Returns a vector of PathBuf that shows where the AppImage was created.
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
// generate the deb binary name
let arch: &str = match settings.binary_arch() {
Arch::X86_64 => "amd64",
Arch::X86 => "i386",
Arch::AArch64 => "aarch64",
Arch::Armhf => "armhf",
target => {
return Err(crate::Error::ArchError(format!(
"Unsupported architecture: {:?}",
target
)));
}
};
let package_dir = settings.project_out_directory().join("bundle/appimage_deb");
let main_binary = settings.main_binary()?;
let mut settings = settings.clone();
if main_binary.name().contains(" ") {
let main_binary_path = settings.binary_path(main_binary);
let project_out_directory = settings.project_out_directory();
let main_binary_name_kebab = heck::AsKebabCase(main_binary.name()).to_string();
let new_path = project_out_directory.join(&main_binary_name_kebab);
fs::copy(main_binary_path, new_path)?;
let main_binary = settings.main_binary_mut()?;
main_binary.set_name(main_binary_name_kebab);
}
// generate deb_folder structure
let (data_dir, icons) = debian::generate_data(&settings, &package_dir)
.with_context(|| "Failed to build data folders and files")?;
common::copy_custom_files(&settings.appimage().files, &data_dir)
.with_context(|| "Failed to copy custom files")?;
let output_path = settings.project_out_directory().join("bundle/appimage");
if output_path.exists() {
fs::remove_dir_all(&output_path)?;
}
fs::create_dir_all(output_path.clone())?;
let app_dir_path = output_path.join(format!("{}.AppDir", settings.product_name()));
let appimage_filename = format!(
"{}_{}_{}.AppImage",
settings.product_name(),
settings.version_string(),
arch
);
let appimage_path = output_path.join(&appimage_filename);
path_utils::create(app_dir_path, true)?;
// setup data to insert into shell script
let mut sh_map = BTreeMap::new();
sh_map.insert("arch", settings.target().split('-').next().unwrap());
sh_map.insert("product_name", settings.product_name());
sh_map.insert("appimage_filename", &appimage_filename);
let tauri_tools_path = settings
.local_tools_directory()
.map(|d| d.join(".tauri"))
.unwrap_or_else(|| {
dirs::cache_dir().map_or_else(|| output_path.to_path_buf(), |p| p.join("tauri"))
});
std::fs::create_dir_all(&tauri_tools_path)?;
let tauri_tools_path_str = tauri_tools_path.to_string_lossy();
sh_map.insert("tauri_tools_path", &tauri_tools_path_str);
let larger_icon = icons
.iter()
.filter(|i| i.width == i.height)
.max_by_key(|i| i.width)
.expect("couldn't find a square icon to use as AppImage icon");
let larger_icon_path = larger_icon
.path
.strip_prefix(package_dir.join("data"))
.unwrap()
.to_string_lossy()
.to_string();
sh_map.insert("icon_path", &larger_icon_path);
// initialize shell script template.
let mut handlebars = Handlebars::new();
handlebars.register_escape_fn(handlebars::no_escape);
handlebars
.register_template_string("appimage", include_str!("./appimage"))
.expect("Failed to register template for handlebars");
let temp = handlebars.render("appimage", &sh_map)?;
// create the shell script file in the target/ folder.
let sh_file = output_path.join("build_appimage.sh");
log::info!(action = "Bundling"; "{} ({})", appimage_filename, appimage_path.display());
fs::write(&sh_file, temp)?;
// chmod script for execution
Command::new("chmod")
.arg("777")
.arg(&sh_file)
.current_dir(output_path.clone())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("Failed to chmod script");
// execute the shell script to build the appimage.
Command::new(&sh_file)
.current_dir(output_path)
.output_ok()
.context("error running build_appimage.sh")?;
fs::remove_dir_all(&package_dir)?;
Ok(vec![appimage_path])
}

View File

@@ -23,8 +23,8 @@
// metadata, as well as generating the md5sums file. Currently we do not
// generate postinst or prerm files.
use super::{super::common, freedesktop};
use crate::{bundle::settings::Arch, Settings};
use super::freedesktop;
use crate::{bundle::settings::Arch, utils::fs_utils, Settings};
use anyhow::Context;
use flate2::{write::GzEncoder, Compression};
use tar::HeaderMode;
@@ -73,7 +73,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
let (data_dir, _) = generate_data(settings, &package_dir)
.with_context(|| "Failed to build data folders and files")?;
common::copy_custom_files(&settings.deb().files, &data_dir)
fs_utils::copy_custom_files(&settings.deb().files, &data_dir)
.with_context(|| "Failed to copy custom files")?;
// Generate control files.
@@ -113,7 +113,7 @@ pub fn generate_data(
for bin in settings.binaries() {
let bin_path = settings.binary_path(bin);
common::copy_file(&bin_path, bin_dir.join(bin.name()))
fs_utils::copy_file(&bin_path, &bin_dir.join(bin.name()))
.with_context(|| format!("Failed to copy binary from {bin_path:?}"))?;
}
@@ -141,7 +141,7 @@ fn generate_changelog_file(settings: &Settings, data_dir: &Path) -> crate::Resul
let product_name = settings.product_name();
let dest_path = data_dir.join(format!("usr/share/doc/{product_name}/changelog.gz"));
let changelog_file = common::create_file(&dest_path)?;
let changelog_file = fs_utils::create_file(&dest_path)?;
let mut gzip_encoder = GzEncoder::new(changelog_file, Compression::new(9));
io::copy(&mut src_file, &mut gzip_encoder)?;
@@ -161,7 +161,7 @@ fn generate_control_file(
// For more information about the format of this file, see
// https://www.debian.org/doc/debian-policy/ch-controlfields.html
let dest_path = control_dir.join("control");
let mut file = common::create_file(&dest_path)?;
let mut file = fs_utils::create_file(&dest_path)?;
let package = heck::AsKebabCase(settings.product_name());
writeln!(file, "Package: {}", package)?;
writeln!(file, "Version: {}", settings.version_string())?;
@@ -198,6 +198,15 @@ fn generate_control_file(
if !dependencies.is_empty() {
writeln!(file, "Depends: {}", dependencies.join(", "))?;
}
let dependencies = settings
.deb()
.recommends
.as_ref()
.cloned()
.unwrap_or_default();
if !dependencies.is_empty() {
writeln!(file, "Recommends: {}", dependencies.join(", "))?;
}
let provides = settings
.deb()
.provides
@@ -285,7 +294,7 @@ fn create_script_file_from_path(from: &PathBuf, to: &PathBuf) -> crate::Result<(
/// for each file within the `data_dir`.
fn generate_md5sums(control_dir: &Path, data_dir: &Path) -> crate::Result<()> {
let md5sums_path = control_dir.join("md5sums");
let mut md5sums_file = common::create_file(&md5sums_path)?;
let mut md5sums_file = fs_utils::create_file(&md5sums_path)?;
for entry in WalkDir::new(data_dir) {
let entry = entry?;
let path = entry.path();
@@ -318,7 +327,7 @@ fn copy_resource_files(settings: &Settings, data_dir: &Path) -> crate::Result<()
/// Create an empty file at the given path, creating any parent directories as
/// needed, then write `data` into the file.
fn create_file_with_data<P: AsRef<Path>>(path: P, data: &str) -> crate::Result<()> {
let mut file = common::create_file(path.as_ref())?;
let mut file = fs_utils::create_file(path.as_ref())?;
file.write_all(data.as_bytes())?;
file.flush()?;
Ok(())
@@ -367,7 +376,7 @@ fn create_tar_from_dir<P: AsRef<Path>, W: Write>(src_dir: P, dest_file: W) -> cr
fn tar_and_gzip_dir<P: AsRef<Path>>(src_dir: P) -> crate::Result<PathBuf> {
let src_dir = src_dir.as_ref();
let dest_path = src_dir.with_extension("tar.gz");
let dest_file = common::create_file(&dest_path)?;
let dest_file = fs_utils::create_file(&dest_path)?;
let gzip_encoder = GzEncoder::new(dest_file, Compression::default());
let gzip_encoder = create_tar_from_dir(src_dir, gzip_encoder)?;
let mut dest_file = gzip_encoder.finish()?;
@@ -378,7 +387,7 @@ fn tar_and_gzip_dir<P: AsRef<Path>>(src_dir: P) -> crate::Result<PathBuf> {
/// Creates an `ar` archive from the given source files and writes it to the
/// given destination path.
fn create_archive(srcs: Vec<PathBuf>, dest: &Path) -> crate::Result<()> {
let mut builder = ar::Builder::new(common::create_file(dest)?);
let mut builder = ar::Builder::new(fs_utils::create_file(dest)?);
for path in &srcs {
builder.append_path(path)?;
}

View File

@@ -26,8 +26,10 @@ use handlebars::Handlebars;
use image::{self, codecs::png::PngDecoder, ImageDecoder};
use serde::Serialize;
use crate::bundle::common;
use crate::Settings;
use crate::{
utils::{self, fs_utils},
Settings,
};
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub struct Icon {
@@ -65,7 +67,7 @@ pub fn list_icon_files(
let decoder = PngDecoder::new(BufReader::new(File::open(&icon_path)?))?;
let width = decoder.dimensions().0;
let height = decoder.dimensions().1;
let is_high_density = common::is_retina(&icon_path);
let is_high_density = utils::is_retina(&icon_path);
let dest_path = get_dest_path(width, height, is_high_density);
Icon {
width,
@@ -84,7 +86,7 @@ pub fn list_icon_files(
pub fn copy_icon_files(settings: &Settings, data_dir: &Path) -> crate::Result<Vec<Icon>> {
let icons = list_icon_files(settings, data_dir)?;
for (icon, src) in &icons {
common::copy_file(src, &icon.path)?;
fs_utils::copy_file(src, &icon.path)?;
}
Ok(icons.into_keys().collect())
@@ -105,7 +107,7 @@ pub fn generate_desktop_file(
let path = PathBuf::from("usr/share/applications").join(desktop_file_name);
let dest_path = PathBuf::from("/").join(&path);
let file_path = data_dir.join(&path);
let file = &mut common::create_file(&file_path)?;
let file = &mut fs_utils::create_file(&file_path)?;
let mut handlebars = Handlebars::new();
handlebars.register_escape_fn(handlebars::no_escape);
@@ -151,7 +153,7 @@ pub fn generate_desktop_file(
let mime_type = (!mime_type.is_empty()).then_some(mime_type.join(";"));
let bin_name_exec = if bin_name.contains(" ") {
let bin_name_exec = if bin_name.contains(' ') {
format!("\"{bin_name}\"")
} else {
bin_name.to_string()

View File

@@ -12,6 +12,7 @@ use std::{
fs::{self, File},
path::{Path, PathBuf},
};
use tauri_utils::config::RpmCompression;
use super::freedesktop;
@@ -54,11 +55,25 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
let license = settings.license().unwrap_or_default();
let name = heck::AsKebabCase(settings.product_name()).to_string();
let compression = settings
.rpm()
.compression
.map(|c| match c {
RpmCompression::Gzip { level } => rpm::CompressionWithLevel::Gzip(level),
RpmCompression::Zstd { level } => rpm::CompressionWithLevel::Zstd(level),
RpmCompression::Xz { level } => rpm::CompressionWithLevel::Xz(level),
RpmCompression::Bzip2 { level } => rpm::CompressionWithLevel::Bzip2(level),
_ => rpm::CompressionWithLevel::None,
})
// This matches .deb compression. On a 240MB source binary the bundle will be 100KB larger than rpm's default while reducing build times by ~25%.
// TODO: Default to Zstd in v3 to match rpm-rs new default in 0.16
.unwrap_or(rpm::CompressionWithLevel::Gzip(6));
let mut builder = rpm::PackageBuilder::new(&name, version, &license, arch, summary)
.epoch(epoch)
.release(release)
// This matches .deb compression. On a 240MB source binary the bundle will be 100KB larger than rpm's default while reducing build times by ~25%.
.compression(rpm::CompressionWithLevel::Gzip(6));
.compression(compression);
if let Some(description) = settings.long_description() {
builder = builder.description(description);
@@ -84,6 +99,17 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
builder = builder.provides(Dependency::any(dep));
}
// Add recommends
for dep in settings
.rpm()
.recommends
.as_ref()
.cloned()
.unwrap_or_default()
{
builder = builder.recommends(Dependency::any(dep));
}
// Add conflicts
for dep in settings
.rpm()

View File

@@ -23,11 +23,13 @@
// files into the `Contents` directory of the bundle.
use super::{
super::common::{self, CommandExt},
icon::create_icns_file,
sign::{notarize, notarize_auth, sign, NotarizeAuthError, SignTarget},
};
use crate::Settings;
use crate::{
utils::{fs_utils, CommandExt},
Settings,
};
use anyhow::Context;
@@ -157,7 +159,7 @@ fn copy_binaries_to_bundle(
for bin in settings.binaries() {
let bin_path = settings.binary_path(bin);
let dest_path = dest_dir.join(bin.name());
common::copy_file(&bin_path, &dest_path)
fs_utils::copy_file(&bin_path, &dest_path)
.with_context(|| format!("Failed to copy binary from {:?}", bin_path))?;
paths.push(dest_path);
}
@@ -173,10 +175,10 @@ fn copy_custom_files_to_bundle(bundle_directory: &Path, settings: &Settings) ->
contents_path
};
if path.is_file() {
common::copy_file(path, bundle_directory.join(contents_path))
fs_utils::copy_file(path, &bundle_directory.join(contents_path))
.with_context(|| format!("Failed to copy file {:?} to {:?}", path, contents_path))?;
} else {
common::copy_dir(path, &bundle_directory.join(contents_path))
fs_utils::copy_dir(path, &bundle_directory.join(contents_path))
.with_context(|| format!("Failed to copy directory {:?} to {:?}", path, contents_path))?;
}
}
@@ -349,7 +351,7 @@ fn copy_framework_from(dest_dir: &Path, framework: &str, src_dir: &Path) -> crat
let src_name = format!("{}.framework", framework);
let src_path = src_dir.join(&src_name);
if src_path.exists() {
common::copy_dir(&src_path, &dest_dir.join(&src_name))?;
fs_utils::copy_dir(&src_path, &dest_dir.join(&src_name))?;
Ok(true)
} else {
Ok(false)
@@ -382,7 +384,7 @@ fn copy_frameworks_to_bundle(
.file_name()
.expect("Couldn't get framework filename");
let dest_path = dest_dir.join(src_name);
common::copy_dir(&src_path, &dest_path)?;
fs_utils::copy_dir(&src_path, &dest_path)?;
add_framework_sign_path(&src_path, &dest_path, &mut paths);
continue;
} else if framework.ends_with(".dylib") {
@@ -395,7 +397,7 @@ fn copy_frameworks_to_bundle(
}
let src_name = src_path.file_name().expect("Couldn't get library filename");
let dest_path = dest_dir.join(src_name);
common::copy_file(&src_path, &dest_path)?;
fs_utils::copy_file(&src_path, &dest_path)?;
paths.push(SignTarget {
path: dest_path,
is_an_executable: false,

View File

@@ -5,7 +5,8 @@
use super::{app, icon::create_icns_file};
use crate::{
bundle::{common::CommandExt, settings::Arch, Bundle},
bundle::{settings::Arch, Bundle},
utils::CommandExt,
PackageType, Settings,
};
@@ -173,9 +174,11 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
// Issue #592 - Building MacOS dmg files on CI
// https://github.com/tauri-apps/tauri/issues/592
if let Some(value) = env::var_os("CI") {
if value == "true" {
bundle_dmg_cmd.arg("--skip-jenkins");
if env::var_os("TAURI_BUNDLER_DMG_IGNORE_CI").unwrap_or_default() != "true" {
if let Some(value) = env::var_os("CI") {
if value == "true" {
bundle_dmg_cmd.arg("--skip-jenkins");
}
}
}
@@ -191,16 +194,19 @@ pub fn bundle_project(settings: &Settings, bundles: &[Bundle]) -> crate::Result<
fs::rename(bundle_dir.join(dmg_name), dmg_path.clone())?;
// Sign DMG if needed
if let Some(keychain) = super::sign::keychain(settings.macos().signing_identity.as_deref())? {
super::sign::sign(
&keychain,
vec![super::sign::SignTarget {
path: dmg_path.clone(),
is_an_executable: false,
}],
settings,
)?;
// skipping self-signing DMGs https://github.com/tauri-apps/tauri/issues/12288
let identity = settings.macos().signing_identity.as_deref();
if identity != Some("-") {
if let Some(keychain) = super::sign::keychain(identity)? {
super::sign::sign(
&keychain,
vec![super::sign::SignTarget {
path: dmg_path.clone(),
is_an_executable: false,
}],
settings,
)?;
}
}
Ok(Bundled {

View File

@@ -3,7 +3,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::bundle::{common, Settings};
use crate::bundle::Settings;
use crate::utils::{self, fs_utils};
use std::{
cmp::min,
ffi::OsStr,
@@ -28,7 +29,7 @@ pub fn create_icns_file(out_dir: &Path, settings: &Settings) -> crate::Result<Op
if icon_path.extension() == Some(OsStr::new("icns")) {
let mut dest_path = out_dir.to_path_buf();
dest_path.push(icon_path.file_name().expect("Could not get icon filename"));
common::copy_file(&icon_path, &dest_path)?;
fs_utils::copy_file(&icon_path, &dest_path)?;
return Ok(Some(dest_path));
}
}
@@ -63,7 +64,7 @@ pub fn create_icns_file(out_dir: &Path, settings: &Settings) -> crate::Result<Op
for icon_path in settings.icon_files() {
let icon_path = icon_path?;
let icon = image::open(&icon_path)?;
let density = if common::is_retina(&icon_path) { 2 } else { 1 };
let density = if utils::is_retina(&icon_path) { 2 } else { 1 };
let (w, h) = icon.dimensions();
let orig_size = min(w, h);
let next_size_down = 2f32.powf((orig_size as f32).log2().floor()) as u32;

View File

@@ -13,7 +13,10 @@
// See https://developer.apple.com/go/?id=bundle-structure for a full
// explanation.
use crate::{bundle::common, Settings};
use crate::{
utils::{self, fs_utils},
Settings,
};
use anyhow::Context;
use image::{codecs::png::PngDecoder, GenericImageView, ImageDecoder};
@@ -50,7 +53,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
for src in settings.resource_files() {
let src = src?;
let dest = app_bundle_path.join(tauri_utils::resources::resource_relpath(&src));
common::copy_file(&src, &dest)
fs_utils::copy_file(&src, &dest)
.with_context(|| format!("Failed to copy resource file {:?}", src))?;
}
@@ -61,7 +64,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
for bin in settings.binaries() {
let bin_path = settings.binary_path(bin);
common::copy_file(&bin_path, app_bundle_path.join(bin.name()))
fs_utils::copy_file(&bin_path, &app_bundle_path.join(bin.name()))
.with_context(|| format!("Failed to copy binary from {:?}", bin_path))?;
}
@@ -93,11 +96,11 @@ fn generate_icon_files(bundle_dir: &Path, settings: &Settings) -> crate::Result<
let decoder = PngDecoder::new(BufReader::new(File::open(&icon_path)?))?;
let width = decoder.dimensions().0;
let height = decoder.dimensions().1;
let is_retina = common::is_retina(&icon_path);
let is_retina = utils::is_retina(&icon_path);
if !sizes.contains(&(width, height, is_retina)) {
sizes.insert((width, height, is_retina));
let dest_path = get_dest_path(width, height, is_retina);
common::copy_file(&icon_path, &dest_path)?;
fs_utils::copy_file(&icon_path, &dest_path)?;
}
}
// Fall back to non-PNG files for any missing sizes.
@@ -121,12 +124,12 @@ fn generate_icon_files(bundle_dir: &Path, settings: &Settings) -> crate::Result<
} else {
let icon = image::open(&icon_path)?;
let (width, height) = icon.dimensions();
let is_retina = common::is_retina(&icon_path);
let is_retina = utils::is_retina(&icon_path);
if !sizes.contains(&(width, height, is_retina)) {
sizes.insert((width, height, is_retina));
let dest_path = get_dest_path(width, height, is_retina);
icon.write_to(
&mut common::create_file(&dest_path)?,
&mut fs_utils::create_file(&dest_path)?,
image::ImageFormat::Png,
)?;
}
@@ -142,7 +145,7 @@ fn generate_info_plist(
settings: &Settings,
icon_filenames: &[String],
) -> crate::Result<()> {
let file = &mut common::create_file(&bundle_dir.join("Info.plist"))?;
let file = &mut fs_utils::create_file(&bundle_dir.join("Info.plist"))?;
writeln!(
file,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\

View File

@@ -48,9 +48,14 @@ pub fn sign(
log::info!(action = "Signing"; "with identity \"{}\"", keychain.signing_identity());
for target in targets {
let entitlements_path = if target.is_an_executable {
settings.macos().entitlements.as_ref().map(Path::new)
} else {
None
};
keychain.sign(
&target.path,
settings.macos().entitlements.as_ref().map(Path::new),
entitlements_path,
target.is_an_executable && settings.macos().hardened_runtime,
)?;
}

View File

@@ -1,287 +0,0 @@
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{
fs::{create_dir, create_dir_all, read_dir, remove_dir_all},
path::{Path, PathBuf},
};
/// Directory options.
#[derive(Default, Clone)]
pub struct DirOpts {
pub depth: u64,
}
/// File options.
pub struct FileOpts {
pub overwrite: bool,
pub skip: bool,
#[allow(dead_code)]
pub buffer_size: usize,
}
/// Copy options.
#[derive(Clone)]
pub struct Options {
pub overwrite: bool,
pub skip: bool,
pub buffer_size: usize,
pub copy_files: bool,
pub content_only: bool,
pub depth: u64,
}
/// Directory information descriptor
pub struct DirInfo {
pub size: u64,
pub files: Vec<String>,
pub directories: Vec<String>,
}
impl Default for Options {
fn default() -> Options {
Options {
overwrite: false,
skip: false,
buffer_size: 64000,
copy_files: false,
content_only: false,
depth: 0,
}
}
}
impl Default for FileOpts {
fn default() -> FileOpts {
FileOpts {
overwrite: false,
skip: false,
buffer_size: 64000,
}
}
}
/// Creates the given directory path,
/// erasing it first if specified.
pub fn create<P>(path: P, erase: bool) -> crate::Result<()>
where
P: AsRef<Path>,
{
if erase && path.as_ref().exists() {
remove(&path)?;
}
Ok(create_dir(&path)?)
}
/// Creates all of the directories of the specified path,
/// erasing it first if specified.
pub fn create_all<P>(path: P, erase: bool) -> crate::Result<()>
where
P: AsRef<Path>,
{
if erase && path.as_ref().exists() {
remove(&path)?;
}
Ok(create_dir_all(&path)?)
}
/// Removes the directory if it exists.
pub fn remove<P: AsRef<Path>>(path: P) -> crate::Result<()> {
if path.as_ref().exists() {
Ok(remove_dir_all(path)?)
} else {
Ok(())
}
}
/// Copy file with the given options.
pub fn copy_file<P, Q>(from: P, to: Q, options: &FileOpts) -> crate::Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let from = from.as_ref();
if !from.exists() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{msg}\" does not exist or you don't have access");
return Err(crate::Error::PathUtilError(msg));
}
return Err(crate::Error::PathUtilError(
"Path does not exist or you don't have access!".to_owned(),
));
}
if !from.is_file() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{msg}\" is not a file!");
return Err(crate::Error::PathUtilError(msg));
}
return Err(crate::Error::PathUtilError(
"Path is not a file!".to_owned(),
));
}
if !options.overwrite && to.as_ref().exists() {
if options.skip {
return Ok(0);
}
if let Some(msg) = to.as_ref().to_str() {
let msg = format!("Path \"{msg}\" is exist");
return Err(crate::Error::PathUtilError(msg));
}
}
Ok(std::fs::copy(from, to)?)
}
/// Copies the directory with the given options.
#[allow(dead_code)]
pub fn copy<P, Q>(from: P, to: Q, options: &Options) -> crate::Result<u64>
where
P: AsRef<Path>,
Q: AsRef<Path>,
{
let from = from.as_ref();
if !from.exists() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{msg}\" does not exist or you don't have access!");
return Err(crate::Error::PathUtilError(msg));
}
return Err(crate::Error::PathUtilError(
"Path does not exist or you don't have access".to_owned(),
));
}
if !from.is_dir() {
if let Some(msg) = from.to_str() {
let msg = format!("Path \"{msg}\" is not a directory!");
return Err(crate::Error::PathUtilError(msg));
}
return Err(crate::Error::PathUtilError(
"Path is not a directory".to_owned(),
));
}
let dir_name = if let Some(val) = from.components().last() {
val.as_os_str()
} else {
return Err(crate::Error::PathUtilError(
"Invalid Folder form".to_owned(),
));
};
let mut to: PathBuf = to.as_ref().to_path_buf();
if !options.content_only && (!options.copy_files || to.exists()) {
to.push(dir_name);
}
let mut read_options = DirOpts::default();
if options.depth > 0 {
read_options.depth = options.depth;
}
let dir_content = get_dir_info(from, &read_options)?;
for directory in dir_content.directories {
let tmp_to = Path::new(&directory).strip_prefix(from)?;
let dir = to.join(tmp_to);
if !dir.exists() {
if options.copy_files {
create_all(dir, false)?;
} else {
create(dir, false)?;
}
}
}
let mut result: u64 = 0;
for file in dir_content.files {
let to = to.to_path_buf();
let tp = Path::new(&file).strip_prefix(from)?;
let path = to.join(tp);
let file_options = FileOpts {
overwrite: options.overwrite,
skip: options.skip,
buffer_size: options.buffer_size,
};
let mut result_copy: crate::Result<u64>;
let mut work = true;
while work {
#[allow(clippy::needless_borrow)]
{
result_copy = copy_file(&file, &path, &file_options);
}
match result_copy {
Ok(val) => {
result += val;
work = false;
}
Err(err) => {
let err_msg = err.to_string();
return Err(crate::Error::PathUtilError(err_msg));
}
}
}
}
Ok(result)
}
/// Gets the DirInfo from the directory path with the given options.
pub fn get_dir_info<P>(path: P, options: &DirOpts) -> crate::Result<DirInfo>
where
P: AsRef<Path>,
{
let depth = if options.depth == 0 {
0
} else {
options.depth + 1
};
_get_dir_info(path, depth)
}
/// Gets the DirInfo from the directory with the given depth.
fn _get_dir_info<P>(path: P, mut depth: u64) -> crate::Result<DirInfo>
where
P: AsRef<Path>,
{
let mut directories = Vec::new();
let mut files = Vec::new();
let mut size = 0;
let item = path.as_ref().to_str();
if item.is_none() {
return Err(crate::Error::PathUtilError("Invalid Path".to_owned()));
}
let item = item.expect("Item had no data").to_string();
if path.as_ref().is_dir() {
directories.push(item);
if depth == 0 || depth > 1 {
if depth > 1 {
depth -= 1;
}
for entry in read_dir(&path)? {
let _path = entry?.path();
match _get_dir_info(_path, depth) {
Ok(items) => {
let mut _files = items.files;
let mut _directories = items.directories;
size += items.size;
files.append(&mut _files);
directories.append(&mut _directories);
}
Err(err) => return Err(err),
}
}
}
} else {
size = path.as_ref().metadata()?.len();
files.push(item);
}
Ok(DirInfo {
size,
files,
directories,
})
}

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::common::CommandExt;
use crate::utils::CommandExt;
use std::process::Command;
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy

View File

@@ -4,11 +4,14 @@
// SPDX-License-Identifier: MIT
use super::category::AppCategory;
use crate::bundle::{common, platform::target_triple};
use crate::{bundle::platform::target_triple, utils::fs_utils};
use anyhow::Context;
pub use tauri_utils::config::WebviewInstallMode;
use tauri_utils::{
config::{BundleType, DeepLinkProtocol, FileAssociation, NSISInstallerMode, NsisCompression},
config::{
BundleType, DeepLinkProtocol, FileAssociation, NSISInstallerMode, NsisCompression,
RpmCompression,
},
resources::{external_binaries, ResourcePaths},
};
@@ -170,6 +173,8 @@ pub struct DebianSettings {
// OS-specific settings:
/// the list of debian dependencies.
pub depends: Option<Vec<String>>,
/// the list of debian dependencies recommendations.
pub recommends: Option<Vec<String>>,
/// the list of dependencies the package provides.
pub provides: Option<Vec<String>>,
/// the list of package conflicts.
@@ -215,6 +220,10 @@ pub struct DebianSettings {
pub struct AppImageSettings {
/// The files to include in the Appimage Binary.
pub files: HashMap<PathBuf, PathBuf>,
/// Whether to include gstreamer plugins for audio/media support.
pub bundle_media_framework: bool,
/// Whether to include the `xdg-open` binary.
pub bundle_xdg_open: bool,
}
/// The RPM bundle settings.
@@ -222,13 +231,15 @@ pub struct AppImageSettings {
pub struct RpmSettings {
/// The list of RPM dependencies your application relies on.
pub depends: Option<Vec<String>>,
/// the list of of RPM dependencies your application recommends.
pub recommends: Option<Vec<String>>,
/// The list of RPM dependencies your application provides.
pub provides: Option<Vec<String>>,
/// The list of RPM dependencies your application conflicts with. They must not be present
/// in order for the package to be installed.
pub conflicts: Option<Vec<String>>,
/// The list of RPM dependencies your application supersedes - if this package is installed,
/// packages listed as obsoletes will be automatically removed (if they are present).
/// packages listed as "obsoletes" will be automatically removed (if they are present).
pub obsoletes: Option<Vec<String>>,
/// The RPM release tag.
pub release: String,
@@ -258,6 +269,8 @@ pub struct RpmSettings {
/// Path to script that will be executed after the package is removed. See
/// <http://ftp.rpm.org/max-rpm/s1-rpm-inside-scripts.html>
pub post_remove_script: Option<PathBuf>,
/// Compression algorithm and level. Defaults to `Gzip` with level 6.
pub compression: Option<RpmCompression>,
}
/// Position coordinates struct.
@@ -395,7 +408,7 @@ pub struct WixSettings {
pub banner_path: Option<PathBuf>,
/// Path to a bitmap file to use on the installation user interface dialogs.
/// It is used on the welcome and completion dialogs.
///
/// The required dimensions are 493px × 312px.
pub dialog_image_path: Option<PathBuf>,
/// Enables FIPS compliant algorithms.
@@ -1055,7 +1068,7 @@ impl Settings {
.to_string_lossy()
.replace(&format!("-{}", self.target), ""),
);
common::copy_file(&src, &dest)?;
fs_utils::copy_file(&src, &dest)?;
paths.push(dest);
}
Ok(paths)
@@ -1066,7 +1079,7 @@ impl Settings {
for resource in self.resource_files().iter() {
let resource = resource?;
let dest = path.join(resource.target());
common::copy_file(resource.path(), dest)?;
fs_utils::copy_file(resource.path(), &dest)?;
}
Ok(())
}

View File

@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::common;
use crate::{
bundle::{
windows::{
@@ -13,6 +11,7 @@ use crate::{
},
Bundle,
},
utils::fs_utils,
Settings,
};
use tauri_utils::display_path;
@@ -210,7 +209,7 @@ fn bundle_update_windows(settings: &Settings, bundles: &[Bundle]) -> crate::Resu
pub fn create_zip(src_file: &Path, dst_file: &Path) -> crate::Result<PathBuf> {
let parent_dir = dst_file.parent().expect("No data in parent");
fs::create_dir_all(parent_dir)?;
let writer = common::create_file(dst_file)?;
let writer = fs_utils::create_file(dst_file)?;
let file_name = src_file
.file_name()
@@ -235,7 +234,7 @@ pub fn create_zip(src_file: &Path, dst_file: &Path) -> crate::Result<PathBuf> {
fn create_tar(src_dir: &Path, dest_path: &Path) -> crate::Result<PathBuf> {
use flate2::{write::GzEncoder, Compression};
let dest_file = common::create_file(dest_path)?;
let dest_file = fs_utils::create_file(dest_path)?;
let gzip_encoder = GzEncoder::new(dest_file, Compression::default());
let gzip_encoder = create_tar_from_src(src_dir, gzip_encoder)?;

View File

@@ -70,9 +70,12 @@
<Property Id="ARPURLUPDATEINFO" Value="{{homepage}}"/>
{{/if}}
<!-- initialize with previous InstallDir -->
<Property Id="INSTALLDIR">
<RegistrySearch Id="PrevInstallDirReg" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw"/>
<!-- First attempt: Search for "InstallDir" -->
<RegistrySearch Id="PrevInstallDirWithName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Name="InstallDir" Type="raw" />
<!-- Second attempt: If the first fails, search for the default key value (this is how the nsis installer currently stores the path) -->
<RegistrySearch Id="PrevInstallDirNoName" Root="HKCU" Key="Software\\{{manufacturer}}\\{{product_name}}" Type="raw" />
</Property>
<!-- launch app checkbox -->

View File

@@ -3,17 +3,22 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::bundle::{
common::CommandExt,
path_utils::{copy_file, FileOpts},
settings::{Arch, Settings},
windows::{
sign::try_sign,
util::{
download_and_verify, download_webview2_bootstrapper, download_webview2_offline_installer,
extract_zip, HashAlgorithm, WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME,
use crate::{
bundle::{
settings::{Arch, Settings},
windows::{
sign::try_sign,
util::{
download_webview2_bootstrapper, download_webview2_offline_installer,
WIX_OUTPUT_FOLDER_NAME, WIX_UPDATER_OUTPUT_FOLDER_NAME,
},
},
},
utils::{
fs_utils::copy_file,
http_utils::{download_and_verify, extract_zip, HashAlgorithm},
CommandExt,
},
};
use anyhow::{bail, Context};
use handlebars::{html_escape, to_json, Handlebars};
@@ -198,14 +203,7 @@ fn copy_icon(settings: &Settings, filename: &str, path: &Path) -> crate::Result<
let icon_path = std::env::current_dir()?.join(path);
copy_file(
icon_path,
&icon_target_path,
&FileOpts {
overwrite: true,
..Default::default()
},
)?;
copy_file(&icon_path, &icon_target_path)?;
Ok(icon_target_path)
}
@@ -726,38 +724,26 @@ pub fn build_wix_app_installer(
);
// Create the update task XML
let mut skip_uac_task = Handlebars::new();
let skip_uac_task = Handlebars::new();
let xml = include_str!("./update-task.xml");
skip_uac_task
.register_template_string("update.xml", xml)
.map_err(|e| e.to_string())
.expect("Failed to setup Update Task handlebars");
let update_content = skip_uac_task.render_template(xml, &data)?;
let temp_xml_path = output_path.join("update.xml");
let update_content = skip_uac_task.render("update.xml", &data)?;
fs::write(temp_xml_path, update_content)?;
// Create the Powershell script to install the task
let mut skip_uac_task_installer = Handlebars::new();
skip_uac_task_installer.register_escape_fn(handlebars::no_escape);
let xml = include_str!("./install-task.ps1");
skip_uac_task_installer
.register_template_string("install-task.ps1", xml)
.map_err(|e| e.to_string())
.expect("Failed to setup Update Task Installer handlebars");
let install_script_content = skip_uac_task_installer.render_template(xml, &data)?;
let temp_ps1_path = output_path.join("install-task.ps1");
let install_script_content = skip_uac_task_installer.render("install-task.ps1", &data)?;
fs::write(temp_ps1_path, install_script_content)?;
// Create the Powershell script to uninstall the task
let mut skip_uac_task_uninstaller = Handlebars::new();
skip_uac_task_uninstaller.register_escape_fn(handlebars::no_escape);
let xml = include_str!("./uninstall-task.ps1");
skip_uac_task_uninstaller
.register_template_string("uninstall-task.ps1", xml)
.map_err(|e| e.to_string())
.expect("Failed to setup Update Task Uninstaller handlebars");
let install_script_content = skip_uac_task_uninstaller.render_template(xml, &data)?;
let temp_ps1_path = output_path.join("uninstall-task.ps1");
let install_script_content = skip_uac_task_uninstaller.render("uninstall-task.ps1", &data)?;
fs::write(temp_ps1_path, install_script_content)?;
data.insert("enable_elevated_update_task", to_json(true));
@@ -772,7 +758,9 @@ pub fn build_wix_app_installer(
let extension_regex = Regex::new("\"http://schemas.microsoft.com/wix/(\\w+)\"")?;
for fragment_path in fragment_paths {
let fragment_path = current_dir.join(fragment_path);
let fragment = fs::read_to_string(&fragment_path)?;
let fragment_content = fs::read_to_string(&fragment_path)?;
let fragment_handlebars = Handlebars::new();
let fragment = fragment_handlebars.render_template(&fragment_content, &data)?;
let mut extensions = Vec::new();
for cap in extension_regex.captures_iter(&fragment) {
extensions.push(wix_toolset_path.join(format!("Wix{}.dll", &cap[1])));
@@ -941,12 +929,11 @@ fn get_merge_modules(settings: &Settings) -> crate::Result<Vec<MergeModule>> {
let mut merge_modules = Vec::new();
let regex = Regex::new(r"[^\w\d\.]")?;
for msm in glob::glob(
settings
.project_out_directory()
.join("*.msm")
.to_string_lossy()
.to_string()
.as_str(),
&PathBuf::from(glob::Pattern::escape(
&settings.project_out_directory().to_string_lossy(),
))
.join("*.msm")
.to_string_lossy(),
)? {
let path = msm?;
let filename = path
@@ -1053,8 +1040,13 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
let mut dlls = Vec::new();
// TODO: The bundler should not include all DLLs it finds. Instead it should only include WebView2Loader.dll if present and leave the rest to the resources config.
let out_dir = settings.project_out_directory();
for dll in glob::glob(out_dir.join("*.dll").to_string_lossy().to_string().as_str())? {
for dll in glob::glob(
&PathBuf::from(glob::Pattern::escape(&out_dir.to_string_lossy()))
.join("*.dll")
.to_string_lossy(),
)? {
let path = dll?;
let resource_path = dunce::simplified(&path);
let relative_path = path
@@ -1072,15 +1064,15 @@ fn generate_resource_data(settings: &Settings) -> crate::Result<ResourceMap> {
}
if !dlls.is_empty() {
resources.insert(
"".to_string(),
ResourceDirectory {
resources
.entry("".to_string())
.and_modify(|r| r.files.append(&mut dlls))
.or_insert(ResourceDirectory {
path: "".to_string(),
name: "".to_string(),
directories: vec![],
files: dlls,
},
);
});
}
Ok(resources)

View File

@@ -56,7 +56,8 @@ ${StrLoc}
!define WEBVIEW2INSTALLERPATH "{{webview2_installer_path}}"
!define MINIMUMWEBVIEW2VERSION "{{minimum_webview2_version}}"
!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCTNAME}"
!define MANUPRODUCTKEY "Software\${MANUFACTURER}\${PRODUCTNAME}"
!define MANUKEY "Software\${MANUFACTURER}"
!define MANUPRODUCTKEY "${MANUKEY}\${PRODUCTNAME}"
!define UNINSTALLERSIGNCOMMAND "{{uninstaller_sign_cmd}}"
!define ESTIMATEDSIZE "{{estimated_size}}"
!define STARTMENUFOLDER "{{start_menu_folder}}"
@@ -834,12 +835,19 @@ Section Uninstall
DeleteRegKey HKCU "${UNINSTKEY}"
!endif
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
; Delete app data if the checkbox is selected
; and if not updating
${If} $DeleteAppDataCheckboxState = 1
${AndIf} $UpdateMode <> 1
; Clear the install location $INSTDIR from registry
DeleteRegKey SHCTX "${MANUPRODUCTKEY}"
DeleteRegKey /ifempty SHCTX "${MANUKEY}"
; Clear the install language from registry
DeleteRegValue HKCU "${MANUPRODUCTKEY}" "Installer Language"
DeleteRegKey /ifempty HKCU "${MANUPRODUCTKEY}"
DeleteRegKey /ifempty HKCU "${MANUKEY}"
SetShellVarContext current
RmDir /r "$APPDATA\${BUNDLEID}"
RmDir /r "$LOCALAPPDATA\${BUNDLEID}"

View File

@@ -0,0 +1,27 @@
LangString addOrReinstall ${LANG_PORTUGUESE} "Adicionar/Reinstalar componentes"
LangString alreadyInstalled ${LANG_PORTUGUESE} "Já instalado"
LangString alreadyInstalledLong ${LANG_PORTUGUESE} "${PRODUCTNAME} ${VERSION} já está instalado. Selecione a operação que deseja realizar e clique em Seguinte para continuar."
LangString appRunning ${LANG_PORTUGUESE} "${PRODUCTNAME} está em execução! Por favor, feche-o primeiro e tente novamente."
LangString appRunningOkKill ${LANG_PORTUGUESE} "${PRODUCTNAME} está em execução!$\nClique em OK para encerrá-lo."
LangString chooseMaintenanceOption ${LANG_PORTUGUESE} "Escolha a opção de manutenção a realizar."
LangString choowHowToInstall ${LANG_PORTUGUESE} "Escolha como deseja instalar o ${PRODUCTNAME}."
LangString createDesktop ${LANG_PORTUGUESE} "Criar atalho no ambiente de trabalho"
LangString dontUninstall ${LANG_PORTUGUESE} "Não desinstalar"
LangString dontUninstallDowngrade ${LANG_PORTUGUESE} "Não desinstalar (Instalar uma versão anterior sem desinstalar está desativado neste instalador)"
LangString failedToKillApp ${LANG_PORTUGUESE} "Falha ao encerrar ${PRODUCTNAME}. Por favor, feche-o primeiro e tente novamente."
LangString installingWebview2 ${LANG_PORTUGUESE} "A instalar WebView2..."
LangString newerVersionInstalled ${LANG_PORTUGUESE} "Uma versão mais recente do ${PRODUCTNAME} já está instalada! Não é recomendada a instalação de uma versão mais antiga. Se realmente deseja instalar esta versão mais antiga, é melhor desinstalar a versão atual primeiro. Selecione a operação que deseja realizar e clique em Seguinte para continuar."
LangString older ${LANG_PORTUGUESE} "mais antiga"
LangString olderOrUnknownVersionInstalled ${LANG_PORTUGUESE} "Uma versão $R4 do ${PRODUCTNAME} está instalada no sistema. Recomenda-se desinstalar a versão atual antes de instalar. Selecione a operação que deseja realizar e clique em Seguinte para continuar."
LangString silentDowngrades ${LANG_PORTUGUESE} "Rebaixamentos estão desativados neste instalador, não é possível prosseguir com a instalação silenciosa. Por favor, utilize o instalador com interface gráfica.$\n"
LangString unableToUninstall ${LANG_PORTUGUESE} "Não foi possível desinstalar!"
LangString uninstallApp ${LANG_PORTUGUESE} "Desinstalar ${PRODUCTNAME}"
LangString uninstallBeforeInstalling ${LANG_PORTUGUESE} "Desinstalar antes de instalar"
LangString unknown ${LANG_PORTUGUESE} "desconhecida"
LangString webview2AbortError ${LANG_PORTUGUESE} "Falha ao instalar o WebView2! A aplicação não pode ser executada sem ele. Tente reiniciar o instalador."
LangString webview2DownloadError ${LANG_PORTUGUESE} "Erro: Falha ao transferir o WebView2 - $0"
LangString webview2DownloadSuccess ${LANG_PORTUGUESE} "Bootstrapper do WebView2 transferido com sucesso"
LangString webview2Downloading ${LANG_PORTUGUESE} "A transferir o Bootstrapper do WebView2..."
LangString webview2InstallError ${LANG_PORTUGUESE} "Erro: Instalação do WebView2 falhou com o código $1"
LangString webview2InstallSuccess ${LANG_PORTUGUESE} "WebView2 instalado com sucesso"
LangString deleteAppData ${LANG_PORTUGUESE} "Eliminar os dados da aplicação"

View File

@@ -0,0 +1,27 @@
LangString addOrReinstall ${LANG_UKRAINIAN} "Додати/Перевстановити компоненти"
LangString alreadyInstalled ${LANG_UKRAINIAN} "Вже встановлено"
LangString alreadyInstalledLong ${LANG_UKRAINIAN} "${PRODUCTNAME} ${VERSION} вже встановлено. Виберіть дію, яку ви хочете виконати, і натисніть Далі, щоб продовжити."
LangString appRunning ${LANG_UKRAINIAN} "${PRODUCTNAME} запущено! Будь ласка, спочатку закрийте його, а потім спробуйте ще раз."
LangString appRunningOkKill ${LANG_UKRAINIAN} "${PRODUCTNAME} запущено!$\nНатисніть ОК, щоб примусово закрити його"
LangString chooseMaintenanceOption ${LANG_UKRAINIAN} "Виберіть дію, яку треба виконати."
LangString choowHowToInstall ${LANG_UKRAINIAN} "Виберіть, як ви хочете встановити ${PRODUCTNAME}."
LangString createDesktop ${LANG_UKRAINIAN} "Створити ярлик на робочому столі"
LangString dontUninstall ${LANG_UKRAINIAN} "Не видаляти"
LangString dontUninstallDowngrade ${LANG_UKRAINIAN} "Не видаляти (для цього встановлювача вимкнено зниження версії без видалення)"
LangString failedToKillApp ${LANG_UKRAINIAN} "Не вдалося примусово закрити ${PRODUCTNAME}. Будь ласка, спочатку закрийте його, а потім спробуйте ще раз"
LangString installingWebview2 ${LANG_UKRAINIAN} "Встановлення WebView2..."
LangString newerVersionInstalled ${LANG_UKRAINIAN} "Новіша версія ${PRODUCTNAME} вже встановлена! Встановлювати старішу версію не рекомендується. Якщо ви дійсно хочете встановити цю версію, краще спочатку видаліть поточну. Виберіть дію, яку ви хочете виконати, і натисніть Далі, щоб продовжити."
LangString older ${LANG_UKRAINIAN} "старішу"
LangString olderOrUnknownVersionInstalled ${LANG_UKRAINIAN} "У вашій системі вже встановлено $R4 версію ${PRODUCTNAME}. Рекомендується видалити поточну версію перед встановленням. Виберіть дію, яку ви хочете виконати, і натисніть Далі, щоб продовжити."
LangString silentDowngrades ${LANG_UKRAINIAN} "Для цього встановлювача вимкнено зниження версій. Неможливо продовжити роботу з фоновим встановлювачем. Будь ласка, скористайтеся встановлювачем з графічним інтерфейсом.$\n"
LangString unableToUninstall ${LANG_UKRAINIAN} "Не вдалося видалити!"
LangString uninstallApp ${LANG_UKRAINIAN} "Видалити ${PRODUCTNAME}"
LangString uninstallBeforeInstalling ${LANG_UKRAINIAN} "Видалити перед встановленням"
LangString unknown ${LANG_UKRAINIAN} "невідому"
LangString webview2AbortError ${LANG_UKRAINIAN} "Не вдалося встановити WebView2! Без нього програма не може працювати. Спробуйте перезапустити встановлювач."
LangString webview2DownloadError ${LANG_UKRAINIAN} "Помилка: не вдалося завантажити WebView2 - $0"
LangString webview2DownloadSuccess ${LANG_UKRAINIAN} "WebView2 успішно завантажено"
LangString webview2Downloading ${LANG_UKRAINIAN} "Завантаження WebView2..."
LangString webview2InstallError ${LANG_UKRAINIAN} "Помилка: не вдалося встановити WebView2, код виходу - $1"
LangString webview2InstallSuccess ${LANG_UKRAINIAN} "WebView2 успішно встановлено "
LangString deleteAppData ${LANG_UKRAINIAN} "Видалити дані програми"

View File

@@ -2,17 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::bundle::settings::Arch;
use crate::bundle::windows::sign::{sign_command, try_sign};
use crate::{
bundle::{
common::CommandExt,
windows::util::{
download_and_verify, download_webview2_bootstrapper, download_webview2_offline_installer,
verify_file_hash, HashAlgorithm, NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME,
settings::Arch,
windows::{
sign::{sign_command, try_sign},
util::{
download_webview2_bootstrapper, download_webview2_offline_installer,
NSIS_OUTPUT_FOLDER_NAME, NSIS_UPDATER_OUTPUT_FOLDER_NAME,
},
},
},
utils::{
http_utils::{download_and_verify, verify_file_hash, HashAlgorithm},
CommandExt,
},
Settings,
};
use tauri_utils::display_path;
@@ -35,8 +39,8 @@ const NSIS_URL: &str =
#[cfg(target_os = "windows")]
const NSIS_SHA1: &str = "057e83c7d82462ec394af76c87d06733605543d4";
const NSIS_TAURI_UTILS_URL: &str =
"https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.4.1/nsis_tauri_utils.dll";
const NSIS_TAURI_UTILS_SHA1: &str = "F99A50209A345185A84D34D0E5F66D04C75FF52F";
"https://github.com/tauri-apps/nsis-tauri-utils/releases/download/nsis_tauri_utils-v0.4.2/nsis_tauri_utils.dll";
const NSIS_TAURI_UTILS_SHA1: &str = "6532DA4545864C6EC95F62F27F2199BFD668560B";
#[cfg(target_os = "windows")]
const NSIS_REQUIRED_FILES: &[&str] = &[
@@ -90,8 +94,8 @@ pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<P
if !mismatched.is_empty() {
log::warn!("NSIS directory contains mis-hashed files. Redownloading them.");
for (path, url, hash, hash_algorithim) in mismatched {
let data = download_and_verify(url, hash, *hash_algorithim)?;
for (path, url, hash, hash_algorithm) in mismatched {
let data = download_and_verify(url, hash, *hash_algorithm)?;
fs::write(nsis_toolset_path.join(path), data)?;
}
}
@@ -108,7 +112,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
{
let data = download_and_verify(NSIS_URL, NSIS_SHA1, HashAlgorithm::Sha1)?;
log::info!("extracting NSIS");
crate::bundle::windows::util::extract_zip(&data, _tauri_tools_path)?;
crate::utils::http_utils::extract_zip(&data, _tauri_tools_path)?;
fs::rename(_tauri_tools_path.join("nsis-3.08"), nsis_toolset_path)?;
}
@@ -127,7 +131,7 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
Ok(())
}
fn add_build_number_if_needed(version_str: &str) -> anyhow::Result<String> {
fn try_add_numeric_build_number(version_str: &str) -> anyhow::Result<String> {
let version = semver::Version::parse(version_str).context("invalid app version")?;
if !version.build.is_empty() {
let build = version.build.parse::<u64>();
@@ -137,7 +141,10 @@ fn add_build_number_if_needed(version_str: &str) -> anyhow::Result<String> {
version.major, version.minor, version.patch, version.build
));
} else {
anyhow::bail!("optional build metadata in app version must be numeric-only");
log::warn!(
"Unable to parse version build metadata. Numeric value expected, received: `{}`. This will be replaced with `0` in `VIProductVersion` because Windows requires this field to be numeric.",
version.build
);
}
}
@@ -146,6 +153,7 @@ fn add_build_number_if_needed(version_str: &str) -> anyhow::Result<String> {
version.major, version.minor, version.patch,
))
}
fn build_nsis_app_installer(
settings: &Settings,
_nsis_toolset_path: &Path,
@@ -210,7 +218,7 @@ fn build_nsis_app_installer(
data.insert("version", to_json(version));
data.insert(
"version_with_build",
to_json(add_build_number_if_needed(version)?),
to_json(try_add_numeric_build_number(version)?),
);
data.insert(
@@ -591,10 +599,24 @@ fn association_description(
type ResourcesMap = BTreeMap<PathBuf, (PathBuf, PathBuf)>;
fn generate_resource_data(settings: &Settings) -> crate::Result<ResourcesMap> {
let mut resources = ResourcesMap::new();
let cwd = std::env::current_dir()?;
let mut added_resources = Vec::new();
// Adding WebViewer2Loader.dll in case windows-gnu toolchain is used
if settings.target().ends_with("-gnu") {
let loader_path =
dunce::simplified(&settings.project_out_directory().join("WebView2Loader.dll")).to_path_buf();
if loader_path.exists() {
added_resources.push(loader_path.clone());
resources.insert(
loader_path,
(PathBuf::new(), PathBuf::from("WebView2Loader.dll")),
);
}
}
for resource in settings.resource_files().iter() {
let resource = resource?;
@@ -697,6 +719,8 @@ fn get_lang_data(lang: &str) -> Option<(String, &[u8])> {
"persian" => include_bytes!("./languages/Persian.nsh"),
"turkish" => include_bytes!("./languages/Turkish.nsh"),
"swedish" => include_bytes!("./languages/Swedish.nsh"),
"portuguese" => include_bytes!("./languages/Portuguese.nsh"),
"ukrainian" => include_bytes!("./languages/Ukrainian.nsh"),
_ => return None,
};
Some((path, content))

View File

@@ -6,7 +6,7 @@
use crate::bundle::settings::CustomSignCommandSettings;
#[cfg(windows)]
use crate::bundle::windows::util;
use crate::{bundle::common::CommandExt, Settings};
use crate::{utils::CommandExt, Settings};
#[cfg(windows)]
use std::path::PathBuf;
#[cfg(windows)]

View File

@@ -3,15 +3,13 @@
// SPDX-License-Identifier: MIT
use std::{
fs::{create_dir_all, File},
io::{Cursor, Read, Write},
fs::create_dir_all,
path::{Path, PathBuf},
};
use regex::Regex;
use sha2::Digest;
use url::Url;
use zip::ZipArchive;
use ureq::ResponseExt;
use crate::utils::http_utils::download;
pub const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703";
pub const WEBVIEW2_OFFLINE_INSTALLER_X86_URL: &str =
@@ -26,9 +24,12 @@ pub const WIX_OUTPUT_FOLDER_NAME: &str = "msi";
pub const WIX_UPDATER_OUTPUT_FOLDER_NAME: &str = "msi-updater";
pub fn webview2_guid_path(url: &str) -> crate::Result<(String, String)> {
let agent = ureq::AgentBuilder::new().try_proxy_from_env(true).build();
let agent: ureq::Agent = ureq::Agent::config_builder()
.proxy(ureq::Proxy::try_from_env())
.build()
.into();
let response = agent.head(url).call().map_err(Box::new)?;
let final_url = response.get_url();
let final_url = response.get_uri().to_string();
let remaining_url = final_url.strip_prefix(WEBVIEW2_URL_PREFIX).ok_or_else(|| {
anyhow::anyhow!(
"WebView2 URL prefix mismatch. Expected `{}`, found `{}`.",
@@ -69,148 +70,6 @@ pub fn download_webview2_offline_installer(base_path: &Path, arch: &str) -> crat
Ok(file_path)
}
fn generate_github_mirror_url_from_template(github_url: &str) -> Option<String> {
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE")
.ok()
.and_then(|template| {
let re =
Regex::new(r"https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(.*)").unwrap();
re.captures(github_url).map(|caps| {
template
.replace("<owner>", &caps[1])
.replace("<repo>", &caps[2])
.replace("<version>", &caps[3])
.replace("<asset>", &caps[4])
})
})
}
fn generate_github_mirror_url_from_base(github_url: &str) -> Option<String> {
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR")
.ok()
.and_then(|cdn| Url::parse(&cdn).ok())
.map(|mut cdn| {
cdn.set_path(github_url);
cdn.to_string()
})
}
fn generate_github_alternative_url(url: &str) -> Option<(ureq::Agent, String)> {
if !url.starts_with("https://github.com/") {
return None;
}
generate_github_mirror_url_from_template(url)
.or_else(|| generate_github_mirror_url_from_base(url))
.map(|alt_url| (ureq::AgentBuilder::new().build(), alt_url))
}
fn create_agent_and_url(url: &str) -> (ureq::Agent, String) {
generate_github_alternative_url(url).unwrap_or((
ureq::AgentBuilder::new().try_proxy_from_env(true).build(),
url.to_owned(),
))
}
pub fn download(url: &str) -> crate::Result<Vec<u8>> {
let (agent, final_url) = create_agent_and_url(url);
log::info!(action = "Downloading"; "{}", final_url);
let response = agent.get(&final_url).call().map_err(Box::new)?;
let mut bytes = Vec::new();
response.into_reader().read_to_end(&mut bytes)?;
Ok(bytes)
}
#[derive(Clone, Copy)]
pub enum HashAlgorithm {
#[cfg(target_os = "windows")]
Sha256,
Sha1,
}
/// Function used to download a file and checks SHA256 to verify the download.
pub fn download_and_verify(
url: &str,
hash: &str,
hash_algorithm: HashAlgorithm,
) -> crate::Result<Vec<u8>> {
let data = download(url)?;
log::info!("validating hash");
verify_hash(&data, hash, hash_algorithm)?;
Ok(data)
}
pub fn verify_hash(data: &[u8], hash: &str, hash_algorithm: HashAlgorithm) -> crate::Result<()> {
match hash_algorithm {
#[cfg(target_os = "windows")]
HashAlgorithm::Sha256 => {
let hasher = sha2::Sha256::new();
verify_data_with_hasher(data, hash, hasher)
}
HashAlgorithm::Sha1 => {
let hasher = sha1::Sha1::new();
verify_data_with_hasher(data, hash, hasher)
}
}
}
fn verify_data_with_hasher(data: &[u8], hash: &str, mut hasher: impl Digest) -> crate::Result<()> {
hasher.update(data);
let url_hash = hasher.finalize().to_vec();
let expected_hash = hex::decode(hash)?;
if expected_hash == url_hash {
Ok(())
} else {
Err(crate::Error::HashError)
}
}
pub fn verify_file_hash<P: AsRef<Path>>(
path: P,
hash: &str,
hash_algorithm: HashAlgorithm,
) -> crate::Result<()> {
let data = std::fs::read(path)?;
verify_hash(&data, hash, hash_algorithm)
}
/// Extracts the zips from memory into a usable path.
#[allow(dead_code)]
pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> {
let cursor = Cursor::new(data);
let mut zipa = ZipArchive::new(cursor)?;
for i in 0..zipa.len() {
let mut file = zipa.by_index(i)?;
if let Some(name) = file.enclosed_name() {
let dest_path = path.join(name);
if file.is_dir() {
create_dir_all(&dest_path)?;
continue;
}
let parent = dest_path.parent().expect("Failed to get parent");
if !parent.exists() {
create_dir_all(parent)?;
}
let mut buff: Vec<u8> = Vec::new();
file.read_to_end(&mut buff)?;
let mut fileout = File::create(dest_path).expect("Failed to open file");
fileout.write_all(&buff)?;
}
}
Ok(())
}
#[cfg(target_os = "windows")]
pub fn os_bitness<'a>() -> Option<&'a str> {
use windows_sys::Win32::System::SystemInformation::{
@@ -225,57 +84,3 @@ pub fn os_bitness<'a>() -> Option<&'a str> {
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::generate_github_mirror_url_from_template;
use std::env;
const GITHUB_ASSET_URL: &str =
"https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
const NON_GITHUB_ASSET_URL: &str = "https://someotherwebsite.com/somefile.zip";
#[test]
fn test_generate_mirror_url_no_env_var() {
env::remove_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE");
assert!(generate_github_mirror_url_from_template(GITHUB_ASSET_URL).is_none());
}
#[test]
fn test_generate_mirror_url_non_github_url() {
env::set_var(
"TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE",
"https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
);
assert!(generate_github_mirror_url_from_template(NON_GITHUB_ASSET_URL).is_none());
}
struct TestCase {
template: &'static str,
expected_url: &'static str,
}
#[test]
fn test_generate_mirror_url_correctly() {
let test_cases = vec![
TestCase {
template: "https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
expected_url: "https://mirror.example.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip",
},
TestCase {
template: "https://mirror.example.com/<asset>",
expected_url: "https://mirror.example.com/wix311-binaries.zip",
},
];
for case in test_cases {
env::set_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE", case.template);
assert_eq!(
generate_github_mirror_url_from_template(GITHUB_ASSET_URL),
Some(case.expected_url.to_string())
);
}
}
}

View File

@@ -3,8 +3,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
//!
//! The Tauri bundler is a tool that generates installers or app bundles for executables.
//! It supports auto updating through [tauri](https://docs.rs/tauri).
//!
@@ -25,5 +23,6 @@
/// The bundle API.
pub mod bundle;
mod error;
mod utils;
pub use bundle::*;
pub use error::{Error, Result};

View File

@@ -4,28 +4,11 @@
// SPDX-License-Identifier: MIT
use std::{
ffi::OsStr,
fs::{self, File},
io::{self, BufRead, BufReader, BufWriter},
io::{self, BufWriter},
path::Path,
process::{Command, ExitStatus, Output, Stdio},
sync::{Arc, Mutex},
};
/// Returns true if the path has a filename indicating that it is a high-density
/// "retina" icon. Specifically, returns true the file stem ends with
/// "@2x" (a convention specified by the [Apple developer docs](
/// <https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html>)).
#[allow(dead_code)]
pub fn is_retina<P: AsRef<Path>>(path: P) -> bool {
path
.as_ref()
.file_stem()
.and_then(OsStr::to_str)
.map(|stem| stem.ends_with("@2x"))
.unwrap_or(false)
}
/// Creates a new file at the given path, creating any parent directories as
/// needed.
pub fn create_file(path: &Path) -> crate::Result<BufWriter<File>> {
@@ -36,6 +19,36 @@ pub fn create_file(path: &Path) -> crate::Result<BufWriter<File>> {
Ok(BufWriter::new(file))
}
/// Creates the given directory path,
/// erasing it first if specified.
#[allow(dead_code)]
pub fn create_dir(path: &Path, erase: bool) -> crate::Result<()> {
if erase && path.exists() {
remove_dir_all(path)?;
}
Ok(fs::create_dir(path)?)
}
/// Creates all of the directories of the specified path,
/// erasing it first if specified.
#[allow(dead_code)]
pub fn create_dir_all(path: &Path, erase: bool) -> crate::Result<()> {
if erase && path.exists() {
remove_dir_all(path)?;
}
Ok(fs::create_dir_all(path)?)
}
/// Removes the directory and its contents if it exists.
#[allow(dead_code)]
pub fn remove_dir_all(path: &Path) -> crate::Result<()> {
if path.exists() {
Ok(fs::remove_dir_all(path)?)
} else {
Ok(())
}
}
/// Makes a symbolic link to a directory.
#[cfg(unix)]
#[allow(dead_code)]
@@ -63,11 +76,9 @@ fn symlink_file(src: &Path, dst: &Path) -> io::Result<()> {
}
/// Copies a regular file from one path to another, creating any parent
/// directories of the destination path as necessary. Fails if the source path
/// directories of the destination path as necessary. Fails if the source path
/// is a directory or doesn't exist.
pub fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> crate::Result<()> {
let from = from.as_ref();
let to = to.as_ref();
pub fn copy_file(from: &Path, to: &Path) -> crate::Result<()> {
if !from.exists() {
return Err(crate::Error::GenericError(format!(
"{from:?} does not exist"
@@ -151,7 +162,7 @@ pub fn copy_custom_files(
pkg_path
};
if path.is_file() {
copy_file(path, data_dir.join(pkg_path))?;
copy_file(path, &data_dir.join(pkg_path))?;
} else {
copy_dir(path, &data_dir.join(pkg_path))?;
}
@@ -159,93 +170,10 @@ pub fn copy_custom_files(
Ok(())
}
pub trait CommandExt {
// The `pipe` function sets the stdout and stderr to properly
// show the command output in the Node.js wrapper.
fn piped(&mut self) -> std::io::Result<ExitStatus>;
fn output_ok(&mut self) -> crate::Result<Output>;
}
impl CommandExt for Command {
fn piped(&mut self) -> std::io::Result<ExitStatus> {
self.stdin(os_pipe::dup_stdin()?);
self.stdout(os_pipe::dup_stdout()?);
self.stderr(os_pipe::dup_stderr()?);
let program = self.get_program().to_string_lossy().into_owned();
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
self.status().map_err(Into::into)
}
fn output_ok(&mut self) -> crate::Result<Output> {
let program = self.get_program().to_string_lossy().into_owned();
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
self.stdout(Stdio::piped());
self.stderr(Stdio::piped());
let mut child = self.spawn()?;
let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
let stdout_lines = Arc::new(Mutex::new(Vec::new()));
let stdout_lines_ = stdout_lines.clone();
std::thread::spawn(move || {
let mut line = String::new();
let mut lines = stdout_lines_.lock().unwrap();
loop {
line.clear();
match stdout.read_line(&mut line) {
Ok(0) => break,
Ok(_) => {
log::debug!(action = "stdout"; "{}", line.trim_end());
lines.extend(line.as_bytes().to_vec());
}
Err(_) => (),
}
}
});
let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
let stderr_lines_ = stderr_lines.clone();
std::thread::spawn(move || {
let mut line = String::new();
let mut lines = stderr_lines_.lock().unwrap();
loop {
line.clear();
match stderr.read_line(&mut line) {
Ok(0) => break,
Ok(_) => {
log::debug!(action = "stderr"; "{}", line.trim_end());
lines.extend(line.as_bytes().to_vec());
}
Err(_) => (),
}
}
});
let status = child.wait()?;
let output = Output {
status,
stdout: std::mem::take(&mut *stdout_lines.lock().unwrap()),
stderr: std::mem::take(&mut *stderr_lines.lock().unwrap()),
};
if output.status.success() {
Ok(output)
} else {
Err(crate::Error::GenericError(format!(
"failed to run {program}"
)))
}
}
}
#[cfg(test)]
mod tests {
use super::{create_file, is_retina};
use std::{io::Write, path::PathBuf};
use tauri_utils::resources::resource_relpath;
use super::create_file;
use std::io::Write;
#[test]
fn create_file_with_parent_dirs() {
@@ -263,6 +191,8 @@ mod tests {
#[cfg(not(windows))]
#[test]
fn copy_dir_with_symlinks() {
use std::path::PathBuf;
// Create a directory structure that looks like this:
// ${TMP}/orig/
// sub/
@@ -310,26 +240,4 @@ mod tests {
b"Hello, world!\n"
);
}
#[test]
fn retina_icon_paths() {
assert!(!is_retina("data/icons/512x512.png"));
assert!(is_retina("data/icons/512x512@2x.png"));
}
#[test]
fn resource_relative_paths() {
assert_eq!(
resource_relpath(&PathBuf::from("./data/images/button.png")),
PathBuf::from("data/images/button.png")
);
assert_eq!(
resource_relpath(&PathBuf::from("../../images/wheel.png")),
PathBuf::from("_up_/_up_/images/wheel.png")
);
assert_eq!(
resource_relpath(&PathBuf::from("/home/ferris/crab.png")),
PathBuf::from("_root_/home/ferris/crab.png")
);
}
}

View File

@@ -0,0 +1,219 @@
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{
fs::{create_dir_all, File},
io::{Cursor, Read, Write},
path::Path,
};
use regex::Regex;
use sha2::Digest;
use url::Url;
use zip::ZipArchive;
fn generate_github_mirror_url_from_template(github_url: &str) -> Option<String> {
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE")
.ok()
.and_then(|template| {
let re =
Regex::new(r"https://github.com/([^/]+)/([^/]+)/releases/download/([^/]+)/(.*)").unwrap();
re.captures(github_url).map(|caps| {
template
.replace("<owner>", &caps[1])
.replace("<repo>", &caps[2])
.replace("<version>", &caps[3])
.replace("<asset>", &caps[4])
})
})
}
fn generate_github_mirror_url_from_base(github_url: &str) -> Option<String> {
std::env::var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR")
.ok()
.and_then(|cdn| Url::parse(&cdn).ok())
.map(|mut cdn| {
cdn.set_path(github_url);
cdn.to_string()
})
}
fn generate_github_alternative_url(url: &str) -> Option<(ureq::Agent, String)> {
if !url.starts_with("https://github.com/") {
return None;
}
generate_github_mirror_url_from_template(url)
.or_else(|| generate_github_mirror_url_from_base(url))
.map(|alt_url| (ureq::agent(), alt_url))
}
fn create_agent_and_url(url: &str) -> (ureq::Agent, String) {
generate_github_alternative_url(url).unwrap_or((
ureq::Agent::config_builder()
.proxy(ureq::Proxy::try_from_env())
.build()
.into(),
url.to_owned(),
))
}
#[allow(dead_code)]
pub fn download(url: &str) -> crate::Result<Vec<u8>> {
let (agent, final_url) = create_agent_and_url(url);
log::info!(action = "Downloading"; "{}", final_url);
let response = agent.get(&final_url).call().map_err(Box::new)?;
let mut bytes = Vec::new();
response.into_body().into_reader().read_to_end(&mut bytes)?;
Ok(bytes)
}
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum HashAlgorithm {
#[cfg(target_os = "windows")]
Sha256,
Sha1,
}
/// Function used to download a file and checks SHA256 to verify the download.
#[allow(dead_code)]
pub fn download_and_verify(
url: &str,
hash: &str,
hash_algorithm: HashAlgorithm,
) -> crate::Result<Vec<u8>> {
let data = download(url)?;
log::info!("validating hash");
verify_hash(&data, hash, hash_algorithm)?;
Ok(data)
}
#[allow(dead_code)]
pub fn verify_hash(data: &[u8], hash: &str, hash_algorithm: HashAlgorithm) -> crate::Result<()> {
match hash_algorithm {
#[cfg(target_os = "windows")]
HashAlgorithm::Sha256 => {
let hasher = sha2::Sha256::new();
verify_data_with_hasher(data, hash, hasher)
}
HashAlgorithm::Sha1 => {
let hasher = sha1::Sha1::new();
verify_data_with_hasher(data, hash, hasher)
}
}
}
fn verify_data_with_hasher(data: &[u8], hash: &str, mut hasher: impl Digest) -> crate::Result<()> {
hasher.update(data);
let url_hash = hasher.finalize().to_vec();
let expected_hash = hex::decode(hash)?;
if expected_hash == url_hash {
Ok(())
} else {
Err(crate::Error::HashError)
}
}
#[allow(dead_code)]
pub fn verify_file_hash<P: AsRef<Path>>(
path: P,
hash: &str,
hash_algorithm: HashAlgorithm,
) -> crate::Result<()> {
let data = std::fs::read(path)?;
verify_hash(&data, hash, hash_algorithm)
}
/// Extracts the zips from memory into a usable path.
#[allow(dead_code)]
pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> {
let cursor = Cursor::new(data);
let mut zipa = ZipArchive::new(cursor)?;
for i in 0..zipa.len() {
let mut file = zipa.by_index(i)?;
if let Some(name) = file.enclosed_name() {
let dest_path = path.join(name);
if file.is_dir() {
create_dir_all(&dest_path)?;
continue;
}
let parent = dest_path.parent().expect("Failed to get parent");
if !parent.exists() {
create_dir_all(parent)?;
}
let mut buff: Vec<u8> = Vec::new();
file.read_to_end(&mut buff)?;
let mut fileout = File::create(dest_path).expect("Failed to open file");
fileout.write_all(&buff)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::generate_github_mirror_url_from_template;
use std::env;
const GITHUB_ASSET_URL: &str =
"https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
const NON_GITHUB_ASSET_URL: &str = "https://someotherwebsite.com/somefile.zip";
#[test]
fn test_generate_mirror_url_no_env_var() {
env::remove_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE");
assert!(generate_github_mirror_url_from_template(GITHUB_ASSET_URL).is_none());
}
#[test]
fn test_generate_mirror_url_non_github_url() {
env::set_var(
"TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE",
"https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
);
assert!(generate_github_mirror_url_from_template(NON_GITHUB_ASSET_URL).is_none());
}
struct TestCase {
template: &'static str,
expected_url: &'static str,
}
#[test]
fn test_generate_mirror_url_correctly() {
let test_cases = vec![
TestCase {
template: "https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>",
expected_url: "https://mirror.example.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip",
},
TestCase {
template: "https://mirror.example.com/<asset>",
expected_url: "https://mirror.example.com/wix311-binaries.zip",
},
];
for case in test_cases {
env::set_var("TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE", case.template);
assert_eq!(
generate_github_mirror_url_from_template(GITHUB_ASSET_URL),
Some(case.expected_url.to_string())
);
}
}
}

View File

@@ -0,0 +1,141 @@
// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::{
ffi::OsStr,
io::{BufRead, BufReader},
path::Path,
process::{Command, ExitStatus, Output, Stdio},
sync::{Arc, Mutex},
};
pub mod fs_utils;
pub mod http_utils;
/// Returns true if the path has a filename indicating that it is a high-density
/// "retina" icon. Specifically, returns true the file stem ends with
/// "@2x" (a convention specified by the [Apple developer docs](
/// <https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html>)).
#[allow(dead_code)]
pub fn is_retina(path: &Path) -> bool {
path
.file_stem()
.and_then(OsStr::to_str)
.map(|stem| stem.ends_with("@2x"))
.unwrap_or(false)
}
pub trait CommandExt {
// The `pipe` function sets the stdout and stderr to properly
// show the command output in the Node.js wrapper.
fn piped(&mut self) -> std::io::Result<ExitStatus>;
fn output_ok(&mut self) -> crate::Result<Output>;
}
impl CommandExt for Command {
fn piped(&mut self) -> std::io::Result<ExitStatus> {
self.stdin(os_pipe::dup_stdin()?);
self.stdout(os_pipe::dup_stdout()?);
self.stderr(os_pipe::dup_stderr()?);
let program = self.get_program().to_string_lossy().into_owned();
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
self.status()
}
fn output_ok(&mut self) -> crate::Result<Output> {
let program = self.get_program().to_string_lossy().into_owned();
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
self.stdout(Stdio::piped());
self.stderr(Stdio::piped());
let mut child = self.spawn()?;
let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
let stdout_lines = Arc::new(Mutex::new(Vec::new()));
let stdout_lines_ = stdout_lines.clone();
std::thread::spawn(move || {
let mut line = String::new();
let mut lines = stdout_lines_.lock().unwrap();
loop {
line.clear();
match stdout.read_line(&mut line) {
Ok(0) => break,
Ok(_) => {
log::debug!(action = "stdout"; "{}", line.trim_end());
lines.extend(line.as_bytes().to_vec());
}
Err(_) => (),
}
}
});
let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
let stderr_lines_ = stderr_lines.clone();
std::thread::spawn(move || {
let mut line = String::new();
let mut lines = stderr_lines_.lock().unwrap();
loop {
line.clear();
match stderr.read_line(&mut line) {
Ok(0) => break,
Ok(_) => {
log::debug!(action = "stderr"; "{}", line.trim_end());
lines.extend(line.as_bytes().to_vec());
}
Err(_) => (),
}
}
});
let status = child.wait()?;
let output = Output {
status,
stdout: std::mem::take(&mut *stdout_lines.lock().unwrap()),
stderr: std::mem::take(&mut *stderr_lines.lock().unwrap()),
};
if output.status.success() {
Ok(output)
} else {
Err(crate::Error::GenericError(format!(
"failed to run {program}"
)))
}
}
}
#[cfg(test)]
mod tests {
use std::path::{Path, PathBuf};
use tauri_utils::resources::resource_relpath;
use super::is_retina;
#[test]
fn retina_icon_paths() {
assert!(!is_retina(Path::new("data/icons/512x512.png")));
assert!(is_retina(Path::new("data/icons/512x512@2x.png")));
}
#[test]
fn resource_relative_paths() {
assert_eq!(
resource_relpath(Path::new("./data/images/button.png")),
PathBuf::from("data/images/button.png")
);
assert_eq!(
resource_relpath(Path::new("../../images/wheel.png")),
PathBuf::from("_up_/_up_/images/wheel.png")
);
assert_eq!(
resource_relpath(Path::new("/home/ferris/crab.png")),
PathBuf::from("_root_/home/ferris/crab.png")
);
}
}

View File

@@ -1,5 +1,127 @@
# Changelog
## \[2.3.0]
### Enhancements
- [`a2d36b8c3`](https://www.github.com/tauri-apps/tauri/commit/a2d36b8c34a8dcfc6736797ca5cd4665faf75e7e) ([#12181](https://www.github.com/tauri-apps/tauri/pull/12181) by [@bastiankistner](https://www.github.com/tauri-apps/tauri/../../bastiankistner)) Add an option to change the default background throttling policy (currently for WebKit only).
- [`6e417c943`](https://www.github.com/tauri-apps/tauri/commit/6e417c9435d8fae0eca9e8d42d6215e887a28d98) ([#12786](https://www.github.com/tauri-apps/tauri/pull/12786) by [@kandrelczyk](https://www.github.com/tauri-apps/tauri/../../kandrelczyk)) Added RPM to the list of package types for which signature file will be generated.
### Dependencies
- Upgraded to `tauri-utils@2.2.0`
- Upgraded to `tauri-bundler@2.2.4`
- Upgraded to `tauri-macos-sign@2.1.0`
## \[2.2.7]
### Bug Fixes
- [`8e9134c4a`](https://www.github.com/tauri-apps/tauri/commit/8e9134c4a2047329be0dbb868b7ae061a9d3f190) ([#12511](https://www.github.com/tauri-apps/tauri/pull/12511) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused `tauri dev` to fail because of an incorrect `--bins` flag.
## \[2.2.6]
### Enhancements
- [`1a86974aa`](https://www.github.com/tauri-apps/tauri/commit/1a86974aa3d09957c6b1142a17bbfed9998798fd) ([#12406](https://www.github.com/tauri-apps/tauri/pull/12406) by [@bradleat](https://www.github.com/tauri-apps/tauri/../../bradleat)) `ios build --open` will now let xcode start the rust build process.
- [`9a30bed98`](https://www.github.com/tauri-apps/tauri/commit/9a30bed98c2d8501328006fad5840eb9d533e1c2) ([#12423](https://www.github.com/tauri-apps/tauri/pull/12423) by [@tr3ysmith](https://www.github.com/tauri-apps/tauri/../../tr3ysmith)) Added conditional logic to MacOS codesigning where only executables get the entitlements file when being signed. This solves an issue where the app may not launch when using 3rd party frameworks if certain entitlements are added. Ex: multicast support (must be applied for through apple developer, and the framework would not have that capability).
- [`0b79af711`](https://www.github.com/tauri-apps/tauri/commit/0b79af711430934362602fb950c3e4cb5b59cf9c) ([#12438](https://www.github.com/tauri-apps/tauri/pull/12438) by [@3lpsy](https://www.github.com/tauri-apps/tauri/../../3lpsy)) Log the command used to start the rust app in development.
### Bug Fixes
- [`bc43c738b`](https://www.github.com/tauri-apps/tauri/commit/bc43c738baf686353690d3d9259b4976881718c8) ([#12442](https://www.github.com/tauri-apps/tauri/pull/12442) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that prevented `tauri add` to work for the `clipboard-manager` plugin.
- [`27096cdc0`](https://www.github.com/tauri-apps/tauri/commit/27096cdc05d89b61b2372b4e4a3018c87f240ab8) ([#12445](https://www.github.com/tauri-apps/tauri/pull/12445) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused Tauri's CLI to enable tauri's `native-tls` feature even though it wasn't needed. Moved `reqwest` to a mobile-only dependency in `tauri` and enabled its `rustls-tls` feature flag.
### Dependencies
- Upgraded to `tauri-bundler@2.2.3`
## \[2.2.5]
### Dependencies
- Upgraded to `tauri-bundler@2.2.2`
## \[2.2.4]
### Bug Fixes
- [`cad550445`](https://www.github.com/tauri-apps/tauri/commit/cad5504455ffa53e297cebff473c113b1afa5d29) ([#12354](https://www.github.com/tauri-apps/tauri/pull/12354) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed and issue that caused `tauri add` to try to install incorrect npm packages.
## \[2.2.3]
### Enhancements
- [`a0f2c84d5`](https://www.github.com/tauri-apps/tauri/commit/a0f2c84d51f5086c5055867d6f61ea90c463a26c) ([#12204](https://www.github.com/tauri-apps/tauri/pull/12204) by [@pjf-dev](https://www.github.com/tauri-apps/tauri/../../pjf-dev)) Enhance `tauri icon` command by including 64x64 png size in default icon sizes.
### Bug Fixes
- [`98f62e65a`](https://www.github.com/tauri-apps/tauri/commit/98f62e65a27a375272c6b4d9f34c23e142b9d3a6) ([#12246](https://www.github.com/tauri-apps/tauri/pull/12246) by [@marcomq](https://www.github.com/tauri-apps/tauri/../../marcomq)) Properly add NPM packages for community plugins when using the `tauri add` command.
- [`b9a99a5c6`](https://www.github.com/tauri-apps/tauri/commit/b9a99a5c69d8a2a1a3ff30e500b46872258dca15) ([#12297](https://www.github.com/tauri-apps/tauri/pull/12297) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the built-in dev server to constantly refresh on Linux. This only affected users who do not have `devUrl` point to a URL.
- [`ef21ed9ac`](https://www.github.com/tauri-apps/tauri/commit/ef21ed9ac1c045c38b0c04e3d71a441694abc257) ([#12290](https://www.github.com/tauri-apps/tauri/pull/12290) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Fix iOS build failing when the development team contains spaces.
### Dependencies
- Upgraded to `tauri-bundler@2.2.1`
## \[2.2.2]
### Bug Fixes
- [`26fc9558f`](https://www.github.com/tauri-apps/tauri/commit/26fc9558fe7b2fe649f61926da88f36110dd5707) ([#12178](https://www.github.com/tauri-apps/tauri/pull/12178) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused the `tauri dev` file watcher to exit after detecting file changes.
## \[2.2.1]
### Bug Fixes
- [`881729448`](https://www.github.com/tauri-apps/tauri/commit/881729448c9abd0d0c7941a8a31c94119ce827af) ([#12164](https://www.github.com/tauri-apps/tauri/pull/12164) by [@FabianLars](https://www.github.com/tauri-apps/tauri/../../FabianLars)) Fixed an issue that caused `tauri dev` to crash before showing the app on Linux.
## \[2.2.0]
### New Features
- [`cccb308c7`](https://www.github.com/tauri-apps/tauri/commit/cccb308c7b559b0838138d6cea280665f060c925) ([#11562](https://www.github.com/tauri-apps/tauri/pull/11562) by [@jLynx](https://www.github.com/tauri-apps/tauri/../../jLynx)) Generate signature for `.deb` packages when `createUpdaterArtifacts` option is enabled.
- [`74212d40d`](https://www.github.com/tauri-apps/tauri/commit/74212d40d80dba4501b3d4ae30104fa3d447bdf9) ([#11653](https://www.github.com/tauri-apps/tauri/pull/11653) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Include Linux destkop environment and session type in `tauri info` command.
### Enhancements
- [`93a3a043d`](https://www.github.com/tauri-apps/tauri/commit/93a3a043d39cc96515d51d98beeb14261d3a246b) ([#11727](https://www.github.com/tauri-apps/tauri/pull/11727) by [@Kiyozz](https://www.github.com/tauri-apps/tauri/../../Kiyozz)) Add support for `Portuguese` language for NSIS windows installer.
### Bug Fixes
- [`c8700656b`](https://www.github.com/tauri-apps/tauri/commit/c8700656be3001a0cc6e087f23aebd482430a85b) ([#11985](https://www.github.com/tauri-apps/tauri/pull/11985) by [@ShaunSHamilton](https://www.github.com/tauri-apps/tauri/../../ShaunSHamilton)) Fix `tauri remove` from removing object type (`{}`) permissions.
- [`0ae06c5ca`](https://www.github.com/tauri-apps/tauri/commit/0ae06c5ca89cecd24154affdc69668f5e1e67d85) ([#11914](https://www.github.com/tauri-apps/tauri/pull/11914) by [@wtto00](https://www.github.com/tauri-apps/tauri/../../wtto00)) Fix the exclude path in file `Cargo.toml` of plugin template generated by cli. Path changed in [#9346](https://github.com/tauri-apps/tauri/pull/9346)
### Dependencies
- Upgraded to `tauri-bundler@2.2.0`
- Upgraded to `tauri-utils@2.1.1`
## \[2.1.0]
### New Features
- [`1b6b2cfaa`](https://www.github.com/tauri-apps/tauri/commit/1b6b2cfaa14ab1d418c676cedbf942a812377a30) ([#11521](https://www.github.com/tauri-apps/tauri/pull/11521) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Process `bundle > windows > wix > fragmentPaths` with Handlebars to interpolate expressions within it.
- [`6bf917941`](https://www.github.com/tauri-apps/tauri/commit/6bf917941ff0fcc49e86b3ba427340b75f3ce49c) ([#11322](https://www.github.com/tauri-apps/tauri/pull/11322) by [@ShaunSHamilton](https://www.github.com/tauri-apps/tauri/../../ShaunSHamilton)) Add `tauri remove` to remove plugins from projects.
- [`058c0db72`](https://www.github.com/tauri-apps/tauri/commit/058c0db72f43fbe1574d0db654560e693755cd7e) ([#11584](https://www.github.com/tauri-apps/tauri/pull/11584) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add `bundle > linux > rpm > compression` config option to control RPM bundle compression type and level.
### Enhancements
- [`1f311832a`](https://www.github.com/tauri-apps/tauri/commit/1f311832ab5b2d62a533dfcf9b1d78bddf249ae8) ([#11405](https://www.github.com/tauri-apps/tauri/pull/11405) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Add more context for errors when decoding secret and public keys for signing updater artifacts.
- [`e0d1307d3`](https://www.github.com/tauri-apps/tauri/commit/e0d1307d3f78987d0059921a5ab01ea4b26e0ef1) ([#11414](https://www.github.com/tauri-apps/tauri/pull/11414) by [@Czxck001](https://www.github.com/tauri-apps/tauri/../../Czxck001)) Migrate the `$schema` Tauri configuration to the v2 format.
- [`c43d5df15`](https://www.github.com/tauri-apps/tauri/commit/c43d5df15828ecffa606482ea2b60350c488c981) ([#11512](https://www.github.com/tauri-apps/tauri/pull/11512) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Associate a newly created capability file with the `main` window on the `tauri add` and `tauri permission add` commands.
### Bug Fixes
- [`7af01ff2c`](https://www.github.com/tauri-apps/tauri/commit/7af01ff2ce623d727cd13a4c8a549c1c80031882) ([#11523](https://www.github.com/tauri-apps/tauri/pull/11523) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix `tauri migrate` failing to install NPM depenencies when running from Deno.
- [`100a4455a`](https://www.github.com/tauri-apps/tauri/commit/100a4455aa48df508510bbc08273215bdf70c012) ([#11529](https://www.github.com/tauri-apps/tauri/pull/11529) by [@amrbashir](https://www.github.com/tauri-apps/tauri/../../amrbashir)) Fix detecting yarn berry (v2 and higher) in various tauri cli commands.
- [`60e86d5f6`](https://www.github.com/tauri-apps/tauri/commit/60e86d5f6e0f0c769d34ef368cd8801a918d796d) ([#11624](https://www.github.com/tauri-apps/tauri/pull/11624) by [@lucasfernog](https://www.github.com/tauri-apps/tauri/../../lucasfernog)) Use the public network IP address on `android dev` by default on Windows.
### Dependencies
- Upgraded to `tauri-utils@2.1.0`
- Upgraded to `tauri-bundler@2.1.0`
## \[2.0.4]
### Enhancements

View File

@@ -1,6 +1,6 @@
[package]
name = "tauri-cli"
version = "2.0.4"
version = "2.3.0"
authors = ["Tauri Programme within The Commons Conservancy"]
edition = "2021"
rust-version = "1.77.2"
@@ -36,28 +36,28 @@ name = "cargo-tauri"
path = "src/main.rs"
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
cargo-mobile2 = { version = "0.17.3", default-features = false }
cargo-mobile2 = { version = "0.17", default-features = false }
[dependencies]
jsonrpsee = { version = "0.24.5", features = ["server"] }
jsonrpsee-core = "0.24.5"
jsonrpsee-client-transport = { version = "0.24.5", features = ["ws"] }
jsonrpsee-ws-client = { version = "0.24.5", default-features = false }
jsonrpsee = { version = "0.24", features = ["server"] }
jsonrpsee-core = "0.24"
jsonrpsee-client-transport = { version = "0.24", features = ["ws"] }
jsonrpsee-ws-client = { version = "0.24", default-features = false }
sublime_fuzzy = "0.7"
clap_complete = "4"
clap = { version = "4.5", features = ["derive", "env"] }
anyhow = "1.0"
tauri-bundler = { version = "2.0.4", default-features = false, path = "../tauri-bundler" }
colored = "2.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
notify = "6.1"
notify-debouncer-mini = "0.4"
shared_child = "1.0"
clap = { version = "4", features = ["derive", "env"] }
anyhow = "1"
tauri-bundler = { version = "2.2.4", default-features = false, path = "../tauri-bundler" }
colored = "2"
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["preserve_order"] }
notify = "8"
notify-debouncer-full = "0.5"
shared_child = "1"
duct = "0.13"
toml_edit = { version = "0.22", features = ["serde"] }
json-patch = "2.0"
tauri-utils = { version = "2.0.1", path = "../tauri-utils", features = [
json-patch = "3"
tauri-utils = { version = "2.2.0", path = "../tauri-utils", features = [
"isolation",
"schema",
"config-json5",
@@ -70,41 +70,42 @@ tauri-utils-v1 = { version = "1", package = "tauri-utils", features = [
"config-toml",
] }
toml = "0.8"
jsonschema = "0.18"
handlebars = "6.0"
jsonschema = "0.29"
handlebars = "6"
include_dir = "0.7"
minisign = "=0.7.3"
base64 = "0.22.0"
ureq = { version = "2.9.6", default-features = false, features = ["gzip"] }
base64 = "0.22"
ureq = { version = "3", default-features = false, features = ["gzip"] }
os_info = "3"
semver = "1.0"
regex = "1.10.3"
semver = "1"
regex = "1"
heck = "0.5"
dialoguer = "0.11"
url = { version = "2.5", features = ["serde"] }
url = { version = "2", features = ["serde"] }
os_pipe = "1"
ignore = "0.4"
ctrlc = "3.4"
ctrlc = "3"
log = { version = "0.4.21", features = ["kv", "kv_std"] }
env_logger = "0.11.5"
env_logger = "0.11"
icns = { package = "tauri-icns", version = "0.1" }
image = { version = "0.25", default-features = false, features = ["ico"] }
axum = { version = "0.7.4", features = ["ws"] }
axum = { version = "0.7", features = ["ws"] }
html5ever = "0.26"
kuchiki = { package = "kuchikiki", version = "0.8" }
tokio = { version = "1", features = ["macros", "sync"] }
common-path = "1"
serde-value = "0.7.0"
serde-value = "0.7"
itertools = "0.13"
local-ip-address = "0.6"
css-color = "0.2"
resvg = "0.43.0"
resvg = "0.44.0"
dunce = "1"
glob = "0.3"
oxc_parser = "0.25"
oxc_span = "0.25"
oxc_allocator = "0.25"
oxc_ast = "0.25"
# 0.39 raised msrv to above 1.78 but 0.37+ can't compile on 1.77.2 either.
oxc_parser = "0.36"
oxc_span = "0.36"
oxc_allocator = "0.36"
oxc_ast = "0.36"
magic_string = "0.3"
phf = { version = "0.11", features = ["macros"] }
walkdir = "2"
@@ -131,7 +132,7 @@ libc = "0.2"
[target."cfg(target_os = \"macos\")".dependencies]
plist = "1"
tauri-macos-sign = { version = "2.0.1", path = "../tauri-macos-sign" }
tauri-macos-sign = { version = "2.1.0", path = "../tauri-macos-sign" }
object = { version = "0.36", default-features = false, features = [
"macho",
"read_core",
@@ -147,4 +148,4 @@ native-tls = [
"ureq/native-tls",
]
native-tls-vendored = ["native-tls", "tauri-bundler/native-tls-vendored"]
rustls = ["tauri-bundler/rustls", "cargo-mobile2/rustls", "ureq/tls"]
rustls = ["tauri-bundler/rustls", "cargo-mobile2/rustls", "ureq/rustls"]

View File

@@ -17,6 +17,7 @@ These environment variables are inputs to the CLI which may have an equivalent C
- `TAURI_BUNDLER_WIX_FIPS_COMPLIANT` — Specify the bundler's WiX `FipsCompliant` option.
- `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR` - Specify a GitHub mirror to download files and tools used by tauri bundler.
- `TAURI_BUNDLER_TOOLS_GITHUB_MIRROR_TEMPLATE` - Specify a GitHub mirror template to download files and tools used by tauri bundler, for example: `https://mirror.example.com/<owner>/<repo>/releases/download/<version>/<asset>`.
- `TAURI_BUNDLER_DMG_IGNORE_CI` - Disable the check for `CI: true` in the `.dmg` bundler.
- `TAURI_SKIP_SIDECAR_SIGNATURE_CHECK` - Skip signing sidecars.
- `TAURI_SIGNING_PRIVATE_KEY` — Private key used to sign your app bundles, can be either a string or a path to the file.
- `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` — The signing private key password, see `TAURI_SIGNING_PRIVATE_KEY`.

View File

@@ -1,8 +1,8 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://schema.tauri.app/config/2.0.5",
"$id": "https://schema.tauri.app/config/2.3.0",
"title": "Config",
"description": "The Tauri configuration object.\n It is read from a file where you can define your frontend assets,\n configure the bundler and define a tray icon.\n\n The configuration file is generated by the\n [`tauri init`](https://v2.tauri.app/reference/cli/#init) command that lives in\n your Tauri application source directory (src-tauri).\n\n Once generated, you may modify it at will to customize your Tauri application.\n\n ## File Formats\n\n By default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\n Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.\n The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.\n The TOML file name is `Tauri.toml`.\n\n ## Platform-Specific Configuration\n\n In addition to the default configuration file, Tauri can\n read a platform-specific configuration from `tauri.linux.conf.json`,\n `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`\n (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),\n which gets merged with the main configuration object.\n\n ## Configuration Structure\n\n The configuration is composed of the following objects:\n\n - [`app`](#appconfig): The Tauri configuration\n - [`build`](#buildconfig): The build configuration\n - [`bundle`](#bundleconfig): The bundle configurations\n - [`plugins`](#pluginconfig): The plugins configuration\n\n Example tauri.config.json file:\n\n ```json\n {\n \"productName\": \"tauri-app\",\n \"version\": \"0.1.0\",\n \"build\": {\n \"beforeBuildCommand\": \"\",\n \"beforeDevCommand\": \"\",\n \"devUrl\": \"../dist\",\n \"frontendDist\": \"../dist\"\n },\n \"app\": {\n \"security\": {\n \"csp\": null\n },\n \"windows\": [\n {\n \"fullscreen\": false,\n \"height\": 600,\n \"resizable\": true,\n \"title\": \"Tauri App\",\n \"width\": 800\n }\n ]\n },\n \"bundle\": {},\n \"plugins\": {}\n }\n ```",
"description": "The Tauri configuration object.\n It is read from a file where you can define your frontend assets,\n configure the bundler and define a tray icon.\n\n The configuration file is generated by the\n [`tauri init`](https://v2.tauri.app/reference/cli/#init) command that lives in\n your Tauri application source directory (src-tauri).\n\n Once generated, you may modify it at will to customize your Tauri application.\n\n ## File Formats\n\n By default, the configuration is defined as a JSON file named `tauri.conf.json`.\n\n Tauri also supports JSON5 and TOML files via the `config-json5` and `config-toml` Cargo features, respectively.\n The JSON5 file name must be either `tauri.conf.json` or `tauri.conf.json5`.\n The TOML file name is `Tauri.toml`.\n\n ## Platform-Specific Configuration\n\n In addition to the default configuration file, Tauri can\n read a platform-specific configuration from `tauri.linux.conf.json`,\n `tauri.windows.conf.json`, `tauri.macos.conf.json`, `tauri.android.conf.json` and `tauri.ios.conf.json`\n (or `Tauri.linux.toml`, `Tauri.windows.toml`, `Tauri.macos.toml`, `Tauri.android.toml` and `Tauri.ios.toml` if the `Tauri.toml` format is used),\n which gets merged with the main configuration object.\n\n ## Configuration Structure\n\n The configuration is composed of the following objects:\n\n - [`app`](#appconfig): The Tauri configuration\n - [`build`](#buildconfig): The build configuration\n - [`bundle`](#bundleconfig): The bundle configurations\n - [`plugins`](#pluginconfig): The plugins configuration\n\n Example tauri.config.json file:\n\n ```json\n {\n \"productName\": \"tauri-app\",\n \"version\": \"0.1.0\",\n \"build\": {\n \"beforeBuildCommand\": \"\",\n \"beforeDevCommand\": \"\",\n \"devUrl\": \"http://localhost:3000\",\n \"frontendDist\": \"../dist\"\n },\n \"app\": {\n \"security\": {\n \"csp\": null\n },\n \"windows\": [\n {\n \"fullscreen\": false,\n \"height\": 600,\n \"resizable\": true,\n \"title\": \"Tauri App\",\n \"width\": 800\n }\n ]\n },\n \"bundle\": {},\n \"plugins\": {}\n }\n ```",
"type": "object",
"required": [
"identifier"
@@ -397,6 +397,13 @@
"default": false,
"type": "boolean"
},
"windowClassname": {
"description": "The name of the window class created on Windows to create the window. **Windows only**.",
"type": [
"string",
"null"
]
},
"theme": {
"description": "The initial window theme. Defaults to the system theme. Only implemented on Windows and macOS 10.14+.",
"anyOf": [
@@ -486,6 +493,40 @@
"description": "Whether browser extensions can be installed for the webview process\n\n ## Platform-specific:\n\n - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled)\n - **MacOS / Linux / iOS / Android** - Unsupported.",
"default": false,
"type": "boolean"
},
"useHttpsScheme": {
"description": "Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.\n\n ## Note\n\n Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.\n\n ## Warning\n\n Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.",
"default": false,
"type": "boolean"
},
"devtools": {
"description": "Enable web inspector which is usually called browser devtools. Enabled by default.\n\n This API works in **debug** builds, but requires `devtools` feature flag to enable it in **release** builds.\n\n ## Platform-specific\n\n - macOS: This will call private functions on **macOS**.\n - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.\n - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.",
"type": [
"boolean",
"null"
]
},
"backgroundColor": {
"description": "Set the window and webview background color.\n\n ## Platform-specific:\n\n - **Windows**: alpha channel is ignored for the window layer.\n - **Windows**: On Windows 7, alpha channel is ignored for the webview layer.\n - **Windows**: On Windows 8 and newer, if alpha channel is not `0`, it will be ignored for the webview layer.",
"anyOf": [
{
"$ref": "#/definitions/Color"
},
{
"type": "null"
}
]
},
"backgroundThrottling": {
"description": "Change the default background throttling behaviour.\n\n By default, browsers use a suspend policy that will throttle timers and even unload\n the whole tab (view) to free resources after roughly 5 minutes when a view became\n minimized or hidden. This will pause all tasks until the documents visibility state\n changes back from hidden to visible by bringing the view back to the foreground.\n\n ## Platform-specific\n\n - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.\n - **iOS**: Supported since version 17.0+.\n - **macOS**: Supported since version 14.0+.\n\n see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578",
"anyOf": [
{
"$ref": "#/definitions/BackgroundThrottlingPolicy"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
@@ -827,32 +868,122 @@
]
},
"Color": {
"description": "a tuple struct of RGBA colors. Each value has minimum of 0 and maximum of 255.",
"type": "array",
"items": [
"anyOf": [
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
"description": "Color hex string, for example: #fff, #ffffff, or #ffffffff.",
"type": "string",
"pattern": "^#?([A-Fa-f0-9]{3}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$"
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
"description": "Array of RGB colors. Each value has minimum of 0 and maximum of 255.",
"type": "array",
"items": [
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
],
"maxItems": 3,
"minItems": 3
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
"description": "Array of RGBA colors. Each value has minimum of 0 and maximum of 255.",
"type": "array",
"items": [
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
],
"maxItems": 4,
"minItems": 4
},
{
"type": "integer",
"format": "uint8",
"minimum": 0.0
"description": "Object of red, green, blue, alpha color values. Each value has minimum of 0 and maximum of 255.",
"type": "object",
"required": [
"blue",
"green",
"red"
],
"properties": {
"red": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
"green": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
"blue": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
},
"alpha": {
"default": 255,
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
}
}
],
"maxItems": 4,
"minItems": 4
]
},
"BackgroundThrottlingPolicy": {
"description": "Background throttling policy.",
"oneOf": [
{
"description": "A policy where background throttling is disabled",
"type": "string",
"enum": [
"disabled"
]
},
{
"description": "A policy where a web view thats not in a window fully suspends tasks. This is usually the default behavior in case no policy is set.",
"type": "string",
"enum": [
"suspend"
]
},
{
"description": "A policy where a web view thats not in a window limits processing, but does not fully suspend tasks.",
"type": "string",
"enum": [
"throttle"
]
}
]
},
"SecurityConfig": {
"description": "Security configuration.\n\n See more: <https://v2.tauri.app/reference/config/#securityconfig>",
@@ -924,6 +1055,17 @@
"items": {
"$ref": "#/definitions/CapabilityEntry"
}
},
"headers": {
"description": "The headers, which are added to every http response from tauri to the web view\n This doesn't include IPC Messages and error responses",
"anyOf": [
{
"$ref": "#/definitions/HeaderConfig"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
@@ -1104,7 +1246,7 @@
]
},
"Capability": {
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n \"identifier\": \"main-user-files-write\",\n \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n \"windows\": [\n \"main\"\n ],\n \"permissions\": [\n \"core:default\",\n \"dialog:open\",\n {\n \"identifier\": \"fs:allow-write-text-file\",\n \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n },\n ],\n \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
"description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\n It controls application windows fine grained access to the Tauri core, application, or plugin commands.\n If a window is not matching any capability then it has no access to the IPC layer at all.\n\n This can be done to create groups of windows, based on their required system access, which can reduce\n impact of frontend vulnerabilities in less privileged windows.\n Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`.\n A Window can have none, one, or multiple associated capabilities.\n\n ## Example\n\n ```json\n {\n \"identifier\": \"main-user-files-write\",\n \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\",\n \"windows\": [\n \"main\"\n ],\n \"permissions\": [\n \"core:default\",\n \"dialog:open\",\n {\n \"identifier\": \"fs:allow-write-text-file\",\n \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n },\n ],\n \"platforms\": [\"macOS\",\"windows\"]\n }\n ```",
"type": "object",
"required": [
"identifier",
@@ -1151,7 +1293,7 @@
}
},
"permissions": {
"description": "List of permissions attached to this capability.\n\n Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.\n For commands directly implemented in the application itself only `${permission-name}`\n is required.\n\n ## Example\n\n ```json\n [\n \"core:default\",\n \"shell:allow-open\",\n \"dialog:open\",\n {\n \"identifier\": \"fs:allow-write-text-file\",\n \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n }\n ```",
"description": "List of permissions attached to this capability.\n\n Must include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`.\n For commands directly implemented in the application itself only `${permission-name}`\n is required.\n\n ## Example\n\n ```json\n [\n \"core:default\",\n \"shell:allow-open\",\n \"dialog:open\",\n {\n \"identifier\": \"fs:allow-write-text-file\",\n \"allow\": [{ \"path\": \"$HOME/test.txt\" }]\n }\n ]\n ```",
"type": "array",
"items": {
"$ref": "#/definitions/PermissionEntry"
@@ -1333,6 +1475,168 @@
}
]
},
"HeaderConfig": {
"description": "A struct, where the keys are some specific http header names.\n If the values to those keys are defined, then they will be send as part of a response message.\n This does not include error messages and ipc messages\n\n ## Example configuration\n ```javascript\n {\n //..\n app:{\n //..\n security: {\n headers: {\n \"Cross-Origin-Opener-Policy\": \"same-origin\",\n \"Cross-Origin-Embedder-Policy\": \"require-corp\",\n \"Timing-Allow-Origin\": [\n \"https://developer.mozilla.org\",\n \"https://example.com\",\n ],\n \"Access-Control-Expose-Headers\": \"Tauri-Custom-Header\",\n \"Tauri-Custom-Header\": {\n \"key1\": \"'value1' 'value2'\",\n \"key2\": \"'value3'\"\n }\n },\n csp: \"default-src 'self'; connect-src ipc: http://ipc.localhost\",\n }\n //..\n }\n //..\n }\n ```\n In this example `Cross-Origin-Opener-Policy` and `Cross-Origin-Embedder-Policy` are set to allow for the use of [`SharedArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer).\n The result is, that those headers are then set on every response sent via the `get_response` function in crates/tauri/src/protocol/tauri.rs.\n The Content-Security-Policy header is defined separately, because it is also handled separately.\n\n For the helloworld example, this config translates into those response headers:\n ```http\n access-control-allow-origin: http://tauri.localhost\n access-control-expose-headers: Tauri-Custom-Header\n content-security-policy: default-src 'self'; connect-src ipc: http://ipc.localhost; script-src 'self' 'sha256-Wjjrs6qinmnr+tOry8x8PPwI77eGpUFR3EEGZktjJNs='\n content-type: text/html\n cross-origin-embedder-policy: require-corp\n cross-origin-opener-policy: same-origin\n tauri-custom-header: key1 'value1' 'value2'; key2 'value3'\n timing-allow-origin: https://developer.mozilla.org, https://example.com\n ```\n Since the resulting header values are always 'string-like'. So depending on the what data type the HeaderSource is, they need to be converted.\n - `String`(JS/Rust): stay the same for the resulting header value\n - `Array`(JS)/`Vec\\<String\\>`(Rust): Item are joined by \", \" for the resulting header value\n - `Object`(JS)/ `Hashmap\\<String,String\\>`(Rust): Items are composed from: key + space + value. Item are then joined by \"; \" for the resulting header value",
"type": "object",
"properties": {
"Access-Control-Allow-Credentials": {
"description": "The Access-Control-Allow-Credentials response header tells browsers whether the\n server allows cross-origin HTTP requests to include credentials.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Credentials>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Access-Control-Allow-Headers": {
"description": "The Access-Control-Allow-Headers response header is used in response\n to a preflight request which includes the Access-Control-Request-Headers\n to indicate which HTTP headers can be used during the actual request.\n\n This header is required if the request has an Access-Control-Request-Headers header.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Access-Control-Allow-Methods": {
"description": "The Access-Control-Allow-Methods response header specifies one or more methods\n allowed when accessing a resource in response to a preflight request.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Access-Control-Expose-Headers": {
"description": "The Access-Control-Expose-Headers response header allows a server to indicate\n which response headers should be made available to scripts running in the browser,\n in response to a cross-origin request.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Access-Control-Max-Age": {
"description": "The Access-Control-Max-Age response header indicates how long the results of a\n preflight request (that is the information contained in the\n Access-Control-Allow-Methods and Access-Control-Allow-Headers headers) can\n be cached.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Cross-Origin-Embedder-Policy": {
"description": "The HTTP Cross-Origin-Embedder-Policy (COEP) response header configures embedding\n cross-origin resources into the document.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Cross-Origin-Opener-Policy": {
"description": "The HTTP Cross-Origin-Opener-Policy (COOP) response header allows you to ensure a\n top-level document does not share a browsing context group with cross-origin documents.\n COOP will process-isolate your document and potential attackers can't access your global\n object if they were to open it in a popup, preventing a set of cross-origin attacks dubbed XS-Leaks.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Cross-Origin-Resource-Policy": {
"description": "The HTTP Cross-Origin-Resource-Policy response header conveys a desire that the\n browser blocks no-cors cross-origin/cross-site requests to the given resource.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Permissions-Policy": {
"description": "The HTTP Permissions-Policy header provides a mechanism to allow and deny the\n use of browser features in a document or within any \\<iframe\\> elements in the document.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Timing-Allow-Origin": {
"description": "The Timing-Allow-Origin response header specifies origins that are allowed to see values\n of attributes retrieved via features of the Resource Timing API, which would otherwise be\n reported as zero due to cross-origin restrictions.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"X-Content-Type-Options": {
"description": "The X-Content-Type-Options response HTTP header is a marker used by the server to indicate\n that the MIME types advertised in the Content-Type headers should be followed and not be\n changed. The header allows you to avoid MIME type sniffing by saying that the MIME types\n are deliberately configured.\n\n See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options>",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
},
"Tauri-Custom-Header": {
"description": "A custom header field Tauri-Custom-Header, don't use it.\n Remember to set Access-Control-Expose-Headers accordingly\n\n **NOT INTENDED FOR PRODUCTION USE**",
"anyOf": [
{
"$ref": "#/definitions/HeaderSource"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"HeaderSource": {
"description": "definition of a header source\n\n The header value to a header name",
"anyOf": [
{
"description": "string version of the header Value",
"type": "string"
},
{
"description": "list version of the header value. Item are joined by \",\" for the real header value",
"type": "array",
"items": {
"type": "string"
}
},
{
"description": "(Rust struct | Json | JavaScript Object) equivalent of the header value. Items are composed from: key + space + value. Item are then joined by \";\" for the real header value",
"type": "object",
"additionalProperties": {
"type": "string"
}
}
]
},
"TrayIconConfig": {
"description": "Configuration for application tray icon.\n\n See more: <https://v2.tauri.app/reference/config/#trayiconconfig>",
"type": "object",
@@ -1357,7 +1661,13 @@
"type": "boolean"
},
"menuOnLeftClick": {
"description": "A Boolean value that determines whether the menu should appear when the tray icon receives a left click on macOS.",
"description": "A Boolean value that determines whether the menu should appear when the tray icon receives a left click.\n\n ## Platform-specific:\n\n - **Linux**: Unsupported.",
"default": true,
"deprecated": true,
"type": "boolean"
},
"showMenuOnLeftClick": {
"description": "A Boolean value that determines whether the menu should appear when the tray icon receives a left click.\n\n ## Platform-specific:\n\n - **Linux**: Unsupported.",
"default": true,
"type": "boolean"
},
@@ -1390,7 +1700,7 @@
]
},
"devUrl": {
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [vite](https://vitejs.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [Vite](https://vite.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
"type": [
"string",
"null"
@@ -1651,7 +1961,7 @@
]
},
"useLocalToolsDir": {
"description": "Whether to use the project's `target` directory, for caching build tools (e.g., Wix and NSIS) when building this application. Defaults to `false`.\n\n If true, tools will be cached in `target\\.tauri-tools`.\n If false, tools will be cached in the current user's platform-specific cache directory.\n\n An example where it can be appropriate to set this to `true` is when building this application as a Windows System user (e.g., AWS EC2 workloads),\n because the Window system's app data directory is restricted.",
"description": "Whether to use the project's `target` directory, for caching build tools (e.g., Wix and NSIS) when building this application. Defaults to `false`.\n\n If true, tools will be cached in `target/.tauri/`.\n If false, tools will be cached in the current user's platform-specific cache directory.\n\n An example where it can be appropriate to set this to `true` is when building this application as a Windows System user (e.g., AWS EC2 workloads),\n because the Window system's app data directory is restricted.",
"default": false,
"type": "boolean"
},
@@ -2262,7 +2572,7 @@
]
},
"dialogImagePath": {
"description": "Path to a bitmap file to use on the installation user interface dialogs.\n It is used on the welcome and completion dialogs.\n The required dimensions are 493px × 312px.",
"description": "Path to a bitmap file to use on the installation user interface dialogs.\n It is used on the welcome and completion dialogs.\n\n The required dimensions are 493px × 312px.",
"type": [
"string",
"null"
@@ -2574,6 +2884,16 @@
"type": "string"
}
},
"recommends": {
"description": "The list of deb dependencies your application recommends.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"provides": {
"description": "The list of dependencies the package provides.",
"type": [
@@ -2685,6 +3005,16 @@
"type": "string"
}
},
"recommends": {
"description": "The list of RPM dependencies your application recommends.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"provides": {
"description": "The list of RPM dependencies your application provides.",
"type": [
@@ -2769,10 +3099,133 @@
"string",
"null"
]
},
"compression": {
"description": "Compression algorithm and level. Defaults to `Gzip` with level 6.",
"anyOf": [
{
"$ref": "#/definitions/RpmCompression"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false
},
"RpmCompression": {
"description": "Compression algorithms used when bundling RPM packages.",
"oneOf": [
{
"description": "Gzip compression",
"type": "object",
"required": [
"level",
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"gzip"
]
},
"level": {
"description": "Gzip compression level",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
{
"description": "Zstd compression",
"type": "object",
"required": [
"level",
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"zstd"
]
},
"level": {
"description": "Zstd compression level",
"type": "integer",
"format": "int32"
}
},
"additionalProperties": false
},
{
"description": "Xz compression",
"type": "object",
"required": [
"level",
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"xz"
]
},
"level": {
"description": "Xz compression level",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
{
"description": "Bzip2 compression",
"type": "object",
"required": [
"level",
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"bzip2"
]
},
"level": {
"description": "Bzip2 compression level",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
{
"description": "Disable compression",
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"type": "string",
"enum": [
"none"
]
}
},
"additionalProperties": false
}
]
},
"MacConfig": {
"description": "Configuration for the macOS bundles.\n\n See more: <https://v2.tauri.app/reference/config/#macconfig>",
"type": "object",
@@ -3005,7 +3458,7 @@
"additionalProperties": false
},
"AndroidConfig": {
"description": "General configuration for the iOS target.",
"description": "General configuration for the Android target.",
"type": "object",
"properties": {
"minSdkVersion": {

View File

@@ -1,9 +1,9 @@
{
"cli.js": {
"version": "2.0.4",
"version": "2.3.0",
"node": ">= 10.0.0"
},
"tauri": "2.0.5",
"tauri-build": "2.0.1",
"tauri-plugin": "2.0.1"
"tauri": "2.3.0",
"tauri-build": "2.0.5",
"tauri-plugin": "2.0.4"
}

View File

@@ -465,6 +465,15 @@
"description": "Whether page zooming by hotkeys is enabled\n\n ## Platform-specific:\n\n - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting.\n - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`,\n 20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission\n\n - **Android / iOS**: Unsupported.",
"default": false,
"type": "boolean"
},
"backgroundThrottling": {
"description": "Change the default background throttling behaviour.\n\n By default, browsers use a suspend policy that will throttle timers and even unload\n the whole tab (view) to free resources after roughly 5 minutes when a view became\n minimized or hidden. This will pause all tasks until the documents visibility state\n changes back from hidden to visible by bringing the view back to the foreground.\n\n ## Platform-specific\n\n - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.\n - **iOS**: Supported since version 17.0+.\n - **macOS**: Supported since version 14.0+.\n\n see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578",
"type": "string",
"enum": [
"disabled",
"throttle",
"suspend"
]
}
},
"additionalProperties": false
@@ -1369,7 +1378,7 @@
]
},
"devUrl": {
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [vite](https://vitejs.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
"description": "The URL to load in development.\n\n This is usually an URL to a dev server, which serves your application assets with hot-reload and HMR.\n Most modern JavaScript bundlers like [Vite](https://vite.dev/guide/) provides a way to start a dev server by default.\n\n If you don't have a dev server or don't want to use one, ignore this option and use [`frontendDist`](BuildConfig::frontend_dist)\n and point to a web assets directory, and Tauri CLI will run its built-in dev server and provide a simple hot-reload experience.",
"type": [
"string",
"null"
@@ -2995,4 +3004,4 @@
"additionalProperties": true
}
}
}
}

View File

@@ -166,16 +166,15 @@ pub fn command(options: Options) -> Result<()> {
let capabilities = if let Some((expected_platforms, target_name)) = expected_capability_config {
let mut capabilities = capabilities_iter
.filter(|(capability, _path)| {
capability.platforms().map_or(
false, /* allows any target, so we should skip it since we're adding a target-specific plugin */
|platforms| {
// all platforms must be in the expected platforms list
platforms.iter().all(|p| expected_platforms.contains(&p.to_string()))
},
)
.filter(|(capability, _path)| {
capability.platforms().is_some_and(|platforms| {
// all platforms must be in the expected platforms list
platforms
.iter()
.all(|p| expected_platforms.contains(&p.to_string()))
})
.collect::<Vec<_>>();
})
.collect::<Vec<_>>();
if capabilities.is_empty() {
let identifier = format!("{target_name}-capability");
@@ -187,7 +186,8 @@ pub fn command(options: Options) -> Result<()> {
capabilities.push((
TomlOrJson::Json(serde_json::json!({
"identifier": identifier,
"platforms": expected_platforms
"platforms": expected_platforms,
"windows": ["main"]
})),
capability_path,
));

View File

@@ -23,8 +23,7 @@ pub struct Options {
pub fn command(options: Options) -> Result<()> {
crate::helpers::app_paths::resolve();
let tauri_dir = tauri_dir();
let acl_manifests_path = tauri_dir
let acl_manifests_path = tauri_dir()
.join("gen")
.join("schemas")
.join("acl-manifests.json");

View File

@@ -9,7 +9,7 @@ use crate::Result;
pub mod add;
mod ls;
mod new;
mod rm;
pub mod rm;
#[derive(Debug, Parser)]
#[clap(about = "Manage or create permissions for your app or plugin")]

View File

@@ -46,13 +46,15 @@ fn rm_permission_files(identifier: &str, dir: &Path) -> Result<()> {
permission_file.default = None;
} else {
let set_len = permission_file.set.len();
permission_file.set.retain(|s| s.identifier != identifier);
permission_file
.set
.retain(|s| !identifier_match(identifier, &s.identifier));
updated = permission_file.set.len() != set_len;
let permission_len = permission_file.permission.len();
permission_file
.permission
.retain(|s| s.identifier != identifier);
.retain(|s| !identifier_match(identifier, &s.identifier));
updated = updated || permission_file.permission.len() != permission_len;
}
@@ -84,7 +86,17 @@ fn rm_permission_from_capabilities(identifier: &str, dir: &Path) -> Result<()> {
if let Ok(mut value) = content.parse::<toml_edit::DocumentMut>() {
if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) {
let prev_len = permissions.len();
permissions.retain(|p| p.as_str().map(|p| p != identifier).unwrap_or(false));
permissions.retain(|p| match p {
toml_edit::Value::String(s) => !identifier_match(identifier, s.value()),
toml_edit::Value::InlineTable(o) => {
if let Some(toml_edit::Value::String(permission_name)) = o.get("identifier") {
return !identifier_match(identifier, permission_name.value());
}
true
}
_ => false,
});
if prev_len != permissions.len() {
std::fs::write(&path, value.to_string())?;
log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display());
@@ -97,7 +109,17 @@ fn rm_permission_from_capabilities(identifier: &str, dir: &Path) -> Result<()> {
if let Ok(mut value) = serde_json::from_slice::<serde_json::Value>(&content) {
if let Some(permissions) = value.get_mut("permissions").and_then(|p| p.as_array_mut()) {
let prev_len = permissions.len();
permissions.retain(|p| p.as_str().map(|p| p != identifier).unwrap_or(false));
permissions.retain(|p| match p {
serde_json::Value::String(s) => !identifier_match(identifier, s),
serde_json::Value::Object(o) => {
if let Some(serde_json::Value::String(permission_name)) = o.get("identifier") {
return !identifier_match(identifier, permission_name);
}
true
}
_ => false,
});
if prev_len != permissions.len() {
std::fs::write(&path, serde_json::to_vec_pretty(&value)?)?;
log::info!(action = "Removed"; "permission from capability at {}", dunce::simplified(&path).display());
@@ -113,11 +135,20 @@ fn rm_permission_from_capabilities(identifier: &str, dir: &Path) -> Result<()> {
Ok(())
}
fn identifier_match(identifier: &str, permission: &str) -> bool {
match identifier.split_once(':') {
Some((plugin_name, "*")) => permission.contains(plugin_name),
_ => permission == identifier,
}
}
#[derive(Debug, Parser)]
#[clap(about = "Remove a permission file, and its reference from any capability")]
pub struct Options {
/// Permission to remove.
identifier: String,
///
/// To remove all permissions for a given plugin, provide `<plugin-name>:*`
pub identifier: String,
}
pub fn command(options: Options) -> Result<()> {

View File

@@ -9,7 +9,7 @@ use regex::Regex;
use crate::{
acl,
helpers::{
app_paths::{resolve_app_dir, tauri_dir},
app_paths::{resolve_frontend_dir, tauri_dir},
cargo,
npm::PackageManager,
},
@@ -49,14 +49,27 @@ pub fn run(options: Options) -> Result<()> {
.map(|(p, v)| (p, Some(v)))
.unwrap_or((&options.plugin, None));
let mut plugins = crate::helpers::plugins::known_plugins();
let (metadata, is_known) = plugins
.remove(plugin)
.map(|metadata| (metadata, true))
.unwrap_or_default();
let plugin_snake_case = plugin.replace('-', "_");
let crate_name = format!("tauri-plugin-{plugin}");
let npm_name = format!("@tauri-apps/plugin-{plugin}");
let npm_name = if is_known {
format!("@tauri-apps/plugin-{plugin}")
} else {
format!("tauri-plugin-{plugin}-api")
};
let mut plugins = crate::helpers::plugins::known_plugins();
let metadata = plugins.remove(plugin).unwrap_or_default();
if !is_known && (options.tag.is_some() || options.rev.is_some() || options.branch.is_some()) {
anyhow::bail!(
"Git options --tag, --rev and --branch can only be used with official Tauri plugins"
);
}
let app_dir = resolve_app_dir();
let frontend_dir = resolve_frontend_dir();
let tauri_dir = tauri_dir();
let target_str = metadata
@@ -81,10 +94,7 @@ pub fn run(options: Options) -> Result<()> {
})?;
if !metadata.rust_only {
if let Some(manager) = app_dir
.map(PackageManager::from_project)
.and_then(|managers| managers.into_iter().next())
{
if let Some(manager) = frontend_dir.map(PackageManager::from_project) {
let npm_version_req = version
.map(ToString::to_string)
.or(metadata.version_req.as_ref().map(|v| match manager {
@@ -93,10 +103,7 @@ pub fn run(options: Options) -> Result<()> {
}));
let npm_spec = match (npm_version_req, options.tag, options.rev, options.branch) {
(Some(version_req), _, _, _) => match manager {
PackageManager::Deno => format!("npm:{npm_name}@{version_req}"),
_ => format!("{npm_name}@{version_req}"),
},
(Some(version_req), _, _, _) => format!("{npm_name}@{version_req}"),
(None, Some(tag), None, None) => {
format!("tauri-apps/tauri-plugin-{plugin}#{tag}")
}

View File

@@ -87,7 +87,7 @@ pub fn command(mut options: Options, verbosity: u8) -> Result<()> {
let bin_path = interface.build(interface_options)?;
log::info!(action ="Built"; "application at: {}", tauri_utils::display_path(&bin_path));
log::info!(action ="Built"; "application at: {}", tauri_utils::display_path(bin_path));
let app_settings = interface.app_settings();

View File

@@ -9,7 +9,6 @@ use std::{
};
use anyhow::Context;
use base64::Engine;
use clap::{builder::PossibleValue, ArgAction, Parser, ValueEnum};
use tauri_bundler::PackageType;
use tauri_utils::platform::Target;
@@ -40,7 +39,13 @@ impl FromStr for BundleFormat {
impl ValueEnum for BundleFormat {
fn value_variants<'a>() -> &'a [Self] {
static VARIANTS: OnceLock<Vec<BundleFormat>> = OnceLock::new();
VARIANTS.get_or_init(|| PackageType::all().iter().map(|t| Self(*t)).collect())
VARIANTS.get_or_init(|| {
PackageType::all()
.iter()
.filter(|t| **t != PackageType::Updater)
.map(|t| Self(*t))
.collect()
})
}
fn to_possible_value(&self) -> Option<PossibleValue> {
@@ -182,24 +187,6 @@ pub fn bundle<A: AppSettings>(
_ => log::Level::Trace,
});
// set env vars used by the bundler
#[cfg(target_os = "linux")]
{
if config.bundle.linux.appimage.bundle_media_framework {
std::env::set_var("APPIMAGE_BUNDLE_GSTREAMER", "1");
}
if let Some(open) = config.plugins.0.get("shell").and_then(|v| v.get("open")) {
if open.as_bool().is_some_and(|x| x) || open.is_string() {
std::env::set_var("APPIMAGE_BUNDLE_XDG_OPEN", "1");
}
}
if settings.deep_link_protocols().is_some() {
std::env::set_var("APPIMAGE_BUNDLE_XDG_MIME", "1");
}
}
let bundles = tauri_bundler::bundle_project(&settings)
.map_err(|e| match e {
tauri_bundler::Error::BundlerError(e) => e,
@@ -227,7 +214,12 @@ fn sign_updaters(
.filter(|bundle| {
matches!(
bundle.package_type,
PackageType::Updater | PackageType::Nsis | PackageType::WindowsMsi | PackageType::AppImage
PackageType::Updater
| PackageType::Nsis
| PackageType::WindowsMsi
| PackageType::AppImage
| PackageType::Deb
| PackageType::Rpm
)
})
.collect();
@@ -257,15 +249,14 @@ fn sign_updaters(
// check if private_key points to a file...
let maybe_path = Path::new(&private_key);
let private_key = if maybe_path.exists() {
std::fs::read_to_string(maybe_path)?
std::fs::read_to_string(maybe_path)
.with_context(|| format!("faild to read {}", maybe_path.display()))?
} else {
private_key
};
let secret_key = updater_signature::secret_key(private_key, password)?;
let pubkey = base64::engine::general_purpose::STANDARD.decode(pubkey)?;
let pub_key_decoded = String::from_utf8_lossy(&pubkey);
let public_key = minisign::PublicKeyBox::from_string(&pub_key_decoded)?.into_public_key()?;
let secret_key =
updater_signature::secret_key(private_key, password).context("failed to decode secret key")?;
let public_key = updater_signature::pub_key(pubkey).context("failed to decode pubkey")?;
let mut signed_paths = Vec::new();
for bundle in update_enabled_bundles {

View File

@@ -4,13 +4,13 @@
use crate::{
helpers::{
app_paths::{app_dir, tauri_dir},
app_paths::{frontend_dir, tauri_dir},
command_env,
config::{
get as get_config, reload as reload_config, BeforeDevCommand, ConfigHandle, FrontendDist,
},
},
interface::{AppInterface, DevProcess, ExitReason, Interface},
interface::{AppInterface, ExitReason, Interface},
CommandExt, ConfigValue, Result,
};
@@ -140,7 +140,7 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand
(Some(script), cwd.map(Into::into), wait)
}
};
let cwd = script_cwd.unwrap_or_else(|| app_dir().clone());
let cwd = script_cwd.unwrap_or_else(|| frontend_dir().clone());
if let Some(before_dev) = script {
log::info!(action = "Running"; "BeforeDevCommand (`{}`)", before_dev);
let mut env = command_env(true);
@@ -338,30 +338,6 @@ pub fn setup(interface: &AppInterface, options: &mut Options, config: ConfigHand
Ok(())
}
pub fn wait_dev_process<
C: DevProcess + Send + 'static,
F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static,
>(
child: C,
on_exit: F,
) {
std::thread::spawn(move || {
let code = child
.wait()
.ok()
.and_then(|status| status.code())
.or(Some(1));
on_exit(
code,
if child.manually_killed_process() {
ExitReason::TriggeredKill
} else {
ExitReason::NormalExit
},
);
});
}
pub fn on_app_exit(code: Option<i32>, reason: ExitReason, exit_on_panic: bool, no_watch: bool) {
if no_watch
|| (!matches!(reason, ExitReason::TriggeredKill)

View File

@@ -79,7 +79,7 @@ pub fn start<P: AsRef<Path>>(dir: P, ip: IpAddr, port: Option<u16>) -> crate::Re
}
async fn handler(uri: Uri, state: State<ServerState>) -> impl IntoResponse {
// Frontend files should not contain query parameters. This seems to be how vite handles it.
// Frontend files should not contain query parameters. This seems to be how Vite handles it.
let uri = uri.path();
let uri = if uri == "/" {
@@ -162,17 +162,20 @@ fn watch<F: Fn() + Send + 'static>(dir: PathBuf, handler: F) {
thread::spawn(move || {
let (tx, rx) = std::sync::mpsc::channel();
let mut watcher = notify_debouncer_mini::new_debouncer(Duration::from_secs(1), tx)
let mut watcher = notify_debouncer_full::new_debouncer(Duration::from_secs(1), None, tx)
.expect("failed to start builtin server fs watcher");
watcher
.watcher()
.watch(&dir, notify::RecursiveMode::Recursive)
.expect("builtin server failed to watch dir");
loop {
if rx.recv().is_ok() {
handler();
if let Ok(Ok(event)) = rx.recv() {
if let Some(event) = event.first() {
if !event.kind.is_access() {
handler();
}
}
}
}
});

View File

@@ -18,12 +18,12 @@ use tauri_utils::{
};
const TAURI_GITIGNORE: &[u8] = include_bytes!("../../tauri.gitignore");
// path to the Tauri app (Rust crate) directory, usually <cwd>/src-tauri
// path to the Tauri app (Rust crate) directory, usually `<project>/src-tauri/`
const ENV_TAURI_APP_PATH: &str = "TAURI_APP_PATH";
// path to the frontend app directory
// path to the frontend app directory, usually `<project>/`
const ENV_TAURI_FRONTEND_PATH: &str = "TAURI_FRONTEND_PATH";
static APP_DIR: OnceLock<PathBuf> = OnceLock::new();
static FRONTEND_DIR: OnceLock<PathBuf> = OnceLock::new();
static TAURI_DIR: OnceLock<PathBuf> = OnceLock::new();
pub fn walk_builder(path: &Path) -> WalkBuilder {
@@ -79,6 +79,7 @@ fn env_tauri_app_path() -> Option<PathBuf> {
.ok()?
.canonicalize()
.ok()
.map(|p| dunce::simplified(&p).to_path_buf())
}
fn env_tauri_frontend_path() -> Option<PathBuf> {
@@ -87,10 +88,11 @@ fn env_tauri_frontend_path() -> Option<PathBuf> {
.ok()?
.canonicalize()
.ok()
.map(|p| dunce::simplified(&p).to_path_buf())
}
pub fn resolve_tauri_dir() -> Option<PathBuf> {
let src_dir = env_tauri_frontend_path().or_else(|| current_dir().ok())?;
let src_dir = env_tauri_app_path().or_else(|| current_dir().ok())?;
if src_dir.join(ConfigFormat::Json.into_file_name()).exists()
|| src_dir.join(ConfigFormat::Json5.into_file_name()).exists()
@@ -113,7 +115,7 @@ pub fn resolve_tauri_dir() -> Option<PathBuf> {
pub fn resolve() {
TAURI_DIR.set(resolve_tauri_dir().unwrap_or_else(|| {
let env_var_name = env_tauri_frontend_path().is_some().then(|| format!("`{ENV_TAURI_FRONTEND_PATH}`"));
let env_var_name = env_tauri_app_path().is_some().then(|| format!("`{ENV_TAURI_APP_PATH}`"));
panic!("Couldn't recognize the {} folder as a Tauri project. It must contain a `{}`, `{}` or `{}` file in any subfolder.",
env_var_name.as_deref().unwrap_or("current"),
ConfigFormat::Json.into_file_name(),
@@ -121,8 +123,8 @@ pub fn resolve() {
ConfigFormat::Toml.into_file_name()
)
})).expect("tauri dir already resolved");
APP_DIR
.set(resolve_app_dir().unwrap_or_else(|| tauri_dir().parent().unwrap().to_path_buf()))
FRONTEND_DIR
.set(resolve_frontend_dir().unwrap_or_else(|| tauri_dir().parent().unwrap().to_path_buf()))
.expect("app dir already resolved");
}
@@ -132,14 +134,15 @@ pub fn tauri_dir() -> &'static PathBuf {
.expect("app paths not initialized, this is a Tauri CLI bug")
}
pub fn resolve_app_dir() -> Option<PathBuf> {
let app_dir = env_tauri_app_path().unwrap_or_else(|| current_dir().expect("failed to read cwd"));
pub fn resolve_frontend_dir() -> Option<PathBuf> {
let frontend_dir =
env_tauri_frontend_path().unwrap_or_else(|| current_dir().expect("failed to read cwd"));
if app_dir.join("package.json").exists() {
return Some(app_dir);
if frontend_dir.join("package.json").exists() {
return Some(frontend_dir);
}
lookup(&app_dir, |path| {
lookup(&frontend_dir, |path| {
if let Some(file_name) = path.file_name() {
file_name == OsStr::new("package.json")
} else {
@@ -149,8 +152,8 @@ pub fn resolve_app_dir() -> Option<PathBuf> {
.map(|p| p.parent().unwrap().to_path_buf())
}
pub fn app_dir() -> &'static PathBuf {
APP_DIR
pub fn frontend_dir() -> &'static PathBuf {
FRONTEND_DIR
.get()
.expect("app paths not initialized, this is a Tauri CLI bug")
}

View File

@@ -61,3 +61,33 @@ pub fn install_one(options: CargoInstallOptions) -> crate::Result<()> {
Ok(())
}
#[derive(Debug, Default, Clone, Copy)]
pub struct CargoUninstallOptions<'a> {
pub name: &'a str,
pub cwd: Option<&'a std::path::Path>,
pub target: Option<&'a str>,
}
pub fn uninstall_one(options: CargoUninstallOptions) -> crate::Result<()> {
let mut cargo = Command::new("cargo");
cargo.arg("remove");
cargo.arg(options.name);
if let Some(target) = options.target {
cargo.args(["--target", target]);
}
if let Some(cwd) = options.cwd {
cargo.current_dir(cwd);
}
log::info!("Uninstalling Cargo dependency \"{}\"...", options.name);
let status = cargo.status().context("failed to run `cargo remove`")?;
if !status.success() {
anyhow::bail!("Failed to remove Cargo dependency");
}
Ok(())
}

View File

@@ -99,13 +99,13 @@ impl std::fmt::Display for CrateVersion {
pub fn crate_latest_version(name: &str) -> Option<String> {
let url = format!("https://docs.rs/crate/{name}/");
match ureq::get(&url).call() {
Ok(response) => match (response.status(), response.header("location")) {
(302, Some(location)) => Some(location.replace(&url, "")),
_ => None,
},
Err(_) => None,
let response = ureq::get(&url).call().ok()?;
if response.status().is_redirection() {
if let Some(location) = response.headers().get("location") {
return location.to_str().ok().map(|s| s.replace(&url, ""));
}
}
None
}
pub fn crate_version(

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use itertools::Itertools;
use json_patch::merge;
use serde_json::Value as JsonValue;
@@ -92,7 +93,7 @@ pub fn wix_settings(config: WixConfig) -> tauri_bundler::WixSettings {
enable_elevated_update_task: config.enable_elevated_update_task,
banner_path: config.banner_path,
dialog_image_path: config.dialog_image_path,
fips_compliant: var_os("TAURI_BUNDLER_WIX_FIPS_COMPLIANT").map_or(false, |v| v == "true"),
fips_compliant: var_os("TAURI_BUNDLER_WIX_FIPS_COMPLIANT").is_some_and(|v| v == "true"),
}
}
@@ -172,11 +173,11 @@ fn get_internal(
|| config_path.extension() == Some(OsStr::new("json5"))
{
let schema: JsonValue = serde_json::from_str(include_str!("../../config.schema.json"))?;
let schema = jsonschema::JSONSchema::compile(&schema).unwrap();
let result = schema.validate(&config);
if let Err(errors) = result {
let validator = jsonschema::validator_for(&schema).expect("Invalid schema");
let mut errors = validator.iter_errors(&config).peekable();
if errors.peek().is_some() {
for error in errors {
let path = error.instance_path.clone().into_vec().join(" > ");
let path = error.instance_path.into_iter().join(" > ");
if path.is_empty() {
log::error!("`{}` error: {}", config_file_name, error);
} else {

View File

@@ -267,7 +267,7 @@ mod sys {
}
pub(super) fn error_contended(err: &Error) -> bool {
err.raw_os_error().map_or(false, |x| x == libc::EWOULDBLOCK)
err.raw_os_error() == Some(libc::EWOULDBLOCK)
}
pub(super) fn error_unsupported(err: &Error) -> bool {
@@ -327,15 +327,11 @@ mod sys {
}
pub(super) fn error_contended(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_LOCK_VIOLATION as i32)
err.raw_os_error() == Some(ERROR_LOCK_VIOLATION as i32)
}
pub(super) fn error_unsupported(err: &Error) -> bool {
err
.raw_os_error()
.map_or(false, |x| x == ERROR_INVALID_FUNCTION as i32)
err.raw_os_error() == Some(ERROR_INVALID_FUNCTION as i32)
}
pub(super) fn unlock(file: &File) -> Result<()> {

View File

@@ -31,7 +31,7 @@ use crate::{
CommandExt,
};
use self::app_paths::app_dir;
use self::app_paths::frontend_dir;
pub fn command_env(debug: bool) -> HashMap<&'static str, String> {
let mut map = HashMap::new();
@@ -80,7 +80,7 @@ pub fn run_hook(
HookCommand::Script(s) => (Some(s), None),
HookCommand::ScriptWithOptions { script, cwd } => (Some(script), cwd.map(Into::into)),
};
let cwd = script_cwd.unwrap_or_else(|| app_dir().clone());
let cwd = script_cwd.unwrap_or_else(|| frontend_dir().clone());
if let Some(script) = script {
log::info!(action = "Running"; "{} `{}`", name, script);

View File

@@ -7,6 +7,22 @@ use anyhow::Context;
use crate::helpers::cross_command;
use std::{fmt::Display, path::Path, process::Command};
pub fn manager_version(package_manager: &str) -> Option<String> {
cross_command(package_manager)
.arg("-v")
.output()
.map(|o| {
if o.status.success() {
let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
Some(v.split('\n').next().unwrap().to_string())
} else {
None
}
})
.ok()
.unwrap_or_default()
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PackageManager {
Npm,
@@ -35,7 +51,16 @@ impl Display for PackageManager {
}
impl PackageManager {
pub fn from_project<P: AsRef<Path>>(path: P) -> Vec<Self> {
/// Detects package manager from the given directory, falls back to [`PackageManager::Npm`].
pub fn from_project<P: AsRef<Path>>(path: P) -> Self {
Self::all_from_project(path)
.first()
.copied()
.unwrap_or(Self::Npm)
}
/// Detects all possible package managers from the given directory.
pub fn all_from_project<P: AsRef<Path>>(path: P) -> Vec<Self> {
let mut found = Vec::new();
if let Ok(entries) = std::fs::read_dir(path) {
@@ -47,7 +72,15 @@ impl PackageManager {
} else if name.as_ref() == "pnpm-lock.yaml" {
found.push(PackageManager::Pnpm);
} else if name.as_ref() == "yarn.lock" {
found.push(PackageManager::Yarn);
let yarn = if manager_version("yarn")
.map(|v| v.chars().next().map(|c| c > '1').unwrap_or_default())
.unwrap_or(false)
{
PackageManager::YarnBerry
} else {
PackageManager::Yarn
};
found.push(yarn);
} else if name.as_ref() == "bun.lockb" {
found.push(PackageManager::Bun);
} else if name.as_ref() == "deno.lock" {
@@ -70,7 +103,11 @@ impl PackageManager {
}
}
pub fn install<P: AsRef<Path>>(&self, dependencies: &[String], app_dir: P) -> crate::Result<()> {
pub fn install<P: AsRef<Path>>(
&self,
dependencies: &[String],
frontend_dir: P,
) -> crate::Result<()> {
let dependencies_str = if dependencies.len() > 1 {
"dependencies"
} else {
@@ -85,11 +122,16 @@ impl PackageManager {
.join(", ")
);
let status = self
.cross_command()
.arg("add")
.args(dependencies)
.current_dir(app_dir)
let mut command = self.cross_command();
command.arg("add");
match self {
PackageManager::Deno => command.args(dependencies.iter().map(|d| format!("npm:{d}"))),
_ => command.args(dependencies),
};
let status = command
.current_dir(frontend_dir)
.status()
.with_context(|| format!("failed to run {self}"))?;
@@ -100,7 +142,11 @@ impl PackageManager {
Ok(())
}
pub fn remove<P: AsRef<Path>>(&self, dependencies: &[String], app_dir: P) -> crate::Result<()> {
pub fn remove<P: AsRef<Path>>(
&self,
dependencies: &[String],
frontend_dir: P,
) -> crate::Result<()> {
let dependencies_str = if dependencies.len() > 1 {
"dependencies"
} else {
@@ -123,7 +169,7 @@ impl PackageManager {
"remove"
})
.args(dependencies)
.current_dir(app_dir)
.current_dir(frontend_dir)
.status()
.with_context(|| format!("failed to run {self}"))?;
@@ -137,7 +183,7 @@ impl PackageManager {
pub fn current_package_version<P: AsRef<Path>>(
&self,
name: &str,
app_dir: P,
frontend_dir: P,
) -> crate::Result<Option<String>> {
let (output, regex) = match self {
PackageManager::Yarn => (
@@ -145,7 +191,7 @@ impl PackageManager {
.args(["list", "--pattern"])
.arg(name)
.args(["--depth", "0"])
.current_dir(app_dir)
.current_dir(frontend_dir)
.output()?,
None,
),
@@ -154,7 +200,7 @@ impl PackageManager {
.arg("info")
.arg(name)
.arg("--json")
.current_dir(app_dir)
.current_dir(frontend_dir)
.output()?,
Some(regex::Regex::new("\"Version\":\"([\\da-zA-Z\\-\\.]+)\"").unwrap()),
),
@@ -163,7 +209,7 @@ impl PackageManager {
.arg("list")
.arg(name)
.args(["--parseable", "--depth", "0"])
.current_dir(app_dir)
.current_dir(frontend_dir)
.output()?,
None,
),
@@ -173,7 +219,7 @@ impl PackageManager {
.arg("list")
.arg(name)
.args(["version", "--depth", "0"])
.current_dir(app_dir)
.current_dir(frontend_dir)
.output()?,
None,
),

View File

@@ -52,7 +52,7 @@ pub fn parse<P: AsRef<Path>>(path: P) -> crate::Result<Pbxproj> {
State::XCBuildConfigurationObject { id } => {
if line.contains("buildSettings") {
state = State::XCBuildConfigurationObjectBuildSettings { id: id.clone() };
} else if split_at_identation(line).map_or(false, |(_ident, token)| token == "};") {
} else if split_at_identation(line).is_some_and(|(_ident, token)| token == "};") {
state = State::XCBuildConfiguration;
}
}
@@ -122,7 +122,7 @@ pub fn parse<P: AsRef<Path>>(path: P) -> crate::Result<Pbxproj> {
State::XCConfigurationListObject { id } => {
if line.contains("buildConfigurations") {
state = State::XCConfigurationListObjectBuildConfigurations { id: id.clone() };
} else if split_at_identation(line).map_or(false, |(_ident, token)| token == "};") {
} else if split_at_identation(line).is_some_and(|(_ident, token)| token == "};") {
state = State::XCConfigurationList;
}
}

View File

@@ -69,6 +69,8 @@ pub fn known_plugins() -> HashMap<&'static str, PluginMetadata> {
"shell",
"upload",
"websocket",
"opener",
"clipboard-manager",
] {
plugins.entry(p).or_default();
}

View File

@@ -4,7 +4,9 @@
use anyhow::Context;
use base64::Engine;
use minisign::{sign, KeyPair as KP, SecretKey, SecretKeyBox, SignatureBox};
use minisign::{
sign, KeyPair as KP, PublicKey, PublicKeyBox, SecretKey, SecretKeyBox, SignatureBox,
};
use std::{
fs::{self, File, OpenOptions},
io::{BufReader, BufWriter, Write},
@@ -132,15 +134,24 @@ pub fn secret_key<S: AsRef<[u8]>>(
private_key: S,
password: Option<String>,
) -> crate::Result<SecretKey> {
let decoded_secret = decode_key(private_key)?;
let sk_box = SecretKeyBox::from_string(&decoded_secret)
.with_context(|| "failed to load updater private key")?;
let decoded_secret = decode_key(private_key).context("failed to decode base64 secret key")?;
let sk_box =
SecretKeyBox::from_string(&decoded_secret).context("failed to load updater private key")?;
let sk = sk_box
.into_secret_key(password)
.with_context(|| "incorrect updater private key password")?;
.context("incorrect updater private key password")?;
Ok(sk)
}
/// Gets the updater secret key from the given private key and password.
pub fn pub_key<S: AsRef<[u8]>>(public_key: S) -> crate::Result<PublicKey> {
let decoded_publick = decode_key(public_key).context("failed to decode base64 pubkey")?;
let pk_box =
PublicKeyBox::from_string(&decoded_publick).context("failed to load updater pubkey")?;
let pk = pk_box.into_public_key()?;
Ok(pk)
}
fn unix_timestamp() -> u64 {
let start = SystemTime::now();
let since_the_epoch = start

View File

@@ -267,13 +267,13 @@ fn ico(source: &Source, out_dir: &Path) -> Result<()> {
Ok(())
}
// Generate .png files in 32x32, 128x128, 256x256, 512x512 (icon.png)
// Generate .png files in 32x32, 64x64, 128x128, 256x256, 512x512 (icon.png)
// Main target: Linux
fn png(source: &Source, out_dir: &Path, ios_color: Rgba<u8>) -> Result<()> {
fn desktop_entries(out_dir: &Path) -> Vec<PngEntry> {
let mut entries = Vec::new();
for size in [32, 128, 256, 512] {
for size in [32, 64, 128, 256, 512] {
let file_name = match size {
256 => "128x128@2x.png".to_string(),
512 => "icon.png".to_string(),

View File

@@ -10,7 +10,7 @@ use std::{
};
use tauri_utils::platform::Target;
pub fn items(app_dir: Option<&PathBuf>, tauri_dir: Option<&Path>) -> Vec<SectionItem> {
pub fn items(frontend_dir: Option<&PathBuf>, tauri_dir: Option<&Path>) -> Vec<SectionItem> {
let mut items = Vec::new();
if tauri_dir.is_some() {
if let Ok(config) = crate::helpers::config::get(Target::current(), None) {
@@ -41,8 +41,8 @@ pub fn items(app_dir: Option<&PathBuf>, tauri_dir: Option<&Path>) -> Vec<Section
items.push(SectionItem::new().description(format!("devUrl: {dev_url}")));
}
if let Some(app_dir) = app_dir {
if let Ok(package_json) = read_to_string(app_dir.join("package.json")) {
if let Some(frontend_dir) = frontend_dir {
if let Ok(package_json) = read_to_string(frontend_dir.join("package.json")) {
let (framework, bundler) = framework::infer_from_package_json(&package_json);
if let Some(framework) = framework {

View File

@@ -5,23 +5,7 @@
use super::{ActionResult, SectionItem, VersionMetadata};
use colored::Colorize;
use crate::helpers::cross_command;
pub fn manager_version(package_manager: &str) -> Option<String> {
cross_command(package_manager)
.arg("-v")
.output()
.map(|o| {
if o.status.success() {
let v = String::from_utf8_lossy(o.stdout.as_slice()).to_string();
Some(v.split('\n').next().unwrap().to_string())
} else {
None
}
})
.ok()
.unwrap_or_default()
}
use crate::helpers::{cross_command, npm::manager_version};
pub fn items(metadata: &VersionMetadata) -> Vec<SectionItem> {
let node_target_ver = metadata.js_cli.node.replace(">= ", "");

View File

@@ -175,17 +175,45 @@ fn is_xcode_command_line_tools_installed() -> bool {
.map(|o| o.status.success())
.unwrap_or(false)
}
fn de_and_session() -> String {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
return {
let de = std::env::var("XDG_SESSION_DESKTOP");
let session = std::env::var("XDG_SESSION_TYPE");
format!(
" ({} on {})",
de.as_deref().unwrap_or("Unknown DE"),
session.as_deref().unwrap_or("Unknown Session")
)
};
#[cfg(not(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
)))]
String::new()
}
pub fn items() -> Vec<SectionItem> {
vec![
SectionItem::new().action(|| {
let os_info = os_info::get();
format!(
"OS: {} {} {} ({:?})",
"OS: {} {} {} ({:?}){}",
os_info.os_type(),
os_info.version(),
os_info.architecture().unwrap_or("Unknown Architecture"),
os_info.bitness()
os_info.bitness(),
de_and_session(),
).into()
}),
#[cfg(windows)]

View File

@@ -2,7 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::Result;
use crate::{
helpers::app_paths::{resolve_frontend_dir, resolve_tauri_dir},
Result,
};
use clap::Parser;
use colored::{ColoredString, Colorize};
use dialoguer::{theme::ColorfulTheme, Confirm};
@@ -258,15 +261,15 @@ pub struct Options {
pub fn command(options: Options) -> Result<()> {
let Options { interactive } = options;
let app_dir = crate::helpers::app_paths::resolve_app_dir();
let tauri_dir = crate::helpers::app_paths::resolve_tauri_dir();
let frontend_dir = resolve_frontend_dir();
let tauri_dir = resolve_tauri_dir();
if tauri_dir.is_some() {
// safe to initialize
crate::helpers::app_paths::resolve();
}
let package_manager = app_dir
let package_manager = frontend_dir
.as_ref()
.map(packages_nodejs::package_manager)
.unwrap_or(crate::helpers::npm::PackageManager::Npm);
@@ -288,11 +291,12 @@ pub fn command(options: Options) -> Result<()> {
interactive,
items: Vec::new(),
};
packages
.items
.extend(packages_rust::items(app_dir.as_ref(), tauri_dir.as_deref()));
packages.items.extend(packages_rust::items(
frontend_dir.as_ref(),
tauri_dir.as_deref(),
));
packages.items.extend(packages_nodejs::items(
app_dir.as_ref(),
frontend_dir.as_ref(),
package_manager,
&metadata,
));
@@ -300,7 +304,7 @@ pub fn command(options: Options) -> Result<()> {
let mut plugins = Section {
label: "Plugins",
interactive,
items: plugins::items(app_dir.as_ref(), tauri_dir.as_deref(), package_manager),
items: plugins::items(frontend_dir.as_ref(), tauri_dir.as_deref(), package_manager),
};
let mut app = Section {
@@ -310,7 +314,7 @@ pub fn command(options: Options) -> Result<()> {
};
app
.items
.extend(app::items(app_dir.as_ref(), tauri_dir.as_deref()));
.extend(app::items(frontend_dir.as_ref(), tauri_dir.as_deref()));
environment.display();
packages.display();

View File

@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT
use super::SectionItem;
use super::{env_nodejs::manager_version, VersionMetadata};
use super::VersionMetadata;
use colored::Colorize;
use serde::Deserialize;
use std::path::PathBuf;
@@ -76,8 +76,8 @@ pub fn npm_latest_version(pm: &PackageManager, name: &str) -> crate::Result<Opti
}
}
pub fn package_manager(app_dir: &PathBuf) -> PackageManager {
let found = PackageManager::from_project(app_dir);
pub fn package_manager(frontend_dir: &PathBuf) -> PackageManager {
let found = PackageManager::all_from_project(frontend_dir);
if found.is_empty() {
println!(
@@ -98,30 +98,22 @@ pub fn package_manager(app_dir: &PathBuf) -> PackageManager {
);
}
if pkg_manager == PackageManager::Yarn
&& manager_version("yarn")
.map(|v| v.chars().next().map(|c| c > '1').unwrap_or_default())
.unwrap_or(false)
{
PackageManager::YarnBerry
} else {
pkg_manager
}
pkg_manager
}
pub fn items(
app_dir: Option<&PathBuf>,
frontend_dir: Option<&PathBuf>,
package_manager: PackageManager,
metadata: &VersionMetadata,
) -> Vec<SectionItem> {
let mut items = Vec::new();
if let Some(app_dir) = app_dir {
if let Some(frontend_dir) = frontend_dir {
for (package, version) in [
("@tauri-apps/api", None),
("@tauri-apps/cli", Some(metadata.js_cli.version.clone())),
] {
let app_dir = app_dir.clone();
let item = nodejs_section_item(package.into(), version, app_dir, package_manager);
let frontend_dir = frontend_dir.clone();
let item = nodejs_section_item(package.into(), version, frontend_dir, package_manager);
items.push(item);
}
}
@@ -132,13 +124,13 @@ pub fn items(
pub fn nodejs_section_item(
package: String,
version: Option<String>,
app_dir: PathBuf,
frontend_dir: PathBuf,
package_manager: PackageManager,
) -> SectionItem {
SectionItem::new().action(move || {
let version = version.clone().unwrap_or_else(|| {
package_manager
.current_package_version(&package, &app_dir)
.current_package_version(&package, &frontend_dir)
.unwrap_or_default()
.unwrap_or_default()
});

View File

@@ -13,10 +13,10 @@ use colored::Colorize;
use std::fs::read_to_string;
use std::path::{Path, PathBuf};
pub fn items(app_dir: Option<&PathBuf>, tauri_dir: Option<&Path>) -> Vec<SectionItem> {
pub fn items(frontend_dir: Option<&PathBuf>, tauri_dir: Option<&Path>) -> Vec<SectionItem> {
let mut items = Vec::new();
if tauri_dir.is_some() || app_dir.is_some() {
if tauri_dir.is_some() || frontend_dir.is_some() {
if let Some(tauri_dir) = tauri_dir {
let manifest: Option<CargoManifest> =
if let Ok(manifest_contents) = read_to_string(tauri_dir.join("Cargo.toml")) {

View File

@@ -19,13 +19,13 @@ use crate::{
use super::{packages_nodejs, packages_rust, SectionItem};
pub fn items(
app_dir: Option<&PathBuf>,
frontend_dir: Option<&PathBuf>,
tauri_dir: Option<&Path>,
package_manager: PackageManager,
) -> Vec<SectionItem> {
let mut items = Vec::new();
if tauri_dir.is_some() || app_dir.is_some() {
if tauri_dir.is_some() || frontend_dir.is_some() {
if let Some(tauri_dir) = tauri_dir {
let manifest: Option<CargoManifest> =
if let Ok(manifest_contents) = fs::read_to_string(tauri_dir.join("Cargo.toml")) {
@@ -48,14 +48,18 @@ pub fn items(
let item = packages_rust::rust_section_item(&dep, crate_version);
items.push(item);
let Some(app_dir) = app_dir else {
let Some(frontend_dir) = frontend_dir else {
continue;
};
let package = format!("@tauri-apps/plugin-{p}");
let item =
packages_nodejs::nodejs_section_item(package, None, app_dir.clone(), package_manager);
let item = packages_nodejs::nodejs_section_item(
package,
None,
frontend_dir.clone(),
package_manager,
);
items.push(item);
}
}

View File

@@ -131,10 +131,7 @@ impl Options {
)
})?;
let detected_package_manager = match PackageManager::from_project(&self.directory).first() {
Some(&package_manager) => package_manager,
None => PackageManager::Npm,
};
let detected_package_manager = PackageManager::from_project(&self.directory);
self.before_dev_command = self
.before_dev_command

View File

@@ -20,7 +20,10 @@ pub use rust::{MobileOptions, Options, Rust as AppInterface};
pub trait DevProcess {
fn kill(&self) -> std::io::Result<()>;
fn try_wait(&self) -> std::io::Result<Option<ExitStatus>>;
// TODO:
#[allow(unused)]
fn wait(&self) -> std::io::Result<ExitStatus>;
#[allow(unused)]
fn manually_killed_process(&self) -> bool;
}

View File

@@ -18,7 +18,7 @@ use anyhow::Context;
use glob::glob;
use ignore::gitignore::{Gitignore, GitignoreBuilder};
use notify::RecursiveMode;
use notify_debouncer_mini::new_debouncer;
use notify_debouncer_full::new_debouncer;
use serde::{Deserialize, Deserializer};
use tauri_bundler::{
AppCategory, AppImageSettings, BundleBinary, BundleSettings, DebianSettings, DmgSettings,
@@ -29,7 +29,7 @@ use tauri_utils::config::{parse::is_configuration_file, DeepLinkProtocol, Update
use super::{AppSettings, DevProcess, ExitReason, Interface};
use crate::{
helpers::{
app_paths::{app_dir, tauri_dir},
app_paths::{frontend_dir, tauri_dir},
config::{nsis_settings, reload as reload_config, wix_settings, BundleResources, Config},
},
ConfigValue,
@@ -124,15 +124,13 @@ impl Interface for Rust {
fn new(config: &Config, target: Option<String>) -> crate::Result<Self> {
let manifest = {
let (tx, rx) = sync_channel(1);
let mut watcher = new_debouncer(Duration::from_secs(1), move |r| {
let mut watcher = new_debouncer(Duration::from_secs(1), None, move |r| {
if let Ok(events) = r {
let _ = tx.send(events);
}
})
.unwrap();
watcher
.watcher()
.watch(&tauri_dir().join("Cargo.toml"), RecursiveMode::Recursive)?;
watcher.watch(tauri_dir().join("Cargo.toml"), RecursiveMode::Recursive)?;
let (manifest, _modified) = rewrite_manifest(config)?;
let now = Instant::now();
let timeout = Duration::from_secs(2);
@@ -147,9 +145,9 @@ impl Interface for Rust {
manifest
};
let target_ios = target.as_ref().map_or(false, |target| {
target.ends_with("ios") || target.ends_with("ios-sim")
});
let target_ios = target
.as_ref()
.is_some_and(|target| target.ends_with("ios") || target.ends_with("ios-sim"));
if target_ios {
std::env::set_var(
"IPHONEOS_DEPLOYMENT_TARGET",
@@ -362,32 +360,6 @@ fn lookup<F: FnMut(FileType, PathBuf)>(dir: &Path, mut f: F) {
}
}
fn shared_options(
mobile: bool,
args: &mut Vec<String>,
features: &mut Option<Vec<String>>,
app_settings: &RustAppSettings,
) {
if mobile {
args.push("--lib".into());
features
.get_or_insert(Vec::new())
.push("tauri/rustls-tls".into());
} else {
args.push("--bins".into());
let all_features = app_settings
.manifest
.lock()
.unwrap()
.all_enabled_features(if let Some(f) = features { f } else { &[] });
if !all_features.contains(&"tauri/rustls-tls".into()) {
features
.get_or_insert(Vec::new())
.push("tauri/native-tls".into());
}
}
}
fn dev_options(
mobile: bool,
args: &mut Vec<String>,
@@ -408,7 +380,9 @@ fn dev_options(
}
*args = dev_args;
shared_options(mobile, args, features, app_settings);
if mobile {
args.push("--lib".into());
}
if !args.contains(&"--no-default-features".into()) {
let manifest_features = app_settings.manifest.lock().unwrap().features();
@@ -488,7 +462,11 @@ impl Rust {
features
.get_or_insert(Vec::new())
.push("tauri/custom-protocol".into());
shared_options(mobile, args, features, &self.app_settings);
if mobile {
args.push("--lib".into());
} else {
args.push("--bins".into());
}
}
fn run_dev<F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static>(
@@ -502,8 +480,6 @@ impl Rust {
run_args,
&mut self.available_targets,
self.config_features.clone(),
&self.app_settings,
self.main_binary_name.clone(),
on_exit,
)
.map(|c| Box::new(c) as Box<dyn DevProcess + Send>)
@@ -518,7 +494,7 @@ impl Rust {
let process = Arc::new(Mutex::new(child));
let (tx, rx) = sync_channel(1);
let app_path = app_dir();
let frontend_path = frontend_dir();
let watch_folders = get_watch_folders()?;
@@ -526,7 +502,7 @@ impl Rust {
.expect("watch_folders should not be empty");
let ignore_matcher = build_ignore_matcher(&common_ancestor);
let mut watcher = new_debouncer(Duration::from_secs(1), move |r| {
let mut watcher = new_debouncer(Duration::from_secs(1), None, move |r| {
if let Ok(events) = r {
tx.send(events).unwrap()
}
@@ -538,7 +514,7 @@ impl Rust {
lookup(&path, |file_type, p| {
if p != path {
log::debug!("Watching {} for changes...", display_path(&p));
let _ = watcher.watcher().watch(
let _ = watcher.watch(
&p,
if file_type.is_dir() {
RecursiveMode::Recursive
@@ -554,38 +530,42 @@ impl Rust {
loop {
if let Ok(events) = rx.recv() {
for event in events {
let event_path = event.path;
if event.kind.is_access() {
continue;
}
if !ignore_matcher.is_ignore(&event_path, event_path.is_dir()) {
if is_configuration_file(self.app_settings.target, &event_path) {
if let Ok(config) = reload_config(config.as_ref()) {
let (manifest, modified) =
rewrite_manifest(config.lock().unwrap().as_ref().unwrap())?;
if modified {
*self.app_settings.manifest.lock().unwrap() = manifest;
// no need to run the watcher logic, the manifest was modified
// and it will trigger the watcher again
continue;
if let Some(event_path) = event.paths.first() {
if !ignore_matcher.is_ignore(event_path, event_path.is_dir()) {
if is_configuration_file(self.app_settings.target, event_path) {
if let Ok(config) = reload_config(config.as_ref()) {
let (manifest, modified) =
rewrite_manifest(config.lock().unwrap().as_ref().unwrap())?;
if modified {
*self.app_settings.manifest.lock().unwrap() = manifest;
// no need to run the watcher logic, the manifest was modified
// and it will trigger the watcher again
continue;
}
}
}
}
log::info!(
"File {} changed. Rebuilding application...",
display_path(event_path.strip_prefix(app_path).unwrap_or(&event_path))
);
log::info!(
"File {} changed. Rebuilding application...",
display_path(event_path.strip_prefix(frontend_path).unwrap_or(event_path))
);
let mut p = process.lock().unwrap();
p.kill().with_context(|| "failed to kill app process")?;
let mut p = process.lock().unwrap();
p.kill().with_context(|| "failed to kill app process")?;
// wait for the process to exit
// note that on mobile, kill() already waits for the process to exit (duct implementation)
loop {
if !matches!(p.try_wait(), Ok(None)) {
break;
// wait for the process to exit
// note that on mobile, kill() already waits for the process to exit (duct implementation)
loop {
if !matches!(p.try_wait(), Ok(None)) {
break;
}
}
*p = run(self)?;
}
*p = run(self)?;
}
}
}
@@ -718,9 +698,7 @@ impl CargoSettings {
toml_file
.read_to_string(&mut toml_str)
.with_context(|| "failed to read Cargo.toml")?;
toml::from_str(&toml_str)
.with_context(|| "failed to parse Cargo.toml")
.map_err(Into::into)
toml::from_str(&toml_str).with_context(|| "failed to parse Cargo.toml")
}
}
@@ -863,6 +841,26 @@ impl AppSettings for RustAppSettings {
});
}
if let Some(open) = config.plugins.0.get("shell").and_then(|v| v.get("open")) {
if open.as_bool().is_some_and(|x| x) || open.is_string() {
settings.appimage.bundle_xdg_open = true;
}
}
if let Some(deps) = self
.manifest
.lock()
.unwrap()
.inner
.as_table()
.get("dependencies")
.and_then(|f| f.as_table())
{
if deps.contains_key("tauri-plugin-opener") {
settings.appimage.bundle_xdg_open = true;
};
}
Ok(settings)
}
@@ -906,7 +904,9 @@ impl AppSettings for RustAppSettings {
}
}
let mut binaries_paths = std::fs::read_dir(tauri_dir().join("src/bin"))
let tauri_dir = tauri_dir();
let mut binaries_paths = std::fs::read_dir(tauri_dir.join("src/bin"))
.map(|dir| {
dir
.into_iter()
@@ -929,11 +929,11 @@ impl AppSettings for RustAppSettings {
if !binaries_paths
.iter()
.any(|(_name, path)| path == Path::new("src/main.rs"))
&& tauri_dir().join("src/main.rs").exists()
&& tauri_dir.join("src/main.rs").exists()
{
binaries_paths.push((
self.cargo_package_settings.name.clone(),
tauri_dir().join("src/main.rs"),
tauri_dir.join("src/main.rs"),
));
}
@@ -998,8 +998,9 @@ impl AppSettings for RustAppSettings {
impl RustAppSettings {
pub fn new(config: &Config, manifest: Manifest, target: Option<String>) -> crate::Result<Self> {
let tauri_dir = tauri_dir();
let cargo_settings =
CargoSettings::load(tauri_dir()).with_context(|| "failed to load cargo settings")?;
CargoSettings::load(tauri_dir).with_context(|| "failed to load cargo settings")?;
let cargo_package_settings = match &cargo_settings.package {
Some(package_info) => package_info.clone(),
None => {
@@ -1073,7 +1074,7 @@ impl RustAppSettings {
default_run: cargo_package_settings.default_run.clone(),
};
let cargo_config = CargoConfig::load(tauri_dir())?;
let cargo_config = CargoConfig::load(tauri_dir)?;
let target_triple = target.unwrap_or_else(|| {
cargo_config
@@ -1227,6 +1228,9 @@ fn tauri_config_to_bundle_settings(
#[allow(unused_mut)]
let mut depends_rpm = config.linux.rpm.depends.unwrap_or_default();
#[allow(unused_mut)]
let mut appimage_files = config.linux.appimage.files;
// set env vars used by the bundler and inject dependencies
#[cfg(target_os = "linux")]
{
@@ -1269,7 +1273,12 @@ fn tauri_config_to_bundle_settings(
}
}
std::env::set_var("TAURI_TRAY_LIBRARY_PATH", path);
// conditionally setting it in case the user provided its own version for some reason
let path = PathBuf::from(path);
if !appimage_files.contains_key(&path) {
// manually construct target path, just in case the source path is something unexpected
appimage_files.insert(Path::new("/usr/lib/").join(path.file_name().unwrap()), path);
}
}
depends_deb.push("libwebkit2gtk-4.1-0".to_string());
@@ -1346,6 +1355,7 @@ fn tauri_config_to_bundle_settings(
} else {
Some(depends_deb)
},
recommends: config.linux.deb.recommends,
provides: config.linux.deb.provides,
conflicts: config.linux.deb.conflicts,
replaces: config.linux.deb.replaces,
@@ -1360,7 +1370,9 @@ fn tauri_config_to_bundle_settings(
post_remove_script: config.linux.deb.post_remove_script,
},
appimage: AppImageSettings {
files: config.linux.appimage.files,
files: appimage_files,
bundle_media_framework: config.linux.appimage.bundle_media_framework,
bundle_xdg_open: false,
},
rpm: RpmSettings {
depends: if depends_rpm.is_empty() {
@@ -1368,6 +1380,7 @@ fn tauri_config_to_bundle_settings(
} else {
Some(depends_rpm)
},
recommends: config.linux.rpm.recommends,
provides: config.linux.rpm.provides,
conflicts: config.linux.rpm.conflicts,
obsoletes: config.linux.rpm.obsoletes,
@@ -1379,6 +1392,7 @@ fn tauri_config_to_bundle_settings(
post_install_script: config.linux.rpm.post_install_script,
pre_remove_script: config.linux.rpm.pre_remove_script,
post_remove_script: config.linux.rpm.post_remove_script,
compression: config.linux.rpm.compression,
},
dmg: DmgSettings {
background: config.macos.dmg.background,
@@ -1493,7 +1507,7 @@ mod pkgconfig_utils {
if !output.stdout.is_empty() {
// output would be "-L/path/to/library\n"
let word = output.stdout[2..].to_vec();
return Some(String::from_utf8_lossy(&word).trim().to_string());
Some(String::from_utf8_lossy(&word).trim().to_string())
} else {
None
}

View File

@@ -20,39 +20,22 @@ use std::{
pub struct DevChild {
manually_killed_app: Arc<AtomicBool>,
build_child: Option<Arc<SharedChild>>,
app_child: Arc<Mutex<Option<Arc<SharedChild>>>>,
dev_child: Arc<SharedChild>,
}
impl DevProcess for DevChild {
fn kill(&self) -> std::io::Result<()> {
if let Some(child) = &*self.app_child.lock().unwrap() {
child.kill()?;
} else if let Some(child) = &self.build_child {
child.kill()?;
}
self.dev_child.kill()?;
self.manually_killed_app.store(true, Ordering::Relaxed);
Ok(())
}
fn try_wait(&self) -> std::io::Result<Option<ExitStatus>> {
if let Some(child) = &*self.app_child.lock().unwrap() {
child.try_wait()
} else if let Some(child) = &self.build_child {
child.try_wait()
} else {
unreachable!()
}
self.dev_child.try_wait()
}
fn wait(&self) -> std::io::Result<ExitStatus> {
if let Some(child) = &*self.app_child.lock().unwrap() {
child.wait()
} else if let Some(child) = &self.build_child {
child.wait()
} else {
unreachable!()
}
self.dev_child.wait()
}
fn manually_killed_process(&self) -> bool {
@@ -65,58 +48,103 @@ pub fn run_dev<F: Fn(Option<i32>, ExitReason) + Send + Sync + 'static>(
run_args: Vec<String>,
available_targets: &mut Option<Vec<RustupTarget>>,
config_features: Vec<String>,
app_settings: &RustAppSettings,
main_binary_name: Option<String>,
on_exit: F,
) -> crate::Result<impl DevProcess> {
let bin_path = app_settings.app_binary_path(&options)?;
let mut dev_cmd = cargo_command(true, options, available_targets, config_features)?;
let runner = dev_cmd.get_program().to_string_lossy().into_owned();
dev_cmd
.env(
"CARGO_TERM_PROGRESS_WIDTH",
terminal::stderr_width()
.map(|width| {
if cfg!(windows) {
std::cmp::min(60, width)
} else {
width
}
})
.unwrap_or(if cfg!(windows) { 60 } else { 80 })
.to_string(),
)
.env("CARGO_TERM_PROGRESS_WHEN", "always");
dev_cmd.arg("--color");
dev_cmd.arg("always");
// TODO: double check this
dev_cmd.stdout(os_pipe::dup_stdout()?);
dev_cmd.stderr(Stdio::piped());
dev_cmd.arg("--");
dev_cmd.args(run_args);
let manually_killed_app = Arc::new(AtomicBool::default());
let manually_killed_app_ = manually_killed_app.clone();
let app_child = Arc::new(Mutex::new(None));
let app_child_ = app_child.clone();
let build_child = build_dev_app(
options,
available_targets,
config_features,
move |status, reason| {
if status == Some(0) {
let main_binary_name = main_binary_name.as_deref();
let bin_path = rename_app(bin_path, main_binary_name).expect("failed to rename app");
log::info!(action = "Running"; "DevCommand (`{} {}`)", &dev_cmd.get_program().to_string_lossy(), dev_cmd.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
let mut app = Command::new(bin_path);
app.stdout(os_pipe::dup_stdout().unwrap());
app.stderr(os_pipe::dup_stderr().unwrap());
app.args(run_args);
let app_child = Arc::new(SharedChild::spawn(&mut app).unwrap());
crate::dev::wait_dev_process(
DevChild {
manually_killed_app: manually_killed_app_,
build_child: None,
app_child: Arc::new(Mutex::new(Some(app_child.clone()))),
},
on_exit,
);
app_child_.lock().unwrap().replace(app_child);
let dev_child = match SharedChild::spawn(&mut dev_cmd) {
Ok(c) => Ok(c),
Err(e) if e.kind() == ErrorKind::NotFound => Err(anyhow::anyhow!(
"`{}` command not found.{}",
runner,
if runner == "cargo" {
" Please follow the Tauri setup guide: https://v2.tauri.app/start/prerequisites/"
} else {
on_exit(
status,
if manually_killed_app_.load(Ordering::Relaxed) {
ExitReason::TriggeredKill
} else {
reason
},
);
""
}
},
)?;
)),
Err(e) => Err(e.into()),
}?;
let dev_child = Arc::new(dev_child);
let dev_child_stderr = dev_child.take_stderr().unwrap();
let mut stderr = BufReader::new(dev_child_stderr);
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
let stderr_lines_ = stderr_lines.clone();
std::thread::spawn(move || {
let mut buf = Vec::new();
let mut lines = stderr_lines_.lock().unwrap();
let mut io_stderr = std::io::stderr();
loop {
buf.clear();
if let Ok(0) = tauri_utils::io::read_line(&mut stderr, &mut buf) {
break;
}
let _ = io_stderr.write_all(&buf);
lines.push(String::from_utf8_lossy(&buf).into_owned());
}
});
let dev_child_ = dev_child.clone();
std::thread::spawn(move || {
let status = dev_child_.wait().expect("failed to build app");
if status.success() {
on_exit(status.code(), ExitReason::NormalExit);
} else {
let is_cargo_compile_error = stderr_lines
.lock()
.unwrap()
.last()
.map(|l| l.contains("could not compile"))
.unwrap_or_default();
stderr_lines.lock().unwrap().clear();
on_exit(
status.code(),
if status.code() == Some(101) && is_cargo_compile_error {
ExitReason::CompilationFailed
} else if manually_killed_app_.load(Ordering::Relaxed) {
ExitReason::TriggeredKill
} else {
ExitReason::NormalExit
},
);
}
});
Ok(DevChild {
manually_killed_app,
build_child: Some(build_child),
app_child,
dev_child,
})
}
@@ -130,7 +158,7 @@ pub fn build(
let out_dir = app_settings.out_dir(&options)?;
let bin_path = app_settings.app_binary_path(&options)?;
if !std::env::var("STATIC_VCRUNTIME").map_or(false, |v| v == "false") {
if !std::env::var("STATIC_VCRUNTIME").is_ok_and(|v| v == "false") {
std::env::set_var("STATIC_VCRUNTIME", "true");
}
@@ -172,102 +200,12 @@ pub fn build(
rename_app(bin_path, main_binary_name)
}
fn build_dev_app<F: FnOnce(Option<i32>, ExitReason) + Send + 'static>(
options: Options,
available_targets: &mut Option<Vec<RustupTarget>>,
config_features: Vec<String>,
on_exit: F,
) -> crate::Result<Arc<SharedChild>> {
let mut build_cmd = build_command(options, available_targets, config_features)?;
let runner = build_cmd.get_program().to_string_lossy().into_owned();
build_cmd
.env(
"CARGO_TERM_PROGRESS_WIDTH",
terminal::stderr_width()
.map(|width| {
if cfg!(windows) {
std::cmp::min(60, width)
} else {
width
}
})
.unwrap_or(if cfg!(windows) { 60 } else { 80 })
.to_string(),
)
.env("CARGO_TERM_PROGRESS_WHEN", "always");
build_cmd.arg("--color");
build_cmd.arg("always");
build_cmd.stdout(os_pipe::dup_stdout()?);
build_cmd.stderr(Stdio::piped());
let build_child = match SharedChild::spawn(&mut build_cmd) {
Ok(c) => Ok(c),
Err(e) if e.kind() == ErrorKind::NotFound => Err(anyhow::anyhow!(
"`{}` command not found.{}",
runner,
if runner == "cargo" {
" Please follow the Tauri setup guide: https://v2.tauri.app/start/prerequisites/"
} else {
""
}
)),
Err(e) => Err(e.into()),
}?;
let build_child = Arc::new(build_child);
let build_child_stderr = build_child.take_stderr().unwrap();
let mut stderr = BufReader::new(build_child_stderr);
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
let stderr_lines_ = stderr_lines.clone();
std::thread::spawn(move || {
let mut buf = Vec::new();
let mut lines = stderr_lines_.lock().unwrap();
let mut io_stderr = std::io::stderr();
loop {
buf.clear();
if let Ok(0) = tauri_utils::io::read_line(&mut stderr, &mut buf) {
break;
}
let _ = io_stderr.write_all(&buf);
lines.push(String::from_utf8_lossy(&buf).into_owned());
}
});
let build_child_ = build_child.clone();
std::thread::spawn(move || {
let status = build_child_.wait().expect("failed to build app");
if status.success() {
on_exit(status.code(), ExitReason::NormalExit);
} else {
let is_cargo_compile_error = stderr_lines
.lock()
.unwrap()
.last()
.map(|l| l.contains("could not compile"))
.unwrap_or_default();
stderr_lines.lock().unwrap().clear();
on_exit(
status.code(),
if status.code() == Some(101) && is_cargo_compile_error {
ExitReason::CompilationFailed
} else {
ExitReason::NormalExit
},
);
}
});
Ok(build_child)
}
fn build_production_app(
options: Options,
available_targets: &mut Option<Vec<RustupTarget>>,
config_features: Vec<String>,
) -> crate::Result<()> {
let mut build_cmd = build_command(options, available_targets, config_features)?;
let mut build_cmd = cargo_command(false, options, available_targets, config_features)?;
let runner = build_cmd.get_program().to_string_lossy().into_owned();
match build_cmd.piped() {
Ok(status) if status.success() => Ok(()),
@@ -285,13 +223,17 @@ fn build_production_app(
}
}
fn build_command(
fn cargo_command(
dev: bool,
options: Options,
available_targets: &mut Option<Vec<RustupTarget>>,
config_features: Vec<String>,
) -> crate::Result<Command> {
let runner = options.runner.unwrap_or_else(|| "cargo".into());
let mut build_cmd = Command::new(runner);
build_cmd.arg(if dev { "run" } else { "build" });
if let Some(target) = &options.target {
if available_targets.is_none() {
*available_targets = fetch_available_targets();
@@ -299,33 +241,26 @@ fn build_command(
validate_target(available_targets, target)?;
}
let mut args = Vec::new();
if !options.args.is_empty() {
args.extend(options.args);
}
build_cmd.args(&options.args);
let mut features = config_features;
if let Some(f) = options.features {
features.extend(f);
}
if !features.is_empty() {
args.push("--features".into());
args.push(features.join(","));
build_cmd.arg("--features");
build_cmd.arg(features.join(","));
}
if !options.debug && !args.contains(&"--profile".to_string()) {
args.push("--release".into());
if !options.debug && !options.args.contains(&"--profile".to_string()) {
build_cmd.arg("--release");
}
if let Some(target) = options.target {
args.push("--target".into());
args.push(target);
build_cmd.arg("--target");
build_cmd.arg(target);
}
let mut build_cmd = Command::new(runner);
build_cmd.arg("build");
build_cmd.args(args);
Ok(build_cmd)
}
@@ -440,7 +375,7 @@ mod terminal {
// INVALID_HANDLE_VALUE. Use an alternate method which works
// in that case as well.
let h = CreateFileA(
"CONOUT$\0".as_ptr() as PCSTR,
c"CONOUT$".as_ptr() as PCSTR,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
ptr::null_mut(),

View File

@@ -2,8 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! [![](https://github.com/tauri-apps/tauri/raw/dev/.github/splash.png)](https://tauri.app)
//!
//! This Rust executable provides the full interface to all of the required activities for which the CLI is required. It will run on macOS, Windows, and Linux.
#![doc(
@@ -30,6 +28,7 @@ mod interface;
mod migrate;
mod mobile;
mod plugin;
mod remove;
mod signer;
use clap::{ArgAction, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
@@ -146,6 +145,7 @@ enum Commands {
Migrate,
Info(info::Options),
Add(add::Options),
Remove(remove::Options),
Plugin(plugin::Cli),
Icon(icon::Options),
Signer(signer::Cli),
@@ -265,6 +265,7 @@ where
Commands::Bundle(options) => bundle::command(options, cli.verbose)?,
Commands::Dev(options) => dev::command(options)?,
Commands::Add(options) => add::command(options)?,
Commands::Remove(options) => remove::command(options)?,
Commands::Icon(options) => icon::command(options)?,
Commands::Info(options) => info::command(options)?,
Commands::Init(options) => init::command(options)?,
@@ -318,7 +319,7 @@ impl CommandExt for Command {
let program = self.get_program().to_string_lossy().into_owned();
log::debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
self.status().map_err(Into::into)
self.status()
}
fn output_ok(&mut self) -> crate::Result<Output> {

View File

@@ -21,7 +21,7 @@ pub fn migrate(tauri_dir: &Path) -> Result<MigratedConfig> {
tauri_utils_v1::config::parse::parse_value(tauri_dir.join("tauri.conf.json"))
{
let migrated = migrate_config(&mut config)?;
if config_path.extension().map_or(false, |ext| ext == "toml") {
if config_path.extension().is_some_and(|ext| ext == "toml") {
fs::write(&config_path, toml::to_string_pretty(&config)?)?;
} else {
fs::write(&config_path, serde_json::to_string_pretty(&config)?)?;
@@ -87,6 +87,28 @@ fn migrate_config(config: &mut Value) -> Result<MigratedConfig> {
migrated.permissions = permissions;
}
// dangerousUseHttpScheme/useHttpsScheme
let dangerouse_use_http = tauri_config
.get("security")
.and_then(|w| w.as_object())
.and_then(|w| {
w.get("dangerousUseHttpScheme")
.or_else(|| w.get("dangerous-use-http-scheme"))
})
.and_then(|v| v.as_bool())
.unwrap_or_default();
if let Some(windows) = tauri_config
.get_mut("windows")
.and_then(|w| w.as_array_mut())
{
for window in windows {
if let Some(window) = window.as_object_mut() {
window.insert("useHttpsScheme".to_string(), (!dangerouse_use_http).into());
}
}
}
// security
if let Some(security) = tauri_config
.get_mut("security")
@@ -684,6 +706,15 @@ mod test {
let mut migrated = original.clone();
super::migrate_config(&mut migrated).expect("failed to migrate config");
if original.get("$schema").is_some() {
if let Some(map) = migrated.as_object_mut() {
map.insert(
"$schema".to_string(),
serde_json::Value::String("https://schema.tauri.app/config/2".to_string()),
);
}
}
if original
.get("tauri")
.and_then(|v| v.get("bundle"))
@@ -708,6 +739,7 @@ mod test {
#[test]
fn migrate_full() {
let original = serde_json::json!({
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"distDir": "../dist",
"devPath": "http://localhost:1240",
@@ -792,12 +824,16 @@ mod test {
"pattern": { "use": "brownfield" },
"security": {
"csp": "default-src 'self' tauri:"
}
},
"windows": [{}]
}
});
let migrated = migrate(&original);
// $schema
assert_eq!(migrated["$schema"], "https://schema.tauri.app/config/2");
// plugins > updater
assert_eq!(
migrated["plugins"]["updater"]["endpoints"],
@@ -894,6 +930,8 @@ mod test {
migrated["app"]["withGlobalTauri"],
original["build"]["withGlobalTauri"]
);
assert_eq!(migrated["app"]["windows"][0]["useHttpsScheme"], true);
}
#[test]
@@ -928,6 +966,28 @@ mod test {
);
}
#[test]
fn migrate_dangerous_use_http_scheme() {
let original = serde_json::json!({
"tauri": {
"windows": [{}],
"security": {
"dangerousUseHttpScheme": true,
}
}
});
let migrated = migrate(&original);
assert_eq!(
!migrated["app"]["windows"][0]["useHttpsScheme"]
.as_bool()
.unwrap(),
original["tauri"]["security"]["dangerousUseHttpScheme"]
.as_bool()
.unwrap()
);
}
#[test]
fn can_migrate_default_config() {
let original = serde_json::to_value(tauri_utils_v1::config::Config::default()).unwrap();

View File

@@ -68,7 +68,7 @@ const MODULES_MAP: phf::Map<&str, &str> = phf::phf_map! {
const JS_EXTENSIONS: &[&str] = &["js", "mjs", "jsx", "ts", "mts", "tsx", "svelte", "vue"];
/// Returns a list of migrated plugins
pub fn migrate(app_dir: &Path) -> Result<Vec<String>> {
pub fn migrate(frontend_dir: &Path) -> Result<Vec<String>> {
let mut new_npm_packages = Vec::new();
let mut new_plugins = Vec::new();
let mut npm_packages_to_remove = Vec::new();
@@ -84,14 +84,11 @@ pub fn migrate(app_dir: &Path) -> Result<Vec<String>> {
)
};
let pm = PackageManager::from_project(app_dir)
.into_iter()
.next()
.unwrap_or(PackageManager::Npm);
let pm = PackageManager::from_project(frontend_dir);
for pkg in ["@tauri-apps/cli", "@tauri-apps/api"] {
let version = pm
.current_package_version(pkg, app_dir)
.current_package_version(pkg, frontend_dir)
.unwrap_or_default()
.unwrap_or_default();
if version.starts_with('1') {
@@ -99,7 +96,7 @@ pub fn migrate(app_dir: &Path) -> Result<Vec<String>> {
}
}
for entry in walk_builder(app_dir).build().flatten() {
for entry in walk_builder(frontend_dir).build().flatten() {
if entry.file_type().map(|t| t.is_file()).unwrap_or_default() {
let path = entry.path();
let ext = path.extension().unwrap_or_default();
@@ -122,14 +119,14 @@ pub fn migrate(app_dir: &Path) -> Result<Vec<String>> {
if !npm_packages_to_remove.is_empty() {
npm_packages_to_remove.sort();
npm_packages_to_remove.dedup();
pm.remove(&npm_packages_to_remove, app_dir)
pm.remove(&npm_packages_to_remove, frontend_dir)
.context("Error removing npm packages")?;
}
if !new_npm_packages.is_empty() {
new_npm_packages.sort();
new_npm_packages.dedup();
pm.install(&new_npm_packages, app_dir)
pm.install(&new_npm_packages, frontend_dir)
.context("Error installing new npm packages")?;
}
@@ -146,7 +143,7 @@ fn migrate_imports<'a>(
let has_partial_js = path
.extension()
.map_or(false, |ext| ext == "vue" || ext == "svelte");
.is_some_and(|ext| ext == "vue" || ext == "svelte");
let sources = if !has_partial_js {
vec![(SourceType::from_path(path).unwrap(), js_source, 0i64)]
@@ -538,13 +535,13 @@ function App() {
<h1>Welcome to Tauri!</h1>
<div className="row">
<a href="https://vitejs.dev" target="_blank">
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" className="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
</a>
<a href="https://reactjs.org" target="_blank">
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
@@ -612,13 +609,13 @@ function App() {
<h1>Welcome to Tauri!</h1>
<div className="row">
<a href="https://vitejs.dev" target="_blank">
<a href="https://vite.dev" target="_blank">
<img src="/vite.svg" className="logo vite" alt="Vite logo" />
</a>
<a href="https://tauri.app" target="_blank">
<img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
</a>
<a href="https://reactjs.org" target="_blank">
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>

Some files were not shown because too many files have changed in this diff Show More