Compare commits

...

38 Commits

Author SHA1 Message Date
Caelan Sayler
1d5c984c14 Fix azure folder uploading and align with s3 implementation 2025-09-24 23:56:49 +01:00
azegallo
095567789a Add folder support to Azure blob storage commands
This change adds a --folder parameter to both 'vpk upload az' and 'vpk download az' commands, allowing users to organize releases in subdirectories within their Azure blob containers.

Key changes:
- Added Folder property to AzureUploadOptions and AzureDownloadOptions
- Modified AzureRepository to prepend folder paths to all blob operations
- Updated upload, download, and delete operations to work with folder paths
- Retention policy (--keepMaxReleases) correctly handles files in folders
- Release index and legacy release files are stored in the specified folder

This enables multiple applications or environments to share a single Azure container by using folders as isolated namespaces. For example:
- vpk upload az --folder "releases/v1" ...
- vpk download az --folder "releases/v1" ...

The implementation is backward compatible - existing deployments without folders continue to work as before.
2025-09-24 23:56:49 +01:00
Caelan Sayler
09f9df1d11 Add download progress to sample app 2025-09-24 23:30:56 +01:00
Caelan Sayler
3df255b8a6 Release GIL using py.allow_threads while long running rust operations are running 2025-09-24 23:30:56 +01:00
JessicaTegner
ea50914cea Python: Added QT sample app (PySide6) 2025-09-24 23:30:56 +01:00
JessicaTegner
935a36c6ea python: Acquire GIL on each loop iteration when calling callback 2025-09-24 23:30:56 +01:00
Ben Evans
84f5fb9d44 added new icon copy in linux command runner 2025-09-24 09:58:45 +01:00
Remco Fischer
fa48e3c56c chore: Update gitea
System.Text.Json 9 is now removed as it is not used
2025-08-19 14:01:04 +01:00
Caelan Sayler
5d569607fc Update ImageSharp (GHSA-rxmq-m78w-7wmc) 2025-08-17 11:53:54 +01:00
Caelan Sayler
78e6b6f683 Remove trailing slashes from symbolic link paths 2025-08-17 11:38:02 +01:00
Caelan Sayler
031bd9b63a New symbolic link implementation for legacy windows 2025-08-17 11:38:02 +01:00
Caelan Sayler
efa9d296b0 Move NCode.ReparsePoints to test project 2025-08-17 11:38:02 +01:00
Monty C
c9c31029ff On mac, move MacOS/sq.version to Resources/sq.version and symlink it. Workaround for code signature not being preserved after updates. 2025-08-17 11:01:49 +01:00
Caelan Sayler
65df343dea Revert "Update Gitea package to fix #708"
This reverts commit 1f5df34aff.
2025-08-17 11:00:22 +01:00
Caelan Sayler
37aa43d6cb Fix rust bundle lifetime 2025-08-16 16:57:55 +01:00
Caelan Sayler
1f5df34aff Update Gitea package to fix #708 2025-08-16 16:51:07 +01:00
Caelan
bb7dd08b99 Update README.md 2025-07-27 20:36:17 +01:00
Caelan
5f2cf75f74 Update README.md 2025-07-27 19:59:01 +01:00
Caelan
50c44adbc7 Update README.md 2025-07-27 19:40:38 +01:00
Caelan Sayler
3ba32af42b Add python missing UM methods and fix apply signature 2025-06-27 13:41:03 +01:00
Caelan Sayler
a2c559072b update c++ docs also 2025-06-27 12:58:30 +01:00
Caelan Sayler
023fdc5150 Update the lib-cpp rust docs 2025-06-27 12:58:30 +01:00
Caelan Sayler
ef7f55691a Fix js typings for waitExitThenApplyUpdate and getUpdatePendingRestart 2025-06-27 12:58:23 +01:00
Caelan Sayler
4ba75beea8 Add more robust error handling to all the sample build scripts 2025-06-27 10:54:45 +01:00
Caelan Sayler
72af62649f Add unix build script and add to samples CI 2025-06-27 10:54:45 +01:00
Caelan Sayler
fee5cb2148 Provide release dir as absolute path during sample build 2025-06-27 10:54:45 +01:00
Caelan Sayler
35d6875b42 Update python sample to 3.9 2025-06-27 10:54:45 +01:00
JessicaTegner
42e3959c43 Updated samples app readme 2025-06-27 10:54:45 +01:00
JessicaTegner
ceebea0fdf Python sample app using WXPython 2025-06-27 10:54:45 +01:00
Marcus Souchon
c69f0cca6f fix: declare alloc_c_string before use in c++ wrapper 2025-06-27 08:47:39 +01:00
JessicaTegner
756bdc66f7 Python: Implement missing UpdaterManager functions (different ways of applying the update) 2025-06-26 22:17:45 +01:00
KennyTK
b378a6b9b9 Update GiteaSource.cs GetApiBaseUrl
Fix #690
2025-06-26 22:16:28 +01:00
Caelan Sayler
1ae63dc1e0 Disable renovate until MSI gets merged in 2025-06-26 21:52:45 +01:00
Caelan Sayler
2fc2597c29 Upload python library to release artifacts 2025-06-16 17:12:16 +01:00
Caelan Sayler
2ad2c3706a Disable free-threaded wheels and fix windows python version 2025-06-16 12:54:52 +01:00
renovate[bot]
989bdb4ea0 Lock file maintenance 2025-06-16 06:58:25 +00:00
Caelan Sayler
8bed8a185c Remove ts-rs and add new node handlebar template 2025-06-15 23:13:18 +01:00
Caelan Sayler
9c76a8d6b3 Remove test release artifact 2025-06-15 23:05:38 +01:00
88 changed files with 3170 additions and 632 deletions

View File

@@ -1,5 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"enabled": false,
"extends": [
"config:recommended",
":semanticCommitsDisabled"

View File

@@ -38,14 +38,14 @@ jobs:
args: --release --out dist
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: auto
- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
working-directory: src/lib-python
target: ${{ matrix.platform.target }}
args: --release --out dist -i python3.13t
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: auto
# - name: Build free-threaded wheels
# uses: PyO3/maturin-action@v1
# with:
# working-directory: src/lib-python
# target: ${{ matrix.platform.target }}
# args: --release --out dist -i python3.13t
# sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
# manylinux: auto
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
@@ -84,14 +84,14 @@ jobs:
args: --release --out dist
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: musllinux_1_2
- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
working-directory: src/lib-python
target: ${{ matrix.platform.target }}
args: --release --out dist -i python3.13t
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
manylinux: musllinux_1_2
# - name: Build free-threaded wheels
# uses: PyO3/maturin-action@v1
# with:
# working-directory: src/lib-python
# target: ${{ matrix.platform.target }}
# args: --release --out dist -i python3.13t
# sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
# manylinux: musllinux_1_2
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
@@ -124,7 +124,7 @@ jobs:
uses: ./.github/actions/job-setup
- uses: actions/setup-python@v5
with:
python-version: 3.13t
python-version: 3.x
architecture: ${{ matrix.platform.target }}
- name: Build wheels
uses: PyO3/maturin-action@v1
@@ -133,13 +133,13 @@ jobs:
target: ${{ matrix.platform.rust_target }}
args: --release --out dist
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
working-directory: src/lib-python
target: ${{ matrix.platform.rust_target }}
args: --release --out dist -i python3.13t
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
# - name: Build free-threaded wheels
# uses: PyO3/maturin-action@v1
# with:
# working-directory: src/lib-python
# target: ${{ matrix.platform.rust_target }}
# args: --release --out dist -i python3.13t
# sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
@@ -173,13 +173,13 @@ jobs:
target: ${{ matrix.platform.target }}
args: --release --out dist
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Build free-threaded wheels
uses: PyO3/maturin-action@v1
with:
working-directory: src/lib-python
target: ${{ matrix.platform.target }}
args: --release --out dist -i python3.13t
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
# - name: Build free-threaded wheels
# uses: PyO3/maturin-action@v1
# with:
# working-directory: src/lib-python
# target: ${{ matrix.platform.target }}
# args: --release --out dist -i python3.13t
# sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
- name: Upload wheels
uses: actions/upload-artifact@v4
with:

View File

@@ -7,7 +7,15 @@ jobs:
strategy:
fail-fast: false
matrix:
sample: [CPlusPlusWidgets, CPlusPlusWin32, CSharpAvalonia, CSharpUno, CSharpWpf, NodeJSElectron, RustIced]
sample:
- CPlusPlusWidgets
- CPlusPlusWin32
- CSharpAvalonia
- CSharpUno
- CSharpWpf
- NodeJSElectron
- PythonWXPython
- RustIced
os: [windows-latest, ubuntu-latest, macos-latest]
exclude:
- os: ubuntu-latest
@@ -44,6 +52,16 @@ jobs:
sudo apt-cache search libgtk*
sudo apt install -y libwxgtk3.2-dev libgtk-3-dev
if: ${{ matrix.sample == 'CPlusPlusWidgets' && matrix.os == 'ubuntu-latest' }}
- name: Install uv
run: |
pip install uv
if: ${{ matrix.sample == 'PythonWXPython' }}
- name: Refresh uv.lock for wxPython
working-directory: samples/${{ matrix.sample }}
run: |
rm uv.lock
uv sync --find-links https://extras.wxpython.org/wxPython4/extras/linux/gtk3/ubuntu-24.04
if: ${{ matrix.sample == 'PythonWXPython' && matrix.os == 'ubuntu-latest' }}
- name: Setup dotnet
uses: actions/setup-dotnet@v4
with:
@@ -51,6 +69,7 @@ jobs:
6.0.x
8.0.x
9.0.x
if: ${{ startsWith(matrix.sample, 'CSharp') }}
- name: Install VPK
run: dotnet tool install -g vpk
- name: Pack Sample (Windows)

View File

@@ -83,13 +83,6 @@ jobs:
mkdir -p ../lib-python
zip -r ../lib-python/velopack_libpython_$PKG_VERSION.zip .
- name: test-artifact
uses: actions/upload-artifact@v4
with:
name: test-artifact
path: lib-python/*
retention-days: 1
- uses: actions/checkout@v4
with:
path: ./repo
@@ -223,6 +216,7 @@ jobs:
gh release upload $currentTag (get-item ../lib-nodejs/*.tgz)
gh release upload $currentTag (get-item ../lib-rust/*.crate)
gh release upload $currentTag (get-item ../lib-c/*.zip)
gh release upload $currentTag (get-item ../lib-python/*.zip)
- name: Publish NuGet Packages
run: |

36
Cargo.lock generated
View File

@@ -1948,12 +1948,9 @@ dependencies = [
[[package]]
name = "slab"
version = "0.4.9"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]]
name = "smallvec"
@@ -2228,29 +2225,6 @@ version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
[[package]]
name = "ts-rs"
version = "10.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e640d9b0964e9d39df633548591090ab92f7a4567bc31d3891af23471a3365c6"
dependencies = [
"lazy_static",
"thiserror 2.0.12",
"ts-rs-macros",
]
[[package]]
name = "ts-rs-macros"
version = "10.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e9d8656589772eeec2cf7a8264d9cda40fb28b9bc53118ceb9e8c07f8f38730"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.103",
"termcolor",
]
[[package]]
name = "typenum"
version = "1.18.0"
@@ -2295,9 +2269,9 @@ dependencies = [
[[package]]
name = "ureq-proto"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36"
checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7"
dependencies = [
"base64",
"http",
@@ -2388,7 +2362,6 @@ dependencies = [
"tempfile",
"thiserror 2.0.12",
"time 0.3.41",
"ts-rs",
"ureq",
"url",
"uuid",
@@ -2476,7 +2449,6 @@ dependencies = [
"semver",
"serde_json",
"simplelog",
"ts-rs",
"velopack",
]

View File

@@ -43,7 +43,6 @@ regex = "1.10"
normpath = "1.3"
bitflags = "2.9"
rand = "0.9"
ts-rs = "10.0"
zstd = "0.13"
async-std = "1.13"
anyhow = "1.0"

View File

@@ -50,3 +50,21 @@ I've used a lot of installer frameworks and Velopack is by far the best. Everyth
I'm extremely impressed with Velopack's performance in creating releases, as well as checking for and applying updates. It is significantly faster than other tools. The vpk CLI is intuitive and easy to implement, even with my complex build pipeline. Thanks to Velopack, I've been able to streamline my workflow and save valuable time. It's a fantastic tool that I highly recommend!
[- khdc (Discord)](https://discord.com/channels/767856501477343282/947444323765583913/1216460920696344576)
## Sponsors
Huge thanks to everyone who provides the project with free tools, services, or donations!
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://velopack.github.io/velopack.sponsorkit/sponsors-white.svg">
<img alt="Sponsors Cloud" src="https://velopack.github.io/velopack.sponsorkit/sponsors-black.svg">
</picture>
<span>Free tools provided by<br/></span>
<a href="https://jb.gg/OpenSource">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://www.jetbrains.com/company/brand/img/logo_jb_dos_3.svg">
<img alt="Jetbrains Logo" src="https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg" width="160">
</picture>
</a>
</div>

View File

@@ -12,12 +12,17 @@ set "version=%~1"
echo.
echo Compiling velopack_libc...
cargo build -p velopack_libc
if errorlevel 1 exit /b 1
echo.
echo Compiling VelopackCppWidgets with cmake...
cmake -S . -B build-rel
if errorlevel 1 exit /b 1
cmake --build build-rel -j --config Release
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%version%
vpk pack -u VelopackCppWidgets -o %~dp0releases -p %~dp0build-rel\Release -e main.exe -v %*
vpk pack -u VelopackCppWidgets -o %~dp0releases -p %~dp0build-rel\Release -e main.exe -v %*
if errorlevel 1 exit /b 1

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

View File

@@ -15,6 +15,7 @@ if "%~1"=="" (
echo.
echo Building Velopack C Lib with Cargo
cargo build -p velopack_libc
if errorlevel 1 exit /b 1
cd %~dp0
@@ -39,9 +40,11 @@ echo.
echo Building CppWin32Sample
cd %~dp0
"%MSBUILD_PATH%" CppWin32Sample.sln
if errorlevel 1 exit /b 1
echo #define UPDATE_URL "REPLACE_ME" > constants.h
echo.
echo Building Velopack Release v%~1
vpk pack -u VelopackCppWin32Sample -o releases -p x64\Debug -v %* -e CppWin32Sample.exe
vpk pack -u VelopackCppWin32Sample -o releases -p x64\Debug -v %* -e CppWin32Sample.exe
if errorlevel 1 exit /b 1

View File

@@ -12,7 +12,9 @@ set "version=%~1"
echo.
echo Compiling VelopackCSharpAvalonia with dotnet...
dotnet publish -c Release --no-self-contained -r win-x64 -o %~dp0publish
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%version%
vpk pack -u VelopackCSharpAvalonia -o %~dp0releases -p %~dp0publish -f net8-x64-desktop -v %*
vpk pack -u VelopackCSharpAvalonia -o %~dp0releases -p %~dp0publish -f net8-x64-desktop -v %*
if errorlevel 1 exit /b 1

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

View File

@@ -17,10 +17,12 @@ cd %~dp0..\..\..\
echo.
echo Building Velopack Rust
cargo build --features windows
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Vpk
dotnet build src/vpk/Velopack.Vpk/Velopack.Vpk.csproj
if errorlevel 1 exit /b 1
cd %~dp0..
set "version=%~1"
@@ -28,7 +30,9 @@ set "version=%~1"
echo.
echo Compiling VelopackCSharpAvalonia with dotnet...
dotnet publish -c Release --self-contained -r win-x64 -o publish -p:UseLocalVelopack=true
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%version%
%~dp0..\..\..\build\Debug\net8.0\vpk pack -u VelopackCSharpAvalonia -o releases -p publish -v %*
%~dp0..\..\..\build\Debug\net8.0\vpk pack -u VelopackCSharpAvalonia -o releases -p publish -v %*
if errorlevel 1 exit /b 1

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

View File

@@ -12,7 +12,9 @@ set "version=%~1"
echo.
echo Compiling CSharpUno with dotnet...
dotnet publish -c Release --framework net9.0-desktop -o %~dp0UnoSample\publish UnoSample\UnoSample.csproj
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%version%
vpk pack -u CSharpUno -v %version% -o %~dp0UnoSample\releases -p %~dp0UnoSample\publish --mainExe UnoSample.exe
if errorlevel 1 exit /b 1

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# Check if version parameter is provided
if [ "$#" -ne 1 ]; then

View File

@@ -12,7 +12,9 @@ set "version=%~1"
echo.
echo Compiling VelopackCSharpWinForms with dotnet...
dotnet publish -c Release -o %~dp0publish
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%version%
vpk pack -u VelopackCSharpWinForms -v %version% -o %~dp0releases -p %~dp0publish -f net8-x64-desktop
vpk pack -u VelopackCSharpWinForms -v %version% -o %~dp0releases -p %~dp0publish -f net8-x64-desktop
if errorlevel 1 exit /b 1

View File

@@ -12,7 +12,9 @@ set "version=%~1"
echo.
echo Compiling VelopackCSharpWpf with dotnet...
dotnet publish -c Release -o %~dp0publish
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%version%
vpk pack -u VelopackCSharpWpf -v %version% -o %~dp0releases -p %~dp0publish -f net8-x64-desktop
vpk pack -u VelopackCSharpWpf -v %version% -o %~dp0releases -p %~dp0publish -f net8-x64-desktop
if errorlevel 1 exit /b 1

View File

@@ -14,33 +14,41 @@ cd %~dp0..\..\src\lib-nodejs
echo.
echo Running npm install
call npm install
if errorlevel 1 exit /b 1
echo.
echo Cleaning lib-nodejs
call npm run clean
if errorlevel 1 exit /b 1
echo.
echo Compiling lib-nodejs
call npm run dev
if errorlevel 1 exit /b 1
echo.
echo Packing lib-nodejs
call npm pack
if errorlevel 1 exit /b 1
cd %~dp0
echo.
echo Running npm install
call npm install
if errorlevel 1 exit /b 1
echo.
echo Installing lib-nodejs package
call npm install ..\..\src\lib-nodejs\velopack-0.0.0.tgz
if errorlevel 1 exit /b 1
echo.
echo Packing with electron-forge
call npm run package
if errorlevel 1 exit /b 1
echo.
echo Creating Velopack Release
call vpk pack -u VelopackElectronSample -v %version% -o %~dp0releases -p %~dp0out\VelopackElectronSample-win32-x64
call vpk pack -u VelopackElectronSample -v %version% -o %~dp0releases -p %~dp0out\VelopackElectronSample-win32-x64
if errorlevel 1 exit /b 1

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

View File

@@ -616,9 +616,9 @@
}
},
"node_modules/@electron/node-gyp/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -813,9 +813,9 @@
}
},
"node_modules/@electron/universal/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1317,13 +1317,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.15.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz",
"integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==",
"version": "24.0.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz",
"integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
"undici-types": "~7.8.0"
}
},
"node_modules/@types/node-forge": {
@@ -2042,9 +2042,9 @@
"optional": true
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2181,9 +2181,9 @@
}
},
"node_modules/cacache/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2306,9 +2306,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001721",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz",
"integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==",
"version": "1.0.30001723",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz",
"integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==",
"dev": true,
"funding": [
{
@@ -3760,9 +3760,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.165",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz",
"integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==",
"version": "1.5.167",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz",
"integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==",
"dev": true,
"license": "ISC"
},
@@ -3788,6 +3788,16 @@
"global-agent": "^3.0.0"
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "22.15.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.31.tgz",
"integrity": "sha512-jnVe5ULKl6tijxUhvQeNbQG/84fHfg+yMak02cT8QVhBx/F05rAVxCGBYYTh2EKz22D6JF5ktXuNwdx7b9iEGw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/electron/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
@@ -3823,6 +3833,13 @@
"semver": "bin/semver.js"
}
},
"node_modules/electron/node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/electron/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
@@ -7115,9 +7132,9 @@
}
},
"node_modules/postcss": {
"version": "8.5.4",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz",
"integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==",
"version": "8.5.5",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
"integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
"dev": true,
"funding": [
{
@@ -8318,9 +8335,9 @@
}
},
"node_modules/socks": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
"integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz",
"integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -8676,9 +8693,9 @@
}
},
"node_modules/terser": {
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz",
"integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==",
"version": "5.42.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.42.0.tgz",
"integrity": "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
@@ -9007,9 +9024,9 @@
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
"dev": true,
"license": "MIT"
},

205
samples/PythonQt/.gitignore vendored Normal file
View File

@@ -0,0 +1,205 @@
build/*
dist/*
Releases/*
*.zip
*.tar.gz
VelopackPythonSampleApp-win-Portable/*
.python-version
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Velopack
_build_config.py
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Cursor
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
# refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore

View File

@@ -0,0 +1 @@
update_url = R"D:\WinDev\Projects\velopack\samples\PythonPySide6\releases"

View File

@@ -0,0 +1,36 @@
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo Version number is required.
echo Usage: build.bat [version] [extra_args...]
exit /b 1
)
cd %~dp0
echo.
echo Installing dependencies
uv sync --reinstall-package velopack
if errorlevel 1 exit /b 1
echo update_url = R"%~dp0releases" > _build_config.py
echo.
echo Building Python application
uv run pyinstaller --onedir --windowed --noconfirm main.py
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%~1
mkdir publish 2>nul
xcopy /E /I /Y dist\main\* publish\ >nul
vpk pack --packId VelopackPythonPySide6App --mainExe main.exe -o releases --packDir publish --packVersion %*
if errorlevel 1 exit /b 1
echo.
echo Cleaning up
rmdir /S /Q build 2>nul
rmdir /S /Q dist 2>nul
rmdir /S /Q publish 2>nul
del /Q main.spec 2>nul

30
samples/PythonQt/build.sh Normal file
View File

@@ -0,0 +1,30 @@
#!/bin/bash
if [ -z "$1" ]; then
echo "Version number is required."
echo "Usage: ./build.sh [version] [extra_args...]"
exit 1
fi
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
cd "$SCRIPT_DIR"
echo
echo "Installing dependencies"
uv sync --reinstall-package velopack || exit 1
echo "update_url = R\"$SCRIPT_DIR/releases\"" > _build_config.py
echo
echo "Building Python application"
uv run pyinstaller --onedir --windowed --noconfirm main.py || exit 1
echo
echo "Building Velopack Release v$1"
mkdir -p publish
cp -r dist/main/* publish/
vpk pack --packId VelopackPythonPySide6App --mainExe main -o releases --packDir publish --packVersion "$@" || exit 1
echo
echo "Cleaning up"
rm -rf build dist publish main.spec

151
samples/PythonQt/main.py Normal file
View File

@@ -0,0 +1,151 @@
import logging
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QWidget, QPushButton, QTextEdit, QVBoxLayout, QHBoxLayout
from PySide6.QtCore import QTimer
import velopack
from _build_config import update_url
class BufferedLogHandler(logging.Handler):
"""Buffers log messages and provides access to them"""
def __init__(self):
super().__init__()
self.buffer = []
def emit(self, record):
msg = self.format(record)
self.buffer.append(msg)
def get_messages(self):
"""Get all buffered messages and clear the buffer"""
messages = self.buffer[:]
self.buffer.clear()
return messages
# Global buffered handler
log_handler = BufferedLogHandler()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Velopack Sample")
self.resize(600, 400)
# create velopacks update manager
self.update_manager = None
self.update_info = None
# Create central widget and main layout
central_widget = QWidget()
self.setCentralWidget(central_widget)
# Create buttons
self.check_btn = QPushButton("Check")
self.download_btn = QPushButton("Download")
self.apply_btn = QPushButton("Apply")
# Create log text control (multiline and read-only)
self.log_text = QTextEdit()
self.log_text.setReadOnly(True)
# Connect button events
self.check_btn.clicked.connect(self.on_check)
self.download_btn.clicked.connect(self.on_download)
self.apply_btn.clicked.connect(self.on_apply)
# Layout
self.setup_layout(central_widget)
# Start timer to check for log messages
self.timer = QTimer(self)
self.timer.timeout.connect(self.on_timer)
self.timer.start(5) # 5ms interval
logging.info("GUI initialized and ready")
def on_timer(self):
"""Check for new log messages and add them to text control"""
messages = log_handler.get_messages()
for msg in messages:
self.log_text.append(f"{msg}")
def setup_layout(self, widget):
# Create layouts
main_layout = QVBoxLayout()
button_layout = QHBoxLayout()
# Add buttons to horizontal layout
button_layout.addWidget(self.check_btn)
button_layout.addWidget(self.download_btn)
button_layout.addWidget(self.apply_btn)
button_layout.addStretch() # Add stretch to align buttons to left
# Add to main layout
main_layout.addLayout(button_layout)
main_layout.addWidget(self.log_text)
widget.setLayout(main_layout)
def on_check(self):
try:
self.update_manager = velopack.UpdateManager(update_url)
except Exception as e:
logging.error(f"Failed to initialize update manager: {e}")
return
self.update_info = self.update_manager.check_for_updates()
if self.update_info:
logging.info(f"Update available: {self.update_info}")
else:
logging.info("No updates available")
def on_download(self):
if not self.update_info:
logging.warning("No update information available. Please check first.")
return
try:
self.update_manager.download_updates(self.update_info)
logging.info("Update downloaded successfully")
except Exception as e:
logging.error(f"Failed to download update: {e}")
def on_apply(self):
if not self.update_info:
logging.warning("No update information available. Please check first.")
return
try:
self.update_manager.apply_updates_and_restart(self.update_info)
logging.info("Update applied successfully")
except Exception as e:
logging.error(f"Failed to apply update: {e}")
def setup_logging():
"""Setup logging to use buffered handler"""
logger = logging.getLogger()
# Remove existing handlers
for handler in logger.handlers[:]:
logger.removeHandler(handler)
# Setup our buffered handler
formatter = logging.Formatter('- %(levelname)s - %(message)s')
log_handler.setFormatter(formatter)
log_handler.setLevel(logging.INFO)
logger.addHandler(log_handler)
logger.setLevel(logging.INFO)
if __name__ == "__main__":
# Setup logging before velopack runs
setup_logging()
# Run velopack early
velopack.App().run()
# Create and run the Qt app
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())

View File

@@ -0,0 +1,18 @@
[project]
name = "pythonqt"
version = "0.1.0"
description = "Velopack sample app using PySide6 (Qt6)"
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
"velopack",
"PySide6>=6.9",
]
[tool.uv.sources]
velopack = { path = "../../src/lib-python" }
[dependency-groups]
dev = [
"pyinstaller>=6.14.1",
]

221
samples/PythonQt/uv.lock generated Normal file
View File

@@ -0,0 +1,221 @@
version = 1
revision = 2
requires-python = ">=3.9"
[[package]]
name = "altgraph"
version = "0.17.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/de/a8/7145824cf0b9e3c28046520480f207df47e927df83aa9555fb47f8505922/altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", size = 48418, upload-time = "2023-09-25T09:04:52.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212, upload-time = "2023-09-25T09:04:50.691Z" },
]
[[package]]
name = "importlib-metadata"
version = "8.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" },
]
[[package]]
name = "macholib"
version = "1.16.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "altgraph" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309, upload-time = "2023-09-25T09:10:16.155Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094, upload-time = "2023-09-25T09:10:14.188Z" },
]
[[package]]
name = "packaging"
version = "25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "pefile"
version = "2023.2.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854, upload-time = "2023-02-07T12:23:55.958Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791, upload-time = "2023-02-07T12:28:36.678Z" },
]
[[package]]
name = "pyinstaller"
version = "6.15.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "altgraph" },
{ name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "macholib", marker = "sys_platform == 'darwin'" },
{ name = "packaging" },
{ name = "pefile", marker = "sys_platform == 'win32'" },
{ name = "pyinstaller-hooks-contrib" },
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/64/17/b2bb4de22650adbeef401fa82a1b43028976547a8728602e4d29735b455e/pyinstaller-6.15.0.tar.gz", hash = "sha256:a48fc4644ee4aa2aa2a35e7b51f496f8fbd7eecf6a2150646bbf1613ad07bc2d", size = 4331521, upload-time = "2025-08-03T18:33:35.709Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/24/dd/d5c8a127446adda954f68ea7fac22772f7ab8656ad4b06df396d82574ca9/pyinstaller-6.15.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:9f00c71c40148cd1e61695b2c6f1e086693d3bcf9bfa22ab513aa4254c3b966f", size = 1016981, upload-time = "2025-08-03T18:31:52.034Z" },
{ url = "https://files.pythonhosted.org/packages/2d/2a/7b50593b419db43e48d9bdeebaac0ff92a5fe035f3c30f87ca3e1650d7e2/pyinstaller-6.15.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cbcc8eb77320c60722030ac875883b564e00768fe3ff1721c7ba3ad0e0a277e9", size = 726337, upload-time = "2025-08-03T18:31:57.592Z" },
{ url = "https://files.pythonhosted.org/packages/77/83/7f498fba0154c57eb5fc93eb9680a2dbadb9f780a3389fb85b8d79683378/pyinstaller-6.15.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c33e6302bc53db2df1104ed5566bd980b3e0ee7f18416a6e3caa908c12a54542", size = 737539, upload-time = "2025-08-03T18:32:02.221Z" },
{ url = "https://files.pythonhosted.org/packages/09/d6/e4477feab7c8379fb49e7ec95c82d0a69ad88f6ccc247f76bef3cb0e3432/pyinstaller-6.15.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:eb902d0fed3bb1f8b7190dc4df5c11f3b59505767e0d56d1ed782b853938bbf3", size = 735426, upload-time = "2025-08-03T18:32:06.485Z" },
{ url = "https://files.pythonhosted.org/packages/32/7e/ff25648276f15e2e77fc563d36d8cfcd917e077bf2a172420df3588601b4/pyinstaller-6.15.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:b4df862adae7cf1f08eff53c43ace283822447f7f528f72e4f94749062712f15", size = 732210, upload-time = "2025-08-03T18:32:21.667Z" },
{ url = "https://files.pythonhosted.org/packages/db/3d/267a7dddd0647de95d260780050ccd8228ab29d2b9edea54ed1f56800967/pyinstaller-6.15.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:b9ebf16ed0f99016ae8ae5746dee4cb244848a12941539e62ce2eea1df5a3f95", size = 732194, upload-time = "2025-08-03T18:32:29.536Z" },
{ url = "https://files.pythonhosted.org/packages/4d/61/962b2eb79ef225233e2d6e04600e998935328011dfb2fa775b1dd16b943a/pyinstaller-6.15.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:22193489e6a22435417103f61e7950363bba600ef36ec3ab1487303668c81092", size = 731256, upload-time = "2025-08-03T18:32:36.069Z" },
{ url = "https://files.pythonhosted.org/packages/67/5e/4e20e1c0e5791b09b69bef3ac921fd0cd25551b56879324ad999b92fa045/pyinstaller-6.15.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:18f743069849dbaee3e10900385f35795a5743eabab55e99dcc42f204e40a0db", size = 731148, upload-time = "2025-08-03T18:32:41.269Z" },
{ url = "https://files.pythonhosted.org/packages/88/31/28956c534991f289e2f981c715730b6241e75dc6295737a8cbd050a0cc8c/pyinstaller-6.15.0-py3-none-win32.whl", hash = "sha256:60da8f1b5071766b45c0f607d8bc3d7e59ba2c3b262d08f2e4066ba65f3544a2", size = 1312297, upload-time = "2025-08-03T18:32:50.572Z" },
{ url = "https://files.pythonhosted.org/packages/09/ab/6a45186c7f8e34c422faecd72580116a67d068158c57faa2d2f6d01faa7f/pyinstaller-6.15.0-py3-none-win_amd64.whl", hash = "sha256:cbea297e16eeda30b41c300d6ec2fd2abea4dbd8d8a32650eeec36431c94fcd9", size = 1373091, upload-time = "2025-08-03T18:32:58.133Z" },
{ url = "https://files.pythonhosted.org/packages/5b/86/72159af032b9db36f2470a3b085f79277ec1c38e7e48f8c5dc1ed16dc4e1/pyinstaller-6.15.0-py3-none-win_arm64.whl", hash = "sha256:f43c035621742cf2d19b84308c60e4e44e72c94786d176b8f6adcde351b5bd98", size = 1314305, upload-time = "2025-08-03T18:33:05.557Z" },
]
[[package]]
name = "pyinstaller-hooks-contrib"
version = "2025.8"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "packaging" },
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/71/d6/e5b378b7d4add8c879295c531309b0320e9c07a70458665d091760ffdc87/pyinstaller_hooks_contrib-2025.8.tar.gz", hash = "sha256:3402ad41dfe9b5110af134422e37fc5d421ba342c6cb980bd67cb30b7415641c", size = 164214, upload-time = "2025-07-27T16:37:31.943Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/48/34/1d973d0dae849683e53fbcda84443ce016f315e6f4dc7605ede4f56a28c3/pyinstaller_hooks_contrib-2025.8-py3-none-any.whl", hash = "sha256:8d0b8cfa0cb689a619294ae200497374234bd4e3994b3ace2a4442274c899064", size = 442346, upload-time = "2025-07-27T16:37:30.268Z" },
]
[[package]]
name = "pyside6"
version = "6.9.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyside6-addons" },
{ name = "pyside6-essentials" },
{ name = "shiboken6" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/91/8e9c7f7e90431297de9856e90a156ade9420977e26d87996909c63f30bd2/PySide6-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:f843ef39970a2f79757810fffd7b8e93ac42a3de9ea62f2a03648cde57648aed", size = 558097, upload-time = "2025-06-03T13:20:03.739Z" },
{ url = "https://files.pythonhosted.org/packages/d7/ff/04d1b6b30edd24d761cc30d964860f997bdf37d06620694bf9aab35eec3a/PySide6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:db44ac08b8f7ac1b421bc1c6a44200d03f08d80dc7b3f68dfdb1684f30f41c17", size = 558239, upload-time = "2025-06-03T13:20:06.205Z" },
{ url = "https://files.pythonhosted.org/packages/3c/b4/ca076c55c11a8e473363e05aa82c5c03dd7ba8f17b77cc9311ce17213193/PySide6-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:531a6e67c429b045674d57fe9864b711eb59e4cded753c2640982e368fd468d1", size = 558239, upload-time = "2025-06-03T13:20:08.257Z" },
{ url = "https://files.pythonhosted.org/packages/83/ff/95c941f53b0faebc27dbe361d8e971b77f504b9cf36f8f5d750fd82cd6fc/PySide6-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:c82dbb7d32bbdd465e01059174f71bddc97de152ab71bded3f1907c40f9a5f16", size = 564571, upload-time = "2025-06-03T13:20:10.321Z" },
{ url = "https://files.pythonhosted.org/packages/d1/ef/0aa5e910fa4e9770db6b45c23e360a52313922e0ca71fc060a57db613de1/PySide6-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:1525d63dc6dc425b8c2dc5bc01a8cb1d67530401449f3a3490c09a14c095b9f9", size = 401793, upload-time = "2025-06-03T13:20:12.108Z" },
]
[[package]]
name = "pyside6-addons"
version = "6.9.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pyside6-essentials" },
{ name = "shiboken6" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/e7/e2/39b9e04335d7ac782b6459bf7abec90c36b8efaac5a88ef818e972c59387/PySide6_Addons-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:7be0708fa89715c282541fca47e2ba97c0c8d2886e0236ef994b2dd8f52aacdd", size = 316212438, upload-time = "2025-06-03T13:06:15.027Z" },
{ url = "https://files.pythonhosted.org/packages/cf/6f/691d7039a6f7943522a770b713ecd85fa169688dfdd65ddd4db1699d01b6/PySide6_Addons-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:da7869b02e3599d26546fad582db4656060786bc5ec8ece5ec9ee8aa8b42371c", size = 166690468, upload-time = "2025-06-03T13:06:34.962Z" },
{ url = "https://files.pythonhosted.org/packages/9d/08/a264db09ad35819643d910cd4c73a86f72f23b7092f8ebc7e51dcca53a86/PySide6_Addons-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:53fd08c8152b6ba8c435458afd189835ba905793a5077a2bb0b1b11222b375d4", size = 162466096, upload-time = "2025-06-03T13:08:58.065Z" },
{ url = "https://files.pythonhosted.org/packages/84/be/a849402f7e73d137b5ae8b4370a49b0cf0e0c02f028b845782cb743e4995/PySide6_Addons-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:cd93a3a5e3886cd958f3a5acc7c061c24f10a394ce9f4ce657ac394544ca7ec2", size = 143150906, upload-time = "2025-06-03T13:09:12.762Z" },
{ url = "https://files.pythonhosted.org/packages/2a/f1/1bb6b5859aff4e2b3f5ef789b9cee200811a9f469f04d9aa7425e816622b/PySide6_Addons-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:4f589631bdceb518080ae9c9fa288e64f092cd5bebe25adc8ad89e8eadd4db29", size = 26938762, upload-time = "2025-06-03T13:09:20.009Z" },
]
[[package]]
name = "pyside6-essentials"
version = "6.9.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "shiboken6" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/59/714874db9ef3bbbbda654fd3223248969bea02ec1a5bfdd1c941c4e97749/PySide6_Essentials-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:ed43435a70e018e1c22efcaf34a9430b83cfcad716dba661b03de21c13322fab", size = 132957077, upload-time = "2025-06-03T13:11:52.629Z" },
{ url = "https://files.pythonhosted.org/packages/59/6a/ea0db68d40a1c487fd255634896f4e37b6560e3ef1f57ca5139bf6509b1f/PySide6_Essentials-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:e5da48883f006c6206ef85874db74ddebcdf69b0281bd4f1642b1c5ac1d54aea", size = 96416183, upload-time = "2025-06-03T13:12:48.945Z" },
{ url = "https://files.pythonhosted.org/packages/5b/2f/4243630d1733522638c4967d36018c38719d8b84f5246bf3d4c010e0aa9d/PySide6_Essentials-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:e46a2801c9c6098025515fd0af6c594b9e9c951842f68b8f6f3da9858b9b26c2", size = 94171343, upload-time = "2025-06-03T13:12:59.426Z" },
{ url = "https://files.pythonhosted.org/packages/0d/a9/a8e0209ba9116f2c2db990cfb79f2edbd5a3a428013be2df1f1cddd660a9/PySide6_Essentials-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:ad1ac94011492dba33051bc33db1c76a7d6f815a81c01422cb6220273b369145", size = 72435676, upload-time = "2025-06-03T13:13:08.805Z" },
{ url = "https://files.pythonhosted.org/packages/d0/e4/23268c57e775a1a4d2843d288a9583a47f2e4b3977a9ae93cb9ded1a4ea5/PySide6_Essentials-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:35c2c2bb4a88db74d11e638cf917524ff35785883f10b439ead07960a5733aa4", size = 49483707, upload-time = "2025-06-03T13:13:16.399Z" },
]
[[package]]
name = "pythonqt"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "pyside6" },
{ name = "velopack" },
]
[package.dev-dependencies]
dev = [
{ name = "pyinstaller" },
]
[package.metadata]
requires-dist = [
{ name = "pyside6", specifier = ">=6.9" },
{ name = "velopack", directory = "../../src/lib-python" },
]
[package.metadata.requires-dev]
dev = [{ name = "pyinstaller", specifier = ">=6.14.1" }]
[[package]]
name = "pywin32-ctypes"
version = "0.2.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
]
[[package]]
name = "setuptools"
version = "80.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
]
[[package]]
name = "shiboken6"
version = "6.9.1"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/98/98/34d4d25b79055959b171420d47fcc10121aefcbb261c91d5491252830e31/shiboken6-6.9.1-cp39-abi3-macosx_12_0_universal2.whl", hash = "sha256:40e92afc88da06b5100c56b761e59837ff282166e9531268f3d910b6128e621e", size = 406159, upload-time = "2025-06-03T13:16:45.104Z" },
{ url = "https://files.pythonhosted.org/packages/5a/07/53b2532ecd42ff925feb06b7bb16917f5f99f9c3470f0815c256789d818b/shiboken6-6.9.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:efcdfa8655d34aaf8d7a0c7724def3440bd46db02f5ad3b1785db5f6ccb0a8ff", size = 206756, upload-time = "2025-06-03T13:16:46.528Z" },
{ url = "https://files.pythonhosted.org/packages/5e/b0/75b86ee3f7b044e6a87fbe7abefd1948ca4ae5fcde8321f4986a1d9eaa5e/shiboken6-6.9.1-cp39-abi3-manylinux_2_39_aarch64.whl", hash = "sha256:efcf75d48a29ae072d0bf54b3cd5a59ae91bb6b3ab7459e17c769355486c2e0b", size = 203233, upload-time = "2025-06-03T13:16:48.264Z" },
{ url = "https://files.pythonhosted.org/packages/30/56/00af281275aab4c79e22e0ea65feede0a5c6da3b84e86b21a4a0071e0744/shiboken6-6.9.1-cp39-abi3-win_amd64.whl", hash = "sha256:209ccf02c135bd70321143dcbc5023ae0c056aa4850a845955dd2f9b2ff280a9", size = 1153587, upload-time = "2025-06-03T13:16:50.454Z" },
{ url = "https://files.pythonhosted.org/packages/de/ce/6ccd382fbe1a96926c5514afa6f2c42da3a9a8482e61f8dfc6068a9ca64f/shiboken6-6.9.1-cp39-abi3-win_arm64.whl", hash = "sha256:2a39997ce275ced7853defc89d3a1f19a11c90991ac6eef3435a69bb0b7ff1de", size = 1831623, upload-time = "2025-06-03T13:16:52.468Z" },
]
[[package]]
name = "velopack"
version = "0.0.1"
source = { directory = "../../src/lib-python" }
[package.metadata]
[package.metadata.requires-dev]
dev = [
{ name = "maturin", specifier = ">=1.8.6" },
{ name = "pyinstaller", specifier = ">=6.13.0" },
]
test = [{ name = "pyinstaller", specifier = ">=6.13.0" }]
[[package]]
name = "zipp"
version = "3.23.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
]

205
samples/PythonWXPython/.gitignore vendored Normal file
View File

@@ -0,0 +1,205 @@
build/*
dist/*
Releases/*
*.zip
*.tar.gz
VelopackPythonSampleApp-win-Portable/*
.python-version
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Velopack
_build_config.py
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Abstra
# Abstra is an AI-powered process automation framework.
# Ignore directories containing user credentials, local state, and settings.
# Learn more at https://abstra.io/docs
.abstra/
# Visual Studio Code
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
# and can be added to the global gitignore or merged into this file. However, if you prefer,
# you could uncomment the following to ignore the entire vscode folder
# .vscode/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc
# Cursor
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
# refer to https://docs.cursor.com/context/ignore-files
.cursorignore
.cursorindexingignore

View File

View File

@@ -0,0 +1,36 @@
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (
echo Version number is required.
echo Usage: build.bat [version] [extra_args...]
exit /b 1
)
cd %~dp0
echo.
echo Installing dependencies
uv sync --reinstall-package velopack
if errorlevel 1 exit /b 1
echo update_url = R"%~dp0releases" > _build_config.py
echo.
echo Building Python application
uv run pyinstaller --onedir --windowed --noconfirm main.py
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%~1
mkdir publish 2>nul
xcopy /E /I /Y dist\main\* publish\ >nul
vpk pack --packId VelopackPythonSampleApp --mainExe main.exe -o releases --packDir publish --packVersion %*
if errorlevel 1 exit /b 1
echo.
echo Cleaning up
rmdir /S /Q build 2>nul
rmdir /S /Q dist 2>nul
rmdir /S /Q publish 2>nul
del /Q main.spec 2>nul

45
samples/PythonWXPython/build.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/bin/bash
set -e
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Check if version parameter is provided
if [ "$#" -lt 1 ]; then
echo "Version number is required."
echo "Usage: ./build.sh [version] [extra_args...]"
exit 1
fi
cd "$SCRIPT_DIR"
# Installing dependencies
echo ""
echo "Installing dependencies..."
uv sync
# Generate build config
# Note: In Python, we use r'' for raw strings. The path needs to be absolute.
echo "update_url = r'$SCRIPT_DIR/releases'" > _build_config.py
# Building Python application
echo ""
echo "Building Python application..."
uv run pyinstaller --onedir --windowed --noconfirm main.py
# Building Velopack Release
echo ""
echo "Building Velopack Release v$1..."
mkdir -p publish
cp -r dist/main/* publish/
vpk pack --packId VelopackPythonSampleApp --mainExe main -o releases --packDir publish --packVersion "$@"
# Cleaning up
echo ""
echo "Cleaning up..."
rm -rf build
rm -rf dist
rm -rf publish
rm -f main.spec
echo "Done."

View File

@@ -0,0 +1,185 @@
import logging
import threading
import wx
import velopack
from _build_config import update_url
class BufferedLogHandler(logging.Handler):
"""Buffers log messages and provides access to them"""
def __init__(self):
super().__init__()
self.buffer = []
def emit(self, record):
msg = self.format(record)
self.buffer.append(msg)
def get_messages(self):
"""Get all buffered messages and clear the buffer"""
messages = self.buffer[:]
self.buffer.clear()
return messages
# Global buffered handler
log_handler = BufferedLogHandler()
class MainFrame(wx.Frame):
def __init__(self):
super().__init__(None, title="Velopack Sample", size=(600, 400))
# create velopacks update manager
self.update_manager = None
self.update_info = None
# Create main panel
panel = wx.Panel(self)
# Create buttons with minimum width to prevent text cutoff
self.check_btn = wx.Button(panel, label="Check", size=(150, -1))
self.download_btn = wx.Button(panel, label="Download", size=(150, -1))
self.apply_btn = wx.Button(panel, label="Apply", size=(150, -1))
# Start with Download and Apply disabled
self.download_btn.Disable()
self.apply_btn.Disable()
# Create log text control (multiline and read-only)
self.log_text = wx.TextCtrl(panel,
style=wx.TE_MULTILINE | wx.TE_READONLY)
# Bind button events
self.check_btn.Bind(wx.EVT_BUTTON, self.on_check)
self.download_btn.Bind(wx.EVT_BUTTON, self.on_download)
self.apply_btn.Bind(wx.EVT_BUTTON, self.on_apply)
# Layout
self.setup_layout(panel)
# Center the frame
self.Center()
# Start timer to check for log messages
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.on_timer)
self.timer.Start(5)
logging.info("GUI initialized and ready")
def on_timer(self, event):
"""Check for new log messages and add them to text control"""
messages = log_handler.get_messages()
for msg in messages:
self.log_text.AppendText(f"{msg}\n")
def setup_layout(self, panel):
# Create sizers
main_sizer = wx.BoxSizer(wx.VERTICAL)
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
# Add buttons to horizontal sizer
button_sizer.Add(self.check_btn, 0, wx.ALL, 5)
button_sizer.Add(self.download_btn, 0, wx.ALL, 5)
button_sizer.Add(self.apply_btn, 0, wx.ALL, 5)
# Add to main sizer
main_sizer.Add(button_sizer, 0, wx.ALL | wx.EXPAND, 10)
main_sizer.Add(self.log_text, 1, wx.ALL | wx.EXPAND, 10)
panel.SetSizer(main_sizer)
def on_check(self, event):
try:
self.update_manager = velopack.UpdateManager(update_url)
except Exception as e:
logging.error(f"Failed to initialize update manager: {e}")
return
self.update_info = self.update_manager.check_for_updates()
if self.update_info:
logging.info(f"Update available: {self.update_info}")
# Enable download and apply buttons when update is available
self.download_btn.Enable()
self.apply_btn.Enable()
else:
logging.info("No updates available")
# Keep buttons disabled if no update
self.download_btn.Disable()
self.apply_btn.Disable()
def on_download(self, event):
if not self.update_info:
logging.warning("No update information available. Please check first.")
return
# Disable buttons during download
self.download_btn.Disable()
self.apply_btn.Disable()
def progress_callback(progress):
# Update button text with progress percentage
wx.CallAfter(self.download_btn.SetLabel, f"Downloading... {progress}%")
def download_thread():
try:
# Store original button label
original_label = self.download_btn.GetLabel()
# Call download_updates with progress callback
self.update_manager.download_updates(self.update_info, progress_callback)
# Restore original button label when done
wx.CallAfter(self.download_btn.SetLabel, original_label)
wx.CallAfter(self.download_btn.Enable)
wx.CallAfter(self.apply_btn.Enable)
wx.CallAfter(logging.info, "Update downloaded successfully")
except Exception as e:
# Restore button label on error
wx.CallAfter(self.download_btn.SetLabel, "Download")
wx.CallAfter(self.download_btn.Enable)
wx.CallAfter(self.apply_btn.Enable)
wx.CallAfter(logging.error, f"Failed to download update: {e}")
# Start download in a separate thread
thread = threading.Thread(target=download_thread, daemon=True)
thread.start()
def on_apply(self, event):
if not self.update_info:
logging.warning("No update information available. Please check first.")
return
try:
self.update_manager.apply_updates_and_restart(self.update_info)
logging.info("Update applied successfully")
except Exception as e:
logging.error(f"Failed to apply update: {e}")
def setup_logging():
"""Setup logging to use buffered handler"""
logger = logging.getLogger()
# Remove existing handlers
for handler in logger.handlers[:]:
logger.removeHandler(handler)
# Setup our buffered handler
formatter = logging.Formatter('- %(levelname)s - %(message)s')
log_handler.setFormatter(formatter)
log_handler.setLevel(logging.INFO)
logger.addHandler(log_handler)
logger.setLevel(logging.INFO)
if __name__ == "__main__":
# Setup logging before velopack runs
setup_logging()
# Run velopack early
velopack.App().run()
# Create and run the wx app
app = wx.App(False)
frame = MainFrame()
frame.Show()
app.MainLoop()

View File

@@ -0,0 +1,18 @@
[project]
name = "pythonwxpython"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.9"
dependencies = [
"velopack",
"wxpython>=4.2.3",
]
[tool.uv.sources]
velopack = { path = "../../src/lib-python" }
[dependency-groups]
dev = [
"pyinstaller>=6.14.1",
]

390
samples/PythonWXPython/uv.lock generated Normal file
View File

@@ -0,0 +1,390 @@
version = 1
revision = 2
requires-python = ">=3.9"
resolution-markers = [
"python_full_version >= '3.11'",
"python_full_version == '3.10.*'",
"python_full_version < '3.10'",
]
[[package]]
name = "altgraph"
version = "0.17.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/de/a8/7145824cf0b9e3c28046520480f207df47e927df83aa9555fb47f8505922/altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406", size = 48418, upload-time = "2023-09-25T09:04:52.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/4d/3f/3bc3f1d83f6e4a7fcb834d3720544ca597590425be5ba9db032b2bf322a2/altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff", size = 21212, upload-time = "2023-09-25T09:04:50.691Z" },
]
[[package]]
name = "importlib-metadata"
version = "8.7.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "zipp" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" },
]
[[package]]
name = "macholib"
version = "1.16.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "altgraph" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/ee/af1a3842bdd5902ce133bd246eb7ffd4375c38642aeb5dc0ae3a0329dfa2/macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30", size = 59309, upload-time = "2023-09-25T09:10:16.155Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/5d/c059c180c84f7962db0aeae7c3b9303ed1d73d76f2bfbc32bc231c8be314/macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c", size = 38094, upload-time = "2023-09-25T09:10:14.188Z" },
]
[[package]]
name = "numpy"
version = "2.0.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.10'",
]
sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" },
{ url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" },
{ url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" },
{ url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" },
{ url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" },
{ url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" },
{ url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" },
{ url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" },
{ url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" },
{ url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" },
{ url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" },
{ url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" },
{ url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" },
{ url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" },
{ url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" },
{ url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" },
{ url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" },
{ url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" },
{ url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" },
{ url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" },
{ url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" },
{ url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" },
{ url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" },
{ url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" },
{ url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" },
{ url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" },
{ url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" },
{ url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" },
{ url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" },
{ url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" },
{ url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" },
{ url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" },
{ url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" },
{ url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" },
{ url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" },
{ url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" },
{ url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" },
{ url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" },
{ url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" },
{ url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" },
{ url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" },
{ url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" },
{ url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" },
{ url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" },
]
[[package]]
name = "numpy"
version = "2.2.6"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version == '3.10.*'",
]
sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" },
{ url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" },
{ url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" },
{ url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" },
{ url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" },
{ url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" },
{ url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" },
{ url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" },
{ url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" },
{ url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" },
{ url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" },
{ url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" },
{ url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" },
{ url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" },
{ url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" },
{ url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" },
{ url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" },
{ url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" },
{ url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" },
{ url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" },
{ url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" },
{ url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" },
{ url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" },
{ url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" },
{ url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" },
{ url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
{ url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" },
{ url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
{ url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" },
{ url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" },
{ url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" },
{ url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" },
{ url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" },
{ url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" },
{ url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" },
{ url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" },
{ url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" },
{ url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" },
{ url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" },
{ url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" },
{ url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" },
{ url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" },
{ url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" },
{ url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" },
{ url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" },
{ url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" },
{ url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" },
{ url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" },
{ url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" },
{ url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" },
{ url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" },
{ url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" },
{ url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" },
{ url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" },
]
[[package]]
name = "numpy"
version = "2.3.1"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.11'",
]
sdist = { url = "https://files.pythonhosted.org/packages/2e/19/d7c972dfe90a353dbd3efbbe1d14a5951de80c99c9dc1b93cd998d51dc0f/numpy-2.3.1.tar.gz", hash = "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", size = 20390372, upload-time = "2025-06-21T12:28:33.469Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b0/c7/87c64d7ab426156530676000c94784ef55676df2f13b2796f97722464124/numpy-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070", size = 21199346, upload-time = "2025-06-21T11:47:47.57Z" },
{ url = "https://files.pythonhosted.org/packages/58/0e/0966c2f44beeac12af8d836e5b5f826a407cf34c45cb73ddcdfce9f5960b/numpy-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae", size = 14361143, upload-time = "2025-06-21T11:48:10.766Z" },
{ url = "https://files.pythonhosted.org/packages/7d/31/6e35a247acb1bfc19226791dfc7d4c30002cd4e620e11e58b0ddf836fe52/numpy-2.3.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a", size = 5378989, upload-time = "2025-06-21T11:48:19.998Z" },
{ url = "https://files.pythonhosted.org/packages/b0/25/93b621219bb6f5a2d4e713a824522c69ab1f06a57cd571cda70e2e31af44/numpy-2.3.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e", size = 6912890, upload-time = "2025-06-21T11:48:31.376Z" },
{ url = "https://files.pythonhosted.org/packages/ef/60/6b06ed98d11fb32e27fb59468b42383f3877146d3ee639f733776b6ac596/numpy-2.3.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db", size = 14569032, upload-time = "2025-06-21T11:48:52.563Z" },
{ url = "https://files.pythonhosted.org/packages/75/c9/9bec03675192077467a9c7c2bdd1f2e922bd01d3a69b15c3a0fdcd8548f6/numpy-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb", size = 16930354, upload-time = "2025-06-21T11:49:17.473Z" },
{ url = "https://files.pythonhosted.org/packages/6a/e2/5756a00cabcf50a3f527a0c968b2b4881c62b1379223931853114fa04cda/numpy-2.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93", size = 15879605, upload-time = "2025-06-21T11:49:41.161Z" },
{ url = "https://files.pythonhosted.org/packages/ff/86/a471f65f0a86f1ca62dcc90b9fa46174dd48f50214e5446bc16a775646c5/numpy-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115", size = 18666994, upload-time = "2025-06-21T11:50:08.516Z" },
{ url = "https://files.pythonhosted.org/packages/43/a6/482a53e469b32be6500aaf61cfafd1de7a0b0d484babf679209c3298852e/numpy-2.3.1-cp311-cp311-win32.whl", hash = "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369", size = 6603672, upload-time = "2025-06-21T11:50:19.584Z" },
{ url = "https://files.pythonhosted.org/packages/6b/fb/bb613f4122c310a13ec67585c70e14b03bfc7ebabd24f4d5138b97371d7c/numpy-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff", size = 13024015, upload-time = "2025-06-21T11:50:39.139Z" },
{ url = "https://files.pythonhosted.org/packages/51/58/2d842825af9a0c041aca246dc92eb725e1bc5e1c9ac89712625db0c4e11c/numpy-2.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a", size = 10456989, upload-time = "2025-06-21T11:50:55.616Z" },
{ url = "https://files.pythonhosted.org/packages/c6/56/71ad5022e2f63cfe0ca93559403d0edef14aea70a841d640bd13cdba578e/numpy-2.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", size = 20896664, upload-time = "2025-06-21T12:15:30.845Z" },
{ url = "https://files.pythonhosted.org/packages/25/65/2db52ba049813670f7f987cc5db6dac9be7cd95e923cc6832b3d32d87cef/numpy-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", size = 14131078, upload-time = "2025-06-21T12:15:52.23Z" },
{ url = "https://files.pythonhosted.org/packages/57/dd/28fa3c17b0e751047ac928c1e1b6990238faad76e9b147e585b573d9d1bd/numpy-2.3.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", size = 5112554, upload-time = "2025-06-21T12:16:01.434Z" },
{ url = "https://files.pythonhosted.org/packages/c9/fc/84ea0cba8e760c4644b708b6819d91784c290288c27aca916115e3311d17/numpy-2.3.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", size = 6646560, upload-time = "2025-06-21T12:16:11.895Z" },
{ url = "https://files.pythonhosted.org/packages/61/b2/512b0c2ddec985ad1e496b0bd853eeb572315c0f07cd6997473ced8f15e2/numpy-2.3.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", size = 14260638, upload-time = "2025-06-21T12:16:32.611Z" },
{ url = "https://files.pythonhosted.org/packages/6e/45/c51cb248e679a6c6ab14b7a8e3ead3f4a3fe7425fc7a6f98b3f147bec532/numpy-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", size = 16632729, upload-time = "2025-06-21T12:16:57.439Z" },
{ url = "https://files.pythonhosted.org/packages/e4/ff/feb4be2e5c09a3da161b412019caf47183099cbea1132fd98061808c2df2/numpy-2.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", size = 15565330, upload-time = "2025-06-21T12:17:20.638Z" },
{ url = "https://files.pythonhosted.org/packages/bc/6d/ceafe87587101e9ab0d370e4f6e5f3f3a85b9a697f2318738e5e7e176ce3/numpy-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77", size = 18361734, upload-time = "2025-06-21T12:17:47.938Z" },
{ url = "https://files.pythonhosted.org/packages/2b/19/0fb49a3ea088be691f040c9bf1817e4669a339d6e98579f91859b902c636/numpy-2.3.1-cp312-cp312-win32.whl", hash = "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", size = 6320411, upload-time = "2025-06-21T12:17:58.475Z" },
{ url = "https://files.pythonhosted.org/packages/b1/3e/e28f4c1dd9e042eb57a3eb652f200225e311b608632bc727ae378623d4f8/numpy-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", size = 12734973, upload-time = "2025-06-21T12:18:17.601Z" },
{ url = "https://files.pythonhosted.org/packages/04/a8/8a5e9079dc722acf53522b8f8842e79541ea81835e9b5483388701421073/numpy-2.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", size = 10191491, upload-time = "2025-06-21T12:18:33.585Z" },
{ url = "https://files.pythonhosted.org/packages/d4/bd/35ad97006d8abff8631293f8ea6adf07b0108ce6fec68da3c3fcca1197f2/numpy-2.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", size = 20889381, upload-time = "2025-06-21T12:19:04.103Z" },
{ url = "https://files.pythonhosted.org/packages/f1/4f/df5923874d8095b6062495b39729178eef4a922119cee32a12ee1bd4664c/numpy-2.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", size = 14152726, upload-time = "2025-06-21T12:19:25.599Z" },
{ url = "https://files.pythonhosted.org/packages/8c/0f/a1f269b125806212a876f7efb049b06c6f8772cf0121139f97774cd95626/numpy-2.3.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", size = 5105145, upload-time = "2025-06-21T12:19:34.782Z" },
{ url = "https://files.pythonhosted.org/packages/6d/63/a7f7fd5f375b0361682f6ffbf686787e82b7bbd561268e4f30afad2bb3c0/numpy-2.3.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", size = 6639409, upload-time = "2025-06-21T12:19:45.228Z" },
{ url = "https://files.pythonhosted.org/packages/bf/0d/1854a4121af895aab383f4aa233748f1df4671ef331d898e32426756a8a6/numpy-2.3.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", size = 14257630, upload-time = "2025-06-21T12:20:06.544Z" },
{ url = "https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", size = 16627546, upload-time = "2025-06-21T12:20:31.002Z" },
{ url = "https://files.pythonhosted.org/packages/6e/ec/3b68220c277e463095342d254c61be8144c31208db18d3fd8ef02712bcd6/numpy-2.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", size = 15562538, upload-time = "2025-06-21T12:20:54.322Z" },
{ url = "https://files.pythonhosted.org/packages/77/2b/4014f2bcc4404484021c74d4c5ee8eb3de7e3f7ac75f06672f8dcf85140a/numpy-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", size = 18360327, upload-time = "2025-06-21T12:21:21.053Z" },
{ url = "https://files.pythonhosted.org/packages/40/8d/2ddd6c9b30fcf920837b8672f6c65590c7d92e43084c25fc65edc22e93ca/numpy-2.3.1-cp313-cp313-win32.whl", hash = "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", size = 6312330, upload-time = "2025-06-21T12:25:07.447Z" },
{ url = "https://files.pythonhosted.org/packages/dd/c8/beaba449925988d415efccb45bf977ff8327a02f655090627318f6398c7b/numpy-2.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", size = 12731565, upload-time = "2025-06-21T12:25:26.444Z" },
{ url = "https://files.pythonhosted.org/packages/0b/c3/5c0c575d7ec78c1126998071f58facfc124006635da75b090805e642c62e/numpy-2.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", size = 10190262, upload-time = "2025-06-21T12:25:42.196Z" },
{ url = "https://files.pythonhosted.org/packages/ea/19/a029cd335cf72f79d2644dcfc22d90f09caa86265cbbde3b5702ccef6890/numpy-2.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", size = 20987593, upload-time = "2025-06-21T12:21:51.664Z" },
{ url = "https://files.pythonhosted.org/packages/25/91/8ea8894406209107d9ce19b66314194675d31761fe2cb3c84fe2eeae2f37/numpy-2.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", size = 14300523, upload-time = "2025-06-21T12:22:13.583Z" },
{ url = "https://files.pythonhosted.org/packages/a6/7f/06187b0066eefc9e7ce77d5f2ddb4e314a55220ad62dd0bfc9f2c44bac14/numpy-2.3.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", size = 5227993, upload-time = "2025-06-21T12:22:22.53Z" },
{ url = "https://files.pythonhosted.org/packages/e8/ec/a926c293c605fa75e9cfb09f1e4840098ed46d2edaa6e2152ee35dc01ed3/numpy-2.3.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", size = 6736652, upload-time = "2025-06-21T12:22:33.629Z" },
{ url = "https://files.pythonhosted.org/packages/e3/62/d68e52fb6fde5586650d4c0ce0b05ff3a48ad4df4ffd1b8866479d1d671d/numpy-2.3.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", size = 14331561, upload-time = "2025-06-21T12:22:55.056Z" },
{ url = "https://files.pythonhosted.org/packages/fc/ec/b74d3f2430960044bdad6900d9f5edc2dc0fb8bf5a0be0f65287bf2cbe27/numpy-2.3.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", size = 16693349, upload-time = "2025-06-21T12:23:20.53Z" },
{ url = "https://files.pythonhosted.org/packages/0d/15/def96774b9d7eb198ddadfcbd20281b20ebb510580419197e225f5c55c3e/numpy-2.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", size = 15642053, upload-time = "2025-06-21T12:23:43.697Z" },
{ url = "https://files.pythonhosted.org/packages/2b/57/c3203974762a759540c6ae71d0ea2341c1fa41d84e4971a8e76d7141678a/numpy-2.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", size = 18434184, upload-time = "2025-06-21T12:24:10.708Z" },
{ url = "https://files.pythonhosted.org/packages/22/8a/ccdf201457ed8ac6245187850aff4ca56a79edbea4829f4e9f14d46fa9a5/numpy-2.3.1-cp313-cp313t-win32.whl", hash = "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", size = 6440678, upload-time = "2025-06-21T12:24:21.596Z" },
{ url = "https://files.pythonhosted.org/packages/f1/7e/7f431d8bd8eb7e03d79294aed238b1b0b174b3148570d03a8a8a8f6a0da9/numpy-2.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", size = 12870697, upload-time = "2025-06-21T12:24:40.644Z" },
{ url = "https://files.pythonhosted.org/packages/d4/ca/af82bf0fad4c3e573c6930ed743b5308492ff19917c7caaf2f9b6f9e2e98/numpy-2.3.1-cp313-cp313t-win_arm64.whl", hash = "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", size = 10260376, upload-time = "2025-06-21T12:24:56.884Z" },
{ url = "https://files.pythonhosted.org/packages/e8/34/facc13b9b42ddca30498fc51f7f73c3d0f2be179943a4b4da8686e259740/numpy-2.3.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3", size = 21070637, upload-time = "2025-06-21T12:26:12.518Z" },
{ url = "https://files.pythonhosted.org/packages/65/b6/41b705d9dbae04649b529fc9bd3387664c3281c7cd78b404a4efe73dcc45/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b", size = 5304087, upload-time = "2025-06-21T12:26:22.294Z" },
{ url = "https://files.pythonhosted.org/packages/7a/b4/fe3ac1902bff7a4934a22d49e1c9d71a623204d654d4cc43c6e8fe337fcb/numpy-2.3.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7", size = 6817588, upload-time = "2025-06-21T12:26:32.939Z" },
{ url = "https://files.pythonhosted.org/packages/ae/ee/89bedf69c36ace1ac8f59e97811c1f5031e179a37e4821c3a230bf750142/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df", size = 14399010, upload-time = "2025-06-21T12:26:54.086Z" },
{ url = "https://files.pythonhosted.org/packages/15/08/e00e7070ede29b2b176165eba18d6f9784d5349be3c0c1218338e79c27fd/numpy-2.3.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68", size = 16752042, upload-time = "2025-06-21T12:27:19.018Z" },
{ url = "https://files.pythonhosted.org/packages/48/6b/1c6b515a83d5564b1698a61efa245727c8feecf308f4091f565988519d20/numpy-2.3.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb", size = 12927246, upload-time = "2025-06-21T12:27:38.618Z" },
]
[[package]]
name = "packaging"
version = "25.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
]
[[package]]
name = "pefile"
version = "2023.2.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/78/c5/3b3c62223f72e2360737fd2a57c30e5b2adecd85e70276879609a7403334/pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc", size = 74854, upload-time = "2023-02-07T12:23:55.958Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/55/26/d0ad8b448476d0a1e8d3ea5622dc77b916db84c6aa3cb1e1c0965af948fc/pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6", size = 71791, upload-time = "2023-02-07T12:28:36.678Z" },
]
[[package]]
name = "pyinstaller"
version = "6.14.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "altgraph" },
{ name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "macholib", marker = "sys_platform == 'darwin'" },
{ name = "packaging" },
{ name = "pefile", marker = "sys_platform == 'win32'" },
{ name = "pyinstaller-hooks-contrib" },
{ name = "pywin32-ctypes", marker = "sys_platform == 'win32'" },
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/d66d3a9c34349d73eb099401060e2591da8ccc5ed427e54fff3961302513/pyinstaller-6.14.1.tar.gz", hash = "sha256:35d5c06a668e21f0122178dbf20e40fd21012dc8f6170042af6050c4e7b3edca", size = 4284317, upload-time = "2025-06-08T18:45:46.367Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/f6/fa56e547fe849db4b8da0acaad6101a6382c18370c7e0f378a1cf0ea89f0/pyinstaller-6.14.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:da559cfe4f7a20a7ebdafdf12ea2a03ea94d3caa49736ef53ee2c155d78422c9", size = 999937, upload-time = "2025-06-08T18:44:26.429Z" },
{ url = "https://files.pythonhosted.org/packages/af/a6/a2814978f47ae038b1ce112717adbdcfd8dfb9504e5c52437902331cde1a/pyinstaller-6.14.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:f040d1e3d42af3730104078d10d4a8ca3350bd1c78de48f12e1b26f761e0cbc3", size = 719569, upload-time = "2025-06-08T18:44:30.948Z" },
{ url = "https://files.pythonhosted.org/packages/35/f0/86391a4c0f558aef43a7dac8f678d46f4e5b84bd133308e3ea81f7384ab9/pyinstaller-6.14.1-py3-none-manylinux2014_i686.whl", hash = "sha256:7b8813fb2d5a82ef4ceffc342ed9a11a6fc1ef21e68e833dbd8fedb8a188d3f5", size = 729824, upload-time = "2025-06-08T18:44:34.983Z" },
{ url = "https://files.pythonhosted.org/packages/e5/88/446814e335d937406e6e1ae4a77ed922b8eea8b90f3aaf69427a16b58ed2/pyinstaller-6.14.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e2cfdbc6dd41d19872054fc233da18856ec422a7fdea899b6985ae04f980376a", size = 727937, upload-time = "2025-06-08T18:44:38.954Z" },
{ url = "https://files.pythonhosted.org/packages/c6/0f/5aa891c61d303ad4a794b7e2f864aacf64fe0f6f5559e2aec0f742595fad/pyinstaller-6.14.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:a4d53b3ecb5786b097b79bda88c4089186fc1498ef7eaa6cee57599ae459241e", size = 724762, upload-time = "2025-06-08T18:44:42.768Z" },
{ url = "https://files.pythonhosted.org/packages/c5/92/e32ec0a1754852a8ed5a60f6746c6483e3da68aee97d314f3a3a99e0ed9e/pyinstaller-6.14.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c48dd257f77f61ebea2d1fdbaf11243730f2271873c88d3b5ecb7869525d3bcb", size = 724957, upload-time = "2025-06-08T18:44:46.829Z" },
{ url = "https://files.pythonhosted.org/packages/c3/66/1260f384e47bf939f6238f791d4cda7edb94771d2fa0a451e0edb21ac9c7/pyinstaller-6.14.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5b05cbb2ffc033b4681268159b82bac94b875475c339603c7e605f00a73c8746", size = 724132, upload-time = "2025-06-08T18:44:51.081Z" },
{ url = "https://files.pythonhosted.org/packages/d2/8b/8570ab94ec07e0b2b1203f45840353ee76aa067a2540c97da43d43477b26/pyinstaller-6.14.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d5fd73757c8ea9adb2f9c1f81656335ba9890029ede3031835d768fde36e89f0", size = 723847, upload-time = "2025-06-08T18:44:54.896Z" },
{ url = "https://files.pythonhosted.org/packages/d5/43/6c68dc9e53b09ff948d6e46477932b387832bbb920c48061d734ef089368/pyinstaller-6.14.1-py3-none-win32.whl", hash = "sha256:547f7a93592e408cbfd093ce9fd9631215387dab0dbf3130351d3b0b1186a534", size = 1299744, upload-time = "2025-06-08T18:45:00.781Z" },
{ url = "https://files.pythonhosted.org/packages/7c/dd/bb8d5bcb0592f7f5d454ad308051d00ed34f8b08d5003400b825cfe35513/pyinstaller-6.14.1-py3-none-win_amd64.whl", hash = "sha256:0794290b4b56ef9d35858334deb29f36ec1e1f193b0f825212a0aa5a1bec5a2f", size = 1357625, upload-time = "2025-06-08T18:45:06.826Z" },
{ url = "https://files.pythonhosted.org/packages/89/57/8a8979737980e50aa5031b77318ce783759bf25be2956317f2e1d7a65a09/pyinstaller-6.14.1-py3-none-win_arm64.whl", hash = "sha256:d9d99695827f892cb19644106da30681363e8ff27b8326ac8416d62890ab9c74", size = 1298607, upload-time = "2025-06-08T18:45:12.766Z" },
]
[[package]]
name = "pyinstaller-hooks-contrib"
version = "2025.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", marker = "python_full_version < '3.10'" },
{ name = "packaging" },
{ name = "setuptools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5f/ff/e3376595935d5f8135964d2177cd3e3e0c1b5a6237497d9775237c247a5d/pyinstaller_hooks_contrib-2025.5.tar.gz", hash = "sha256:707386770b8fe066c04aad18a71bc483c7b25e18b4750a756999f7da2ab31982", size = 163124, upload-time = "2025-06-08T18:47:53.26Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/2c/b4d317534e17dd1df95c394d4b37febb15ead006a1c07c2bb006481fb5e7/pyinstaller_hooks_contrib-2025.5-py3-none-any.whl", hash = "sha256:ebfae1ba341cb0002fb2770fad0edf2b3e913c2728d92df7ad562260988ca373", size = 437246, upload-time = "2025-06-08T18:47:51.516Z" },
]
[[package]]
name = "pythonwxpython"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "velopack" },
{ name = "wxpython" },
]
[package.dev-dependencies]
dev = [
{ name = "pyinstaller" },
]
[package.metadata]
requires-dist = [
{ name = "velopack", directory = "../../src/lib-python" },
{ name = "wxpython", specifier = ">=4.2.3" },
]
[package.metadata.requires-dev]
dev = [{ name = "pyinstaller", specifier = ">=6.14.1" }]
[[package]]
name = "pywin32-ctypes"
version = "0.2.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" },
]
[[package]]
name = "setuptools"
version = "80.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
]
[[package]]
name = "typing-extensions"
version = "4.14.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423, upload-time = "2025-06-02T14:52:11.399Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839, upload-time = "2025-06-02T14:52:10.026Z" },
]
[[package]]
name = "velopack"
version = "0.0.1"
source = { directory = "../../src/lib-python" }
[package.metadata]
[package.metadata.requires-dev]
dev = [
{ name = "maturin", specifier = ">=1.8.6" },
{ name = "pyinstaller", specifier = ">=6.13.0" },
]
test = [{ name = "pyinstaller", specifier = ">=6.13.0" }]
[[package]]
name = "wxpython"
version = "4.2.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
{ name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" },
{ name = "numpy", version = "2.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/4c/d9/4451392d3d6ba45aa23aa77a6f1a9970b43351b956bf61e10fd513a1dc38/wxPython-4.2.3.tar.gz", hash = "sha256:20d6e0c927e27ced85643719bd63e9f7fd501df6e9a8aab1489b039897fd7c01", size = 58861286, upload-time = "2025-04-10T02:49:43.557Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/ee/883048ff3c8e83562a160c43a976034efaa7c502138774db9767679d648d/wxpython-4.2.3-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:80df5158129577b8b1153b9af28ef18cf738a8f200e87c836a1dfc9ca61f86b7", size = 18751324, upload-time = "2025-04-10T02:48:38.712Z" },
{ url = "https://files.pythonhosted.org/packages/a4/1d/2d26018397150818879a00be5aa66371877c39080d65edcf95674e9833ea/wxpython-4.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c8ce9223d256b69815ef2c64842fb6a70303ab46d262b601382adff053779ab3", size = 17844363, upload-time = "2025-04-10T02:48:42.069Z" },
{ url = "https://files.pythonhosted.org/packages/d8/a7/30946564eb241c02bf30c262114f1bf5540fdcc376f46245d2a5703ae9de/wxpython-4.2.3-cp310-cp310-win32.whl", hash = "sha256:d2cf6a99ec39440298a2c6fbbfd95e386f0ad7d56ae7880553c1ae45709c091a", size = 14791534, upload-time = "2025-04-10T02:48:45.175Z" },
{ url = "https://files.pythonhosted.org/packages/c0/48/b7f73096a5912033020d8c9ac1826c4f840724216253e2f95d575b50b62c/wxpython-4.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:8e729d3110a6ed5f6520371bb3cd5f05f993f6b40d5f80ec6446b8678f2fc2ac", size = 16819235, upload-time = "2025-04-10T02:48:48.055Z" },
{ url = "https://files.pythonhosted.org/packages/d0/f9/e9c38a0231d993810b8b4bc2f4e12a3162a3eb2d4d51788022f054f60a75/wxpython-4.2.3-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a66b0d6fe1d7e8e133fdfdee3cae95f70cc433929f36af08f377e103584f49c", size = 18746309, upload-time = "2025-04-10T02:48:51.12Z" },
{ url = "https://files.pythonhosted.org/packages/c3/43/e6dd8277465e03a372127d610dd7223de9126eb42fea381747ea67a91ebd/wxpython-4.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84a01d90c4514d738a05d9045f62b1f1850fbe8d967b4ae545ba090e74cb1216", size = 17841378, upload-time = "2025-04-10T02:48:54.34Z" },
{ url = "https://files.pythonhosted.org/packages/9a/28/18fc80c1ae7bdbbb2c03f6bc995b328b3518e5b3cd69a01fa5354b9acfa4/wxpython-4.2.3-cp311-cp311-win32.whl", hash = "sha256:4e5028d7092701ab20b3bef4547d6a7d528d82f12192d72595eeb98b2ebe6364", size = 14496540, upload-time = "2025-04-10T02:48:57.311Z" },
{ url = "https://files.pythonhosted.org/packages/da/8a/beb38cf4c74a6d8587372aef86c00dde6fec2c8d88292b8e36389aa9dc73/wxpython-4.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:9040fe6ae7e0fd1b33c7020c392a08f94d205614a24e23b9c7a3a74a9836140d", size = 16562662, upload-time = "2025-04-10T02:48:59.804Z" },
{ url = "https://files.pythonhosted.org/packages/18/2f/550022808bcf4cfafc94021d3f77fec2ceccfc21d37cc2ce465027802f87/wxpython-4.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c007b153ebf74a8429789a5dcb93c76b3901c531b1413695fdb96f222ec207fc", size = 18792609, upload-time = "2025-04-10T02:49:02.473Z" },
{ url = "https://files.pythonhosted.org/packages/da/aa/86bf3300c921d81f59ccebcb919ca7516ca7ea2b560eab36ac45e1b9931d/wxpython-4.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a7c95c385895b94b4784a78c06a1626329c3552fcc47a78ab88f0a02ffe1e2db", size = 17851857, upload-time = "2025-04-10T02:49:05.941Z" },
{ url = "https://files.pythonhosted.org/packages/e7/63/40700ad4a43962e31adf754d420d6b2a5e2d50fa5e1041a01ed73b2b0e90/wxpython-4.2.3-cp312-cp312-win32.whl", hash = "sha256:361db46442376ba1cae4ce9f8fab4081b4d085458f973b4c10b68e04492067ad", size = 14505417, upload-time = "2025-04-10T02:49:08.993Z" },
{ url = "https://files.pythonhosted.org/packages/1a/25/ca90e9dc793c328fb97cc011ae92d48337c1a8ef8c54576979840d74226f/wxpython-4.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:9231853d8a15b0610a6153cbef65f392164c17f234c65916df499619f7e75bd9", size = 16567404, upload-time = "2025-04-10T02:49:12.146Z" },
{ url = "https://files.pythonhosted.org/packages/af/d9/da813737843fe4290d63061f8c77d172a3c3f9242a2d3590ecec4ff91d6d/wxpython-4.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:718a1560e341b5c4755fe9e8a431a1c6796a9d742a4bfce7ebd90d8a71ac0dd4", size = 18796223, upload-time = "2025-04-10T02:49:15.582Z" },
{ url = "https://files.pythonhosted.org/packages/7d/b5/f65ba11cd6b1de8279085fba287ec869a35eb122743d620a58d65d54a177/wxpython-4.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a367e4242ae83aedbaf2728f622be8ee7aec4721eb4254fe8cf2d86f893bc5", size = 17853488, upload-time = "2025-04-10T02:49:18.412Z" },
{ url = "https://files.pythonhosted.org/packages/9e/42/fe653ffb7817d09cbc146bb67ace7fc690bdbe2739bbe472f99d8c51b01c/wxpython-4.2.3-cp313-cp313-win32.whl", hash = "sha256:676aeb82d64d3d3cb94210e882a508bb1013de5fb55917f54f8dd2c1483f9110", size = 14507572, upload-time = "2025-04-10T02:49:20.927Z" },
{ url = "https://files.pythonhosted.org/packages/83/5c/1692523ab503b34065add84f184e1b0e4b6af6b5bde6447666a0d192b29d/wxpython-4.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:dac14ea1b04d90b403414f0401704beecae0d3e7143755a20da0ac2cfec02bf9", size = 16566260, upload-time = "2025-04-10T02:49:24.378Z" },
{ url = "https://files.pythonhosted.org/packages/ca/77/0baf8a17812185c15b47fad9acf19cdb609aacdccc7b7ba744de11797323/wxpython-4.2.3-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0b1b5ac5ced6bc46a4bf9041d16b3e35373a5a0c1dddac9384b59c43d06cc294", size = 18751292, upload-time = "2025-04-10T02:49:27.46Z" },
{ url = "https://files.pythonhosted.org/packages/11/73/8e041128db1765aa456a9ff1360dd9cf67f7255156c5cedc11f2bc9e6023/wxpython-4.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f73aebf960f0ec6961454841deed242c2d852066ab2c538e617c5eabd95bd7b", size = 17844562, upload-time = "2025-04-10T02:49:30.937Z" },
{ url = "https://files.pythonhosted.org/packages/3b/b3/044da69a38ebb3637ed15bb52466f0c601f892822e5fdd5a17c8835ed323/wxpython-4.2.3-cp39-cp39-win32.whl", hash = "sha256:23afd08aa530a5ede0bcee0526ce185e38e28374ef5c3d2f65f058c1d7204f67", size = 14795524, upload-time = "2025-04-10T02:49:34.12Z" },
{ url = "https://files.pythonhosted.org/packages/3f/1a/0940baecfb919f31fab688ebc33efe9dbc43c69f5237921e8e1132967f34/wxpython-4.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:f9d5a08199adb8e66ac15bdd494b398f5fa53e7a31ecd9dc07f522265d61aa47", size = 16820318, upload-time = "2025-04-10T02:49:37.535Z" },
]
[[package]]
name = "zipp"
version = "3.23.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
]

View File

@@ -7,4 +7,5 @@
- [**C# / Uno**](CSharpUno) - Velopack in a cross-platform app with [Uno Platform](https://github.com/unoplatform/uno).
- [**C# / Wpf**](CSharpWpf) - Velopack with WPF on Windows.
- [**NodeJS / Electron**](NodeJSElectron) - Velopack in a cross-platform JavaScript app with [Electron](https://www.electronjs.org/).
- [**Python / WXPython**](PythonWXPython) - Velopack in a cross-platform python app with the [WXPython](https://wxpython.org/) ui framework.
- [**Rust / Iced**](RustIced) - Velopack in a cross-platform app with [Iced](https://github.com/iced-rs/iced).

View File

@@ -20,9 +20,9 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
[[package]]
name = "adler2"
version = "2.0.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "ahash"
@@ -282,7 +282,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -344,7 +344,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -458,7 +458,7 @@ checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -495,9 +495,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.2.26"
version = "1.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac"
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
dependencies = [
"jobserver",
"libc",
@@ -512,9 +512,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cfg-if"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "cfg_aliases"
@@ -847,7 +847,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -921,7 +921,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -1004,9 +1004,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
[[package]]
name = "enumflags2"
version = "0.7.11"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147"
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
dependencies = [
"enumflags2_derive",
"serde",
@@ -1014,13 +1014,13 @@ dependencies = [
[[package]]
name = "enumflags2_derive"
version = "0.7.11"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79"
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -1179,7 +1179,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -1267,7 +1267,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -1328,7 +1328,7 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi 0.11.1+wasi-snapshot-preview1",
]
[[package]]
@@ -1513,9 +1513,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "hex"
@@ -1943,9 +1943,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.172"
version = "0.2.173"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
[[package]]
name = "libloading"
@@ -1964,7 +1964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
dependencies = [
"cfg-if",
"windows-targets 0.53.0",
"windows-targets 0.53.2",
]
[[package]]
@@ -1981,7 +1981,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags 2.9.1",
"libc",
"redox_syscall 0.5.12",
"redox_syscall 0.5.13",
]
[[package]]
@@ -2053,9 +2053,9 @@ dependencies = [
[[package]]
name = "memchr"
version = "2.7.4"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "memmap2"
@@ -2092,9 +2092,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.8.8"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
"simd-adler32",
@@ -2228,7 +2228,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -2524,7 +2524,7 @@ dependencies = [
"by_address",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -2576,7 +2576,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.12",
"redox_syscall 0.5.13",
"smallvec",
"windows-targets 0.52.6",
]
@@ -2623,7 +2623,7 @@ dependencies = [
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -2652,7 +2652,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -2911,9 +2911,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.12"
version = "0.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
dependencies = [
"bitflags 2.9.1",
]
@@ -3168,7 +3168,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -3191,7 +3191,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -3264,12 +3264,9 @@ dependencies = [
[[package]]
name = "slab"
version = "0.4.9"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]]
name = "slotmap"
@@ -3351,7 +3348,7 @@ dependencies = [
"objc2-foundation",
"objc2-quartz-core",
"raw-window-handle",
"redox_syscall 0.5.12",
"redox_syscall 0.5.13",
"rustix 0.38.44",
"tiny-xlib",
"wasm-bindgen",
@@ -3426,9 +3423,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.101"
version = "2.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
dependencies = [
"proc-macro2",
"quote",
@@ -3443,7 +3440,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -3503,7 +3500,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -3514,17 +3511,16 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
name = "thread_local"
version = "1.1.8"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
@@ -3627,7 +3623,7 @@ checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -3786,9 +3782,9 @@ dependencies = [
[[package]]
name = "ureq-proto"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36"
checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7"
dependencies = [
"base64",
"http",
@@ -3841,7 +3837,7 @@ checksum = "26b682e8c381995ea03130e381928e0e005b7c9eb483c6c8682f50e07b33c2b7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -3879,7 +3875,7 @@ dependencies = [
"ureq",
"url",
"uuid",
"windows 0.61.1",
"windows 0.61.3",
"xml",
"zip",
]
@@ -3919,9 +3915,9 @@ dependencies = [
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasi"
@@ -3954,7 +3950,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
"wasm-bindgen-shared",
]
@@ -3989,7 +3985,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -4335,9 +4331,9 @@ dependencies = [
[[package]]
name = "windows"
version = "0.61.1"
version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections",
"windows-core 0.61.2",
@@ -4396,7 +4392,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -4407,14 +4403,14 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
name = "windows-link"
version = "0.1.1"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-numerics"
@@ -4519,9 +4515,9 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.53.0"
version = "0.53.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef"
dependencies = [
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
@@ -4776,9 +4772,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.7.10"
version = "0.7.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
dependencies = [
"memchr",
]
@@ -4915,7 +4911,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
"synstructure",
]
@@ -4966,7 +4962,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
"zvariant_utils",
]
@@ -5004,7 +5000,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -5024,7 +5020,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
"synstructure",
]
@@ -5064,7 +5060,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]
[[package]]
@@ -5121,7 +5117,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
"zvariant_utils",
]
@@ -5133,5 +5129,5 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
"syn 2.0.103",
]

View File

@@ -12,9 +12,16 @@ cd %~dp0
echo.
echo Building Iced/Rust
cargo build
if errorlevel 1 exit /b 1
echo.
echo Building Velopack Release v%~1
mkdir publish
if errorlevel 1 exit /b 1
move target\debug\velorusticedsample.exe publish\velorusticedsample.exe
vpk pack -u VeloRustIcedSample -o releases -p publish -v %*
if errorlevel 1 exit /b 1
vpk pack -u VeloRustIcedSample -o releases -p publish -v %*
if errorlevel 1 exit /b 1

View File

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
# Find the absolute path of the script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

View File

@@ -55,16 +55,16 @@ var types = new List<TypeMap>() {
TypeMap.RustStruct("VelopackLocatorConfig", "vpkc_locator_config_t"),
TypeMap.SystemType("String", "char", "string", "c_char"),
TypeMap.SystemType("PathBuf", "char", "string", "c_char"),
TypeMap.Primitive("bool", "bool"),
TypeMap.Primitive("i32", "int32_t"),
TypeMap.Primitive("i64", "int64_t"),
TypeMap.Primitive("u32", "uint32_t"),
TypeMap.Primitive("u64", "uint64_t"),
TypeMap.Primitive("bool", "bool", "boolean"),
TypeMap.Primitive("i32", "int32_t", "number"),
TypeMap.Primitive("i64", "int64_t", "number"),
TypeMap.Primitive("u32", "uint32_t", "number"),
TypeMap.Primitive("u64", "uint64_t", "number"),
}.ToDictionary(v => v.rustType, v => v);
var handlebarData = availableStructs.Select(s => {
var fields = s.Fields.Select(f => {
var isString = types[f.Type].rustType == "PathBuf" || types[f.Type].rustType == "String";
//var isString = types[f.Type].rustType == "PathBuf" || types[f.Type].rustType == "String";
var field = new RustStruct_Field {
rust_comment = f.DocComment.ToRustComment(),
cpp_comment = f.DocComment.ToCppComment(),
@@ -74,6 +74,7 @@ var handlebarData = availableStructs.Select(s => {
field_rust_type = f.Type,
field_c_type = types[f.Type].interopType,
field_cpp_type = types[f.Type].cppType,
field_node_type = types[f.Type].nodeType,
field_system = types[f.Type].system,
field_primitive = types[f.Type].primitive,
field_normal = !f.Vec && !types[f.Type].primitive,
@@ -97,24 +98,30 @@ var handlebarData = availableStructs.Select(s => {
return stru;
}).ToArray();
// --- rust generation ---
string rustTypes = Path.Combine(libcppDir, "src", "types.rs");
var rustCTypesTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "rust_types.hbs")));
var rustCTypes = rustCTypesTemplate(handlebarData);
Util.ReplaceTextInFile(rustTypes, "RUST_TYPES", rustCTypes.ToString().ReplaceLineEndings("\n"));
// --- C++ generation ---
string rustCppInclude = Path.Combine(libcppDir, "include", "Velopack.hpp");
var cppTypesTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "cpp_mapping.hbs")));
var cppTypes = cppTypesTemplate(handlebarData);
Console.WriteLine("Writing all to file");
Util.ReplaceTextInFile(rustTypes, "RUST_TYPES", rustCTypes.ToString().ReplaceLineEndings("\n"));
Util.ReplaceTextInFile(rustCppInclude, "CPP_TYPES", cppTypes.ToString().ReplaceLineEndings("\n"));
// --- Python asset.rs generation ---
// --- python generation ---
string pythonAssetRs = Path.Combine(scriptsDir, "..", "lib-python", "src", "types.rs");
var pythonAssetTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "python_asset.hbs")));
var pythonAsset = pythonAssetTemplate(handlebarData);
File.WriteAllText(pythonAssetRs, pythonAsset.ToString().ReplaceLineEndings("\n"));
// --- nodejs generation ---
var libnodeTypesFile = Path.Combine(scriptsDir, "..", "lib-nodejs", "src", "types.ts");
var nodeTemplate = Handlebars.Compile(File.ReadAllText(Path.Combine(templatesDir, "node_types.hbs")));
var nodeData = nodeTemplate(handlebarData);
File.WriteAllText(libnodeTypesFile, nodeData.ToString().ReplaceLineEndings("\n"));
return 0;
class TypeMap
@@ -122,6 +129,7 @@ class TypeMap
public string rustType;
public string cType;
public string cppType;
public string nodeType;
public string interopType;
public bool primitive;
public bool system;
@@ -132,18 +140,20 @@ class TypeMap
rustType = rustName,
cType = cType,
cppType = rustName,
nodeType = rustName,
interopType = cType,
primitive = false,
system = false,
};
}
public static TypeMap Primitive(string rustName, string cType)
public static TypeMap Primitive(string rustName, string cType, string nodeType)
{
return new TypeMap() {
rustType = rustName,
cType = cType,
cppType = cType,
nodeType = nodeType,
interopType = rustName,
primitive = true,
system = false,
@@ -156,6 +166,7 @@ class TypeMap
rustType = rustName,
cType = cType,
cppType = cppType,
nodeType = cppType,
interopType = interopType,
primitive = false,
system = true,
@@ -179,6 +190,7 @@ class RustStruct_Field
public string field_c_type;
public string field_cpp_type;
public string field_rust_type;
public string field_node_type;
public bool field_primitive;
public bool field_optional;
public bool field_vector;

View File

@@ -0,0 +1,12 @@
// This file is auto-generated. Do not edit by hand.
{{#each this}}
{{cpp_comment}}
export type {{struct_rust_name}} = {
{{#each fields}}
{{#indent "cpp_comment" " "}}
{{field_name}}{{#if field_optional}}?{{/if}}: {{field_node_type}}{{#if field_vector}}[]{{/if}},
{{/each}}
}
{{/each}}

View File

@@ -107,7 +107,7 @@ typedef struct vpkc_update_options_t {
*/
bool AllowVersionDowngrade;
/**
* **This option should usually be left None**.
* **This option should usually be left None/NULL**.
* Overrides the default channel used to fetch updates.
* The default channel will be whatever channel was specified on the command line when building this release.
* For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
@@ -209,11 +209,15 @@ extern "C" {
/**
* Create a new FileSource update source for a given file path.
* @param psz_file_path The path to a local directory containing updates.
* @returns A new vpkc_update_source_t instance, or null on error.
*/
vpkc_update_source_t *vpkc_new_source_file(const char *psz_file_path);
/**
* Create a new HttpSource update source for a given HTTP URL.
* @param psz_http_url The URL to a remote update server.
* @returns A new vpkc_update_source_t instance, or null on error.
*/
vpkc_update_source_t *vpkc_new_source_http_url(const char *psz_http_url);
@@ -223,6 +227,11 @@ vpkc_update_source_t *vpkc_new_source_http_url(const char *psz_http_url);
* for the lifetime of any UpdateManager's that use this source. You should call `vpkc_free_source` to free the source,
* but note that if the source is still in use by an UpdateManager, it will not be freed until the UpdateManager is freed.
* Therefore to avoid possible issues, it is recommended to create this type of source once for the lifetime of your application.
* @param cb_release_feed A callback to fetch the release feed.
* @param cb_free_release_feed A callback to free the memory allocated by `cb_release_feed`.
* @param cb_download_entry A callback to download an asset.
* @param p_user_data Optional user data to be passed to the callbacks.
* @returns A new vpkc_update_source_t instance, or null on error. If null, the error will be available via `vpkc_get_last_error`.
*/
vpkc_update_source_t *vpkc_new_source_custom_callback(vpkc_release_feed_delegate_t cb_release_feed,
vpkc_free_release_feed_t cb_free_release_feed,
@@ -232,19 +241,24 @@ vpkc_update_source_t *vpkc_new_source_custom_callback(vpkc_release_feed_delegate
/**
* Sends a progress update to the callback with the specified ID. This is used by custom
* update sources created with `vpkc_new_source_custom_callback` to report download progress.
* @param progress_callback_id The ID of the progress callback to send the update to.
* @param progress The progress value to send (0-100).
*/
void vpkc_source_report_progress(size_t progress_callback_id, int16_t progress);
/**
* Frees a vpkc_update_source_t instance.
* @param p_source The source to free.
*/
void vpkc_free_source(vpkc_update_source_t *p_source);
/**
* Create a new UpdateManager instance.
* @param urlOrPath Location of the update server or path to the local update directory.
* @param options Optional extra configuration for update manager.
* @param locator Override the default locator configuration (usually used for testing / mocks).
* @param psz_url_or_path Location of the http update server url or path to the local update directory.
* @param p_options Optional extra configuration for update manager.
* @param p_locator Optional explicit path configuration for Velopack. If null, the default locator will be used.
* @param p_manager A pointer to where the new vpkc_update_manager_t* instance will be stored.
* @returns True if the update manager was created successfully, false otherwise. If false, the error will be available via `vpkc_get_last_error`.
*/
bool vpkc_new_update_manager(const char *psz_url_or_path,
struct vpkc_update_options_t *p_options,
@@ -253,9 +267,11 @@ bool vpkc_new_update_manager(const char *psz_url_or_path,
/**
* Create a new UpdateManager instance with a custom UpdateSource.
* @param urlOrPath Location of the update server or path to the local update directory.
* @param options Optional extra configuration for update manager.
* @param locator Override the default locator configuration (usually used for testing / mocks).
* @param p_source A pointer to a custom UpdateSource.
* @param p_options Optional extra configuration for update manager.
* @param p_locator Optional explicit path configuration for Velopack. If null, the default locator will be used.
* @param p_manager A pointer to where the new vpkc_update_manager_t* instance will be stored.
* @returns True if the update manager was created successfully, false otherwise. If false, the error will be available via `vpkc_get_last_error`.
*/
bool vpkc_new_update_manager_with_source(vpkc_update_source_t *p_source,
struct vpkc_update_options_t *p_options,
@@ -264,6 +280,10 @@ bool vpkc_new_update_manager_with_source(vpkc_update_source_t *p_source,
/**
* Returns the currently installed version of the app.
* @param p_manager The update manager instance.
* @param psz_version A buffer to store the version string.
* @param c_version The size of the `psz_version` buffer.
* @returns The number of characters written to `psz_version` (including null terminator), or the required buffer size if the buffer is too small.
*/
size_t vpkc_get_current_version(vpkc_update_manager_t *p_manager,
char *psz_version,
@@ -271,24 +291,38 @@ size_t vpkc_get_current_version(vpkc_update_manager_t *p_manager,
/**
* Returns the currently installed app id.
* @param p_manager The update manager instance.
* @param psz_id A buffer to store the app id string.
* @param c_id The size of the `psz_id` buffer.
* @returns The number of characters written to `psz_id` (including null terminator), or the required buffer size if the buffer is too small.
*/
size_t vpkc_get_app_id(vpkc_update_manager_t *p_manager, char *psz_id, size_t c_id);
size_t vpkc_get_app_id(vpkc_update_manager_t *p_manager,
char *psz_id,
size_t c_id);
/**
* Returns whether the app is in portable mode. On Windows this can be true or false.
* On MacOS and Linux this will always be true.
* @param p_manager The update manager instance.
* @returns True if the app is in portable mode, false otherwise.
*/
bool vpkc_is_portable(vpkc_update_manager_t *p_manager);
/**
* Returns an UpdateInfo object if there is an update downloaded which still needs to be applied.
* You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update.
* Returns an asset if there is an update downloaded which still needs to be applied.
* You can pass this asset to `vpkc_wait_exit_then_apply_updates` to apply the update.
* @param p_manager The update manager instance.
* @param p_asset A pointer to where the new vpkc_asset_t* instance will be stored.
* @returns True if there is an update pending restart, false otherwise.
*/
bool vpkc_update_pending_restart(vpkc_update_manager_t *p_manager, struct vpkc_asset_t **p_asset);
/**
* Checks for updates, returning None if there are none available. If there are updates available, this method will return an
* Checks for updates. If there are updates available, this method will return an
* UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available.
* @param p_manager The update manager instance.
* @param p_update A pointer to where the new vpkc_update_info_t* instance will be stored if an update is available.
* @returns A `vpkc_update_check_t` value indicating the result of the check. If an update is available, the value will be `HasUpdate` and `p_update` will be populated.
*/
vpkc_update_check_t vpkc_check_for_updates(vpkc_update_manager_t *p_manager,
struct vpkc_update_info_t **p_update);
@@ -300,6 +334,11 @@ vpkc_update_check_t vpkc_check_for_updates(vpkc_update_manager_t *p_manager,
* this method will attempt to unpack and prepare them.
* - If there is no delta update available, or there is an error preparing delta
* packages, this method will fall back to downloading the full version of the update.
* @param p_manager The update manager instance.
* @param p_update The update info object from `vpkc_check_for_updates`.
* @param cb_progress An optional callback to report download progress (0-100).
* @param p_user_data Optional user data to be passed to the progress callback.
* @returns true on success, false on failure. If false, the error will be available via `vpkc_get_last_error`.
*/
bool vpkc_download_updates(vpkc_update_manager_t *p_manager,
struct vpkc_update_info_t *p_update,
@@ -309,7 +348,14 @@ bool vpkc_download_updates(vpkc_update_manager_t *p_manager,
/**
* This will launch the Velopack updater and tell it to wait for this program to exit gracefully.
* You should then clean up any state and exit your app. The updater will apply updates and then
* optionally restart your app. The updater will only wait for 60 seconds before giving up.
* (if specified) restart your app. The updater will only wait for 60 seconds before giving up.
* @param p_manager The update manager instance.
* @param p_asset The asset to apply. This can be from `vpkc_update_pending_restart` or `vpkc_update_info_get_target_asset`.
* @param b_silent True to attempt to apply the update without showing any UI.
* @param b_restart True to restart the app after the update is applied.
* @param p_restart_args An array of command line arguments to pass to the new process when it's restarted.
* @param c_restart_args The number of arguments in `p_restart_args`.
* @returns true on success, false on failure. If false, the error will be available via `vpkc_get_last_error`.
*/
bool vpkc_wait_exit_then_apply_updates(vpkc_update_manager_t *p_manager,
struct vpkc_asset_t *p_asset,
@@ -323,6 +369,14 @@ bool vpkc_wait_exit_then_apply_updates(vpkc_update_manager_t *p_manager,
* This method is unsafe because it does not necessarily wait for any / the correct process to exit
* before applying updates. The `vpkc_wait_exit_then_apply_updates` method is recommended for most use cases.
* If dw_wait_pid is 0, the updater will not wait for any process to exit before applying updates (Not Recommended).
* @param p_manager The update manager instance.
* @param p_asset The asset to apply. This can be from `vpkc_update_pending_restart` or `vpkc_update_info_get_target_asset`.
* @param b_silent True to attempt to apply the update without showing any UI.
* @param dw_wait_pid The process ID to wait for before applying updates. If 0, the updater will not wait.
* @param b_restart True to restart the app after the update is applied.
* @param p_restart_args An array of command line arguments to pass to the new process when it's restarted.
* @param c_restart_args The number of arguments in `p_restart_args`.
* @returns true on success, false on failure. If false, the error will be available via `vpkc_get_last_error`.
*/
bool vpkc_unsafe_apply_updates(vpkc_update_manager_t *p_manager,
struct vpkc_asset_t *p_asset,
@@ -334,90 +388,115 @@ bool vpkc_unsafe_apply_updates(vpkc_update_manager_t *p_manager,
/**
* Frees a vpkc_update_manager_t instance.
* @param p_manager The update manager instance to free.
*/
void vpkc_free_update_manager(vpkc_update_manager_t *p_manager);
/**
* Frees a vpkc_update_info_t instance.
* @param p_update_info The update info instance to free.
*/
void vpkc_free_update_info(struct vpkc_update_info_t *p_update_info);
/**
* Frees a vpkc_asset_t instance.
* @param p_asset The asset instance to free.
*/
void vpkc_free_asset(struct vpkc_asset_t *p_asset);
/**
* VelopackApp helps you to handle app activation events correctly.
* This should be used as early as possible in your application startup code.
* (eg. the beginning of main() or wherever your entry point is)
* (eg. the beginning of main() or wherever your entry point is).
* This function will not return in some cases.
* @param p_user_data Optional user data to be passed to the callbacks.
*/
void vpkc_app_run(void *p_user_data);
/**
* Set whether to automatically apply downloaded updates on startup. This is ON by default.
* @param b_auto_apply True to automatically apply updates, false otherwise.
*/
void vpkc_app_set_auto_apply_on_startup(bool b_auto_apply);
/**
* Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1))
* @param p_args An array of command line arguments.
* @param c_args The number of arguments in `p_args`.
*/
void vpkc_app_set_args(char **p_args, size_t c_args);
/**
* VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
* @param p_locator The locator configuration to use.
*/
void vpkc_app_set_locator(struct vpkc_locator_config_t *p_locator);
/**
* Sets a callback to be run after the app is installed.
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cb_after_install The callback to run after the app is installed. The callback takes a user data pointer and the version of the app as a string.
*/
void vpkc_app_set_hook_after_install(vpkc_hook_callback_t cb_after_install);
/**
* Sets a callback to be run before the app is uninstalled.
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cb_before_uninstall The callback to run before the app is uninstalled. The callback takes a user data pointer and the version of the app as a string.
*/
void vpkc_app_set_hook_before_uninstall(vpkc_hook_callback_t cb_before_uninstall);
/**
* Sets a callback to be run before the app is updated.
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cb_before_update The callback to run before the app is updated. The callback takes a user data pointer and the version of the app as a string.
*/
void vpkc_app_set_hook_before_update(vpkc_hook_callback_t cb_before_update);
/**
* Sets a callback to be run after the app is updated.
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cb_after_update The callback to run after the app is updated. The callback takes a user data pointer and the version of the app as a string.
*/
void vpkc_app_set_hook_after_update(vpkc_hook_callback_t cb_after_update);
/**
* This hook is triggered when the application is started for the first time after installation.
* @param cb_first_run The callback to run on first run. The callback takes a user data pointer and the version of the app as a string.
*/
void vpkc_app_set_hook_first_run(vpkc_hook_callback_t cb_first_run);
/**
* This hook is triggered when the application is restarted by Velopack after installing updates.
* @param cb_restarted The callback to run after the app is restarted. The callback takes a user data pointer and the version of the app as a string.
*/
void vpkc_app_set_hook_restarted(vpkc_hook_callback_t cb_restarted);
/**
* Get the last error message that occurred in the Velopack library.
* @param psz_error A buffer to store the error message.
* @param c_error The size of the `psz_error` buffer.
* @returns The number of characters written to `psz_error` (including null terminator). If the return value is greater than `c_error`, the buffer was too small and the message was truncated.
*/
size_t vpkc_get_last_error(char *psz_error, size_t c_error);
size_t vpkc_get_last_error(char *psz_error,
size_t c_error);
/**
* Set a custom log callback. This will be called for all log messages generated by the Velopack library.
* @param cb_log The callback to call with log messages. The callback takes a user data pointer, a log level, and the log message as a string.
* @param p_user_data Optional user data to be passed to the callback.
*/
void vpkc_set_logger(vpkc_log_callback_t cb_log,
void *p_user_data);

View File

@@ -31,12 +31,6 @@ static inline std::optional<std::string> to_cpp_string(const char* psz)
return psz == nullptr ? std::optional<std::string>("") : std::optional<std::string>(psz);
}
static inline char* alloc_c_string(const std::optional<std::string>& str)
{
if (!str.has_value()) { return nullptr; }
return alloc_c_string(str.value());
}
static inline char* alloc_c_string(const std::string& str)
{
char* result = new char[str.size() + 1]; // +1 for null-terminator
@@ -49,6 +43,12 @@ static inline char* alloc_c_string(const std::string& str)
return result;
}
static inline char* alloc_c_string(const std::optional<std::string>& str)
{
if (!str.has_value()) { return nullptr; }
return alloc_c_string(str.value());
}
static inline void free_c_string(char* str)
{
delete[] str;
@@ -165,7 +165,7 @@ static inline void free_c_VelopackLocatorConfig(vpkc_locator_config_t* obj) {
free_c_string(obj->PackagesDir);
free_c_string(obj->ManifestPath);
free_c_string(obj->CurrentBinaryDir);
delete obj;
}
@@ -268,7 +268,7 @@ static inline void free_c_VelopackAsset(vpkc_asset_t* obj) {
free_c_string(obj->FileName);
free_c_string(obj->SHA1);
free_c_string(obj->SHA256);
free_c_string(obj->NotesMarkdown);
free_c_string(obj->NotesHtml);
delete obj;
@@ -354,7 +354,7 @@ static inline void free_c_UpdateInfo(vpkc_update_info_t* obj) {
free_c_VelopackAsset(obj->TargetFullRelease);
free_c_VelopackAsset(obj->BaseRelease);
free_c_VelopackAsset_vec(obj->DeltasToTarget, obj->DeltasToTargetCount);
delete obj;
}
@@ -443,9 +443,9 @@ static inline vpkc_update_options_t** alloc_c_UpdateOptions_vec(const std::vecto
static inline void free_c_UpdateOptions(vpkc_update_options_t* obj) {
if (obj == nullptr) { return; }
free_c_string(obj->ExplicitChannel);
delete obj;
}
@@ -458,20 +458,23 @@ static inline void free_c_UpdateOptions_vec(vpkc_update_options_t** arr, size_t
}
// !! AUTO-GENERATED-END CPP_TYPES
/**
/**
* VelopackApp helps you to handle app activation events correctly.
* This should be used as early as possible in your application startup code.
* (eg. the beginning of main() or wherever your entry point is)
* To use this class, you should create a new VelopackApp::Build() builder instance,
* and then chain calls to the builder to configure your app.
* Finally, call the Run() method to execute the Velopack logic.
*/
class VelopackApp {
private:
VelopackApp() {};
public:
/**
* Build a new VelopackApp instance.
* Create and return a new VelopackApp builder.
*/
static VelopackApp Build() {
return VelopackApp();
static VelopackApp Build() {
return VelopackApp();
};
/**
@@ -491,7 +494,10 @@ public:
};
/**
* Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1))
* Override the command line arguments used by VelopackApp.
* By default, Velopack will use the command line arguments from the current process.
* @param args The command line arguments to use.
* @returns A reference to the builder.
*/
VelopackApp& SetArgs(const std::vector<std::string>& args) {
size_t c;
@@ -502,7 +508,9 @@ public:
};
/**
* VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
* Override the default VelopackLocator. The locator is used to find important paths for the application.
* @param locator The locator to use.
* @returns A reference to the builder.
*/
VelopackApp& SetLocator(const VelopackLocatorConfig& locator) {
vpkc_locator_config_t* vpkc_locator = alloc_c_VelopackLocatorConfig(locator);
@@ -512,68 +520,85 @@ public:
};
/**
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* This hook is triggered after the app has been installed.
* WARNING: This hook is run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cbAfterInstall The callback to run after the app has been installed.
* @returns A reference to the builder.
*/
VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_after_install(cbInstall);
VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbAfterInstall) {
vpkc_app_set_hook_after_install(cbAfterInstall);
return *this;
};
/**
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* This hook is triggered before the app is uninstalled.
* WARNING: This hook is run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cbBeforeUninstall The callback to run before the app is uninstalled.
* @returns A reference to the builder.
*/
VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_before_uninstall(cbInstall);
VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbBeforeUninstall) {
vpkc_app_set_hook_before_uninstall(cbBeforeUninstall);
return *this;
};
/**
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* This hook is triggered before the app is updated.
* WARNING: This hook is run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cbBeforeUpdate The callback to run before the app is updated.
* @returns A reference to the builder.
*/
VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_before_update(cbInstall);
VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbBeforeUpdate) {
vpkc_app_set_hook_before_update(cbBeforeUpdate);
return *this;
};
/**
* WARNING: FastCallback hooks are run during critical stages of Velopack operations.
* This hook is triggered after the app is updated.
* WARNING: This hook is run during critical stages of Velopack operations.
* Your code will be run and then the process will exit.
* If your code has not completed within 30 seconds, it will be terminated.
* Only supported on windows; On other operating systems, this will never be called.
* @param cbAfterUpdate The callback to run after the app is updated.
* @returns A reference to the builder.
*/
VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_after_update(cbInstall);
VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbAfterUpdate) {
vpkc_app_set_hook_after_update(cbAfterUpdate);
return *this;
};
/**
* This hook is triggered when the application is started for the first time after installation.
* @param cbFirstRun The callback to run when the application is started for the first time.
* @returns A reference to the builder.
*/
VelopackApp& OnFirstRun(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_first_run(cbInstall);
VelopackApp& OnFirstRun(vpkc_hook_callback_t cbFirstRun) {
vpkc_app_set_hook_first_run(cbFirstRun);
return *this;
};
/**
* This hook is triggered when the application is restarted by Velopack after installing updates.
* @param cbRestarted The callback to run when the application is restarted.
* @returns A reference to the builder.
*/
VelopackApp& OnRestarted(vpkc_hook_callback_t cbInstall) {
vpkc_app_set_hook_restarted(cbInstall);
VelopackApp& OnRestarted(vpkc_hook_callback_t cbRestarted) {
vpkc_app_set_hook_restarted(cbRestarted);
return *this;
};
/**
* Runs the Velopack startup logic. This should be the first thing to run in your app.
* In some circumstances it may terminate/restart the process to perform tasks.
* @param pUserData A pointer to user data that will be passed to any hooks that are executed.
*/
void Run(void* pUserData = 0) {
vpkc_app_run(pUserData);
@@ -586,7 +611,7 @@ public:
typedef std::function<void(int16_t)> vpkc_progress_send_t;
/**
* Abstract class for retrieving release feeds and downloading assets. You should subclass this and
* Abstract class for retrieving release feeds and downloading assets. You should subclass this and
* implement/override the GetReleaseFeed and DownloadReleaseEntry methods.
* This class is used by the UpdateManager to fetch release feeds and download assets in a custom way.
* SAFETY: It is your responsibility to ensure that a derived class instance is thread-safe,
@@ -596,20 +621,26 @@ class IUpdateSource {
friend class UpdateManager;
friend class FileSource;
friend class HttpSource;
private:
private:
IUpdateSource(vpkc_update_source_t* pSource) : m_pSource(pSource) {}
vpkc_update_source_t* m_pSource = 0;
public:
/**
* Destructor for IUpdateSource.
*/
virtual ~IUpdateSource() {
vpkc_free_source(m_pSource);
}
/**
* Default constructor for IUpdateSource. This will create a new custom source that calls back into the virtual methods of this class.
*/
IUpdateSource() {
m_pSource = vpkc_new_source_custom_callback(
[](void* userData, const char* releasesName) {
IUpdateSource* source = reinterpret_cast<IUpdateSource*>(userData);
std::string json = source->GetReleaseFeed(releasesName);
return alloc_c_string(json);
},
},
[](void* userData, char* pszFeed) {
free_c_string(pszFeed);
},
@@ -623,6 +654,9 @@ public:
return source->DownloadReleaseEntry(asset, localPath, progress_callback);
},
this);
if (!m_pSource) {
throw_last_error();
}
}
/**
@@ -641,12 +675,16 @@ public:
*/
class FileSource : public IUpdateSource {
public:
/**
* Creates a new FileSource.
* @param filePath The path to the directory containing the releases.
*/
FileSource(const std::string& filePath) : IUpdateSource(vpkc_new_source_file(filePath.c_str())) { }
const std::string GetReleaseFeed(const std::string releasesName) override {
throw std::runtime_error("Not implemented");
const std::string GetReleaseFeed(const std::string releasesName) override {
throw std::runtime_error("Not implemented");
}
bool DownloadReleaseEntry(const VelopackAsset& asset, const std::string localFilePath, vpkc_progress_send_t progress) override {
throw std::runtime_error("Not implemented");
bool DownloadReleaseEntry(const VelopackAsset& asset, const std::string localFilePath, vpkc_progress_send_t progress) override {
throw std::runtime_error("Not implemented");
}
};
@@ -655,17 +693,22 @@ public:
*/
class HttpSource : public IUpdateSource {
public:
/**
* Creates a new HttpSource.
* @param httpUrl The URL to the releases feed.
*/
HttpSource(const std::string& httpUrl) : IUpdateSource(vpkc_new_source_http_url(httpUrl.c_str())) { }
const std::string GetReleaseFeed(const std::string releasesName) override {
throw std::runtime_error("Not implemented");
const std::string GetReleaseFeed(const std::string releasesName) override {
throw std::runtime_error("Not implemented");
}
bool DownloadReleaseEntry(const VelopackAsset& asset, const std::string localFilePath, vpkc_progress_send_t progress) override {
throw std::runtime_error("Not implemented");
bool DownloadReleaseEntry(const VelopackAsset& asset, const std::string localFilePath, vpkc_progress_send_t progress) override {
throw std::runtime_error("Not implemented");
}
};
/**
* Provides functionality for checking for updates, downloading updates, and applying updates to the current application.
* This class is the main entry point for interacting with Velopack.
*/
class UpdateManager {
private:
@@ -674,7 +717,7 @@ private:
public:
/**
* Create a new UpdateManager instance.
* Create a new UpdateManager instance for a local or remote directory of releases.
* @param urlOrPath Location of the http update server or the local update directory path containing releases.
* @param options Optional extra configuration for update manager.
* @param locator Override the default locator configuration (usually used for testing / mocks).
@@ -691,7 +734,7 @@ public:
};
/**
* Create a new UpdateManager instance.
* Create a new UpdateManager instance with a custom update source.
* @param updateSource The source to use for retrieving feed and downloading assets.
* @param options Optional extra configuration for update manager.
* @param locator Override the default locator configuration (usually used for testing / mocks).
@@ -749,8 +792,9 @@ public:
};
/**
* Returns an UpdateInfo object if there is an update downloaded which still needs to be applied.
* You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update.
* Returns a VelopackAsset object if there is an update downloaded which still needs to be applied.
* You can pass this object to WaitExitThenApplyUpdates to apply the update.
* @returns A VelopackAsset object if there is a pending update, otherwise null.
*/
std::optional<VelopackAsset> UpdatePendingRestart() noexcept {
vpkc_asset_t* asset;
@@ -763,8 +807,9 @@ public:
};
/**
* Checks for updates, returning None if there are none available. If there are updates available, this method will return an
* Checks for updates, returning null if there are none available. If there are updates available, this method will return an
* UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available.
* @returns An UpdateInfo object if there is an update available, otherwise null.
*/
std::optional<UpdateInfo> CheckForUpdates() {
vpkc_update_info_t* update;
@@ -791,6 +836,9 @@ public:
* this method will attempt to unpack and prepare them.
* - If there is no delta update available, or there is an error preparing delta
* packages, this method will fall back to downloading the full version of the update.
* @param update The update to download.
* @param progress A callback to report progress to.
* @param pUserData A pointer to user data that will be passed to the progress callback.
*/
void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress = nullptr, void* pUserData = 0) {
vpkc_update_info_t* vpkc_update = alloc_c_UpdateInfo(update);
@@ -805,6 +853,10 @@ public:
* This will launch the Velopack updater and tell it to wait for this program to exit gracefully.
* You should then clean up any state and exit your app. The updater will apply updates and then
* optionally restart your app. The updater will only wait for 60 seconds before giving up.
* @param asset The UpdateInfo object for the update to apply.
* @param silent If true, the updater will not show any UI.
* @param restart If true, the app will be restarted after the update is applied.
* @param restartArgs The arguments to pass to the app when it is restarted.
*/
void WaitExitThenApplyUpdates(const UpdateInfo& asset, bool silent = false, bool restart = true, std::vector<std::string> restartArgs = {}) {
this->WaitExitThenApplyUpdates(asset.TargetFullRelease, silent, restart, restartArgs);
@@ -814,6 +866,10 @@ public:
* This will launch the Velopack updater and tell it to wait for this program to exit gracefully.
* You should then clean up any state and exit your app. The updater will apply updates and then
* optionally restart your app. The updater will only wait for 60 seconds before giving up.
* @param asset The update to apply.
* @param silent If true, the updater will not show any UI.
* @param restart If true, the app will be restarted after the update is applied.
* @param restartArgs The arguments to pass to the app when it is restarted.
*/
void WaitExitThenApplyUpdates(const VelopackAsset& asset, bool silent = false, bool restart = true, std::vector<std::string> restartArgs = {}) {
size_t cRestartArgs;
@@ -829,9 +885,14 @@ public:
/**
* This will launch the Velopack updater and optionally wait for a program to exit gracefully.
* This method is unsafe because it does not necessarily wait for any / the correct process to exit
* This method is unsafe because it does not necessarily wait for any / the correct process to exit
* before applying updates. The `WaitExitThenApplyUpdates` method is recommended for most use cases.
* If waitPid is 0, the updater will not wait for any process to exit before applying updates (Not Recommended).
* @param asset The update to apply.
* @param silent If true, the updater will not show any UI.
* @param waitPid The process ID to wait for before applying updates. If 0, the updater will not wait.
* @param restart If true, the app will be restarted after the update is applied.
* @param restartArgs The arguments to pass to the app when it is restarted.
*/
void UnsafeApplyUpdates(const VelopackAsset& asset, bool silent, uint32_t waitPid, bool restart, std::vector<std::string> restartArgs) {
size_t cRestartArgs;

View File

@@ -20,6 +20,8 @@ use velopack::logging::{default_logfile_path, init_logging};
use velopack::{sources, ApplyWaitMode, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp};
/// Create a new FileSource update source for a given file path.
/// @param psz_file_path The path to a local directory containing updates.
/// @returns A new vpkc_update_source_t instance, or null on error.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -33,6 +35,8 @@ pub extern "C" fn vpkc_new_source_file(psz_file_path: *const c_char) -> *mut vpk
}
/// Create a new HttpSource update source for a given HTTP URL.
/// @param psz_http_url The URL to a remote update server.
/// @returns A new vpkc_update_source_t instance, or null on error.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -50,6 +54,11 @@ pub extern "C" fn vpkc_new_source_http_url(psz_http_url: *const c_char) -> *mut
/// for the lifetime of any UpdateManager's that use this source. You should call `vpkc_free_source` to free the source,
/// but note that if the source is still in use by an UpdateManager, it will not be freed until the UpdateManager is freed.
/// Therefore to avoid possible issues, it is recommended to create this type of source once for the lifetime of your application.
/// @param cb_release_feed A callback to fetch the release feed.
/// @param cb_free_release_feed A callback to free the memory allocated by `cb_release_feed`.
/// @param cb_download_entry A callback to download an asset.
/// @param p_user_data Optional user data to be passed to the callbacks.
/// @returns A new vpkc_update_source_t instance, or null on error. If null, the error will be available via `vpkc_get_last_error`.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -59,8 +68,21 @@ pub extern "C" fn vpkc_new_source_custom_callback(
cb_download_entry: vpkc_download_asset_delegate_t,
p_user_data: *mut c_void,
) -> *mut vpkc_update_source_t {
if cb_release_feed.is_none() || cb_download_entry.is_none() || cb_free_release_feed.is_none() {
log::error!("cb_release_feed, cb_download_entry, or cb_free_release_feed is null");
if cb_release_feed.is_none() {
log::error!("cb_release_feed must not be null");
set_last_error("cb_release_feed must not be null");
return ptr::null_mut();
}
if cb_download_entry.is_none() {
log::error!("cb_download_entry must not be null");
set_last_error("cb_download_entry must not be null");
return ptr::null_mut();
}
if cb_free_release_feed.is_none() {
log::error!("cb_free_release_feed must not be null");
set_last_error("cb_free_release_feed must not be null");
return ptr::null_mut();
}
@@ -76,12 +98,15 @@ pub extern "C" fn vpkc_new_source_custom_callback(
/// Sends a progress update to the callback with the specified ID. This is used by custom
/// update sources created with `vpkc_new_source_custom_callback` to report download progress.
/// @param progress_callback_id The ID of the progress callback to send the update to.
/// @param progress The progress value to send (0-100).
#[no_mangle]
pub extern "C" fn vpkc_source_report_progress(progress_callback_id: size_t, progress: i16) {
report_csource_progress(progress_callback_id, progress);
}
/// Frees a vpkc_update_source_t instance.
/// @param p_source The source to free.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -90,9 +115,11 @@ pub extern "C" fn vpkc_free_source(p_source: *mut vpkc_update_source_t) {
}
/// Create a new UpdateManager instance.
/// @param urlOrPath Location of the update server or path to the local update directory.
/// @param options Optional extra configuration for update manager.
/// @param locator Override the default locator configuration (usually used for testing / mocks).
/// @param psz_url_or_path Location of the http update server url or path to the local update directory.
/// @param p_options Optional extra configuration for update manager.
/// @param p_locator Optional explicit path configuration for Velopack. If null, the default locator will be used.
/// @param p_manager A pointer to where the new vpkc_update_manager_t* instance will be stored.
/// @returns True if the update manager was created successfully, false otherwise. If false, the error will be available via `vpkc_get_last_error`.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -114,9 +141,11 @@ pub extern "C" fn vpkc_new_update_manager(
}
/// Create a new UpdateManager instance with a custom UpdateSource.
/// @param urlOrPath Location of the update server or path to the local update directory.
/// @param options Optional extra configuration for update manager.
/// @param locator Override the default locator configuration (usually used for testing / mocks).
/// @param p_source A pointer to a custom UpdateSource.
/// @param p_options Optional extra configuration for update manager.
/// @param p_locator Optional explicit path configuration for Velopack. If null, the default locator will be used.
/// @param p_manager A pointer to where the new vpkc_update_manager_t* instance will be stored.
/// @returns True if the update manager was created successfully, false otherwise. If false, the error will be available via `vpkc_get_last_error`.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -137,6 +166,10 @@ pub extern "C" fn vpkc_new_update_manager_with_source(
}
/// Returns the currently installed version of the app.
/// @param p_manager The update manager instance.
/// @param psz_version A buffer to store the version string.
/// @param c_version The size of the `psz_version` buffer.
/// @returns The number of characters written to `psz_version` (including null terminator), or the required buffer size if the buffer is too small.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -151,6 +184,10 @@ pub extern "C" fn vpkc_get_current_version(p_manager: *mut vpkc_update_manager_t
}
/// Returns the currently installed app id.
/// @param p_manager The update manager instance.
/// @param psz_id A buffer to store the app id string.
/// @param c_id The size of the `psz_id` buffer.
/// @returns The number of characters written to `psz_id` (including null terminator), or the required buffer size if the buffer is too small.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -166,6 +203,8 @@ pub extern "C" fn vpkc_get_app_id(p_manager: *mut vpkc_update_manager_t, psz_id:
/// Returns whether the app is in portable mode. On Windows this can be true or false.
/// On MacOS and Linux this will always be true.
/// @param p_manager The update manager instance.
/// @returns True if the app is in portable mode, false otherwise.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -176,8 +215,11 @@ pub extern "C" fn vpkc_is_portable(p_manager: *mut vpkc_update_manager_t) -> boo
}
}
/// Returns an UpdateInfo object if there is an update downloaded which still needs to be applied.
/// You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update.
/// Returns an asset if there is an update downloaded which still needs to be applied.
/// You can pass this asset to `vpkc_wait_exit_then_apply_updates` to apply the update.
/// @param p_manager The update manager instance.
/// @param p_asset A pointer to where the new vpkc_asset_t* instance will be stored.
/// @returns True if there is an update pending restart, false otherwise.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -194,8 +236,11 @@ pub extern "C" fn vpkc_update_pending_restart(p_manager: *mut vpkc_update_manage
}
}
/// Checks for updates, returning None if there are none available. If there are updates available, this method will return an
/// Checks for updates. If there are updates available, this method will return an
/// UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available.
/// @param p_manager The update manager instance.
/// @param p_update A pointer to where the new vpkc_update_info_t* instance will be stored if an update is available.
/// @returns A `vpkc_update_check_t` value indicating the result of the check. If an update is available, the value will be `HasUpdate` and `p_update` will be populated.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -229,6 +274,12 @@ pub extern "C" fn vpkc_check_for_updates(
/// this method will attempt to unpack and prepare them.
/// - If there is no delta update available, or there is an error preparing delta
/// packages, this method will fall back to downloading the full version of the update.
/// @param p_manager The update manager instance.
/// @param p_update The update info object from `vpkc_check_for_updates`.
/// @param cb_progress An optional callback to report download progress (0-100).
/// @param p_user_data Optional user data to be passed to the progress callback.
/// @returns true on success, false on failure. If false, the error will be available via `vpkc_get_last_error`.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -294,7 +345,14 @@ pub extern "C" fn vpkc_download_updates(
/// This will launch the Velopack updater and tell it to wait for this program to exit gracefully.
/// You should then clean up any state and exit your app. The updater will apply updates and then
/// optionally restart your app. The updater will only wait for 60 seconds before giving up.
/// (if specified) restart your app. The updater will only wait for 60 seconds before giving up.
/// @param p_manager The update manager instance.
/// @param p_asset The asset to apply. This can be from `vpkc_update_pending_restart` or `vpkc_update_info_get_target_asset`.
/// @param b_silent True to attempt to apply the update without showing any UI.
/// @param b_restart True to restart the app after the update is applied.
/// @param p_restart_args An array of command line arguments to pass to the new process when it's restarted.
/// @param c_restart_args The number of arguments in `p_restart_args`.
/// @returns true on success, false on failure. If false, the error will be available via `vpkc_get_last_error`.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -323,6 +381,14 @@ pub extern "C" fn vpkc_wait_exit_then_apply_updates(
/// This method is unsafe because it does not necessarily wait for any / the correct process to exit
/// before applying updates. The `vpkc_wait_exit_then_apply_updates` method is recommended for most use cases.
/// If dw_wait_pid is 0, the updater will not wait for any process to exit before applying updates (Not Recommended).
/// @param p_manager The update manager instance.
/// @param p_asset The asset to apply. This can be from `vpkc_update_pending_restart` or `vpkc_update_info_get_target_asset`.
/// @param b_silent True to attempt to apply the update without showing any UI.
/// @param dw_wait_pid The process ID to wait for before applying updates. If 0, the updater will not wait.
/// @param b_restart True to restart the app after the update is applied.
/// @param p_restart_args An array of command line arguments to pass to the new process when it's restarted.
/// @param c_restart_args The number of arguments in `p_restart_args`.
/// @returns true on success, false on failure. If false, the error will be available via `vpkc_get_last_error`.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -346,6 +412,7 @@ pub extern "C" fn vpkc_unsafe_apply_updates(
}
/// Frees a vpkc_update_manager_t instance.
/// @param p_manager The update manager instance to free.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -354,6 +421,7 @@ pub extern "C" fn vpkc_free_update_manager(p_manager: *mut vpkc_update_manager_t
}
/// Frees a vpkc_update_info_t instance.
/// @param p_update_info The update info instance to free.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -362,6 +430,7 @@ pub extern "C" fn vpkc_free_update_info(p_update_info: *mut vpkc_update_info_t)
}
/// Frees a vpkc_asset_t instance.
/// @param p_asset The asset instance to free.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -371,7 +440,9 @@ pub extern "C" fn vpkc_free_asset(p_asset: *mut vpkc_asset_t) {
/// VelopackApp helps you to handle app activation events correctly.
/// This should be used as early as possible in your application startup code.
/// (eg. the beginning of main() or wherever your entry point is)
/// (eg. the beginning of main() or wherever your entry point is).
/// This function will not return in some cases.
/// @param p_user_data Optional user data to be passed to the callbacks.
#[no_mangle]
#[logfn(Trace)]
#[logfn_inputs(Trace)]
@@ -449,6 +520,7 @@ pub extern "C" fn vpkc_app_run(p_user_data: *mut c_void) {
}
/// Set whether to automatically apply downloaded updates on startup. This is ON by default.
/// @param b_auto_apply True to automatically apply updates, false otherwise.
#[no_mangle]
pub extern "C" fn vpkc_app_set_auto_apply_on_startup(b_auto_apply: bool) {
update_app_options(|opt| {
@@ -457,6 +529,8 @@ pub extern "C" fn vpkc_app_set_auto_apply_on_startup(b_auto_apply: bool) {
}
/// Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1))
/// @param p_args An array of command line arguments.
/// @param c_args The number of arguments in `p_args`.
#[no_mangle]
pub extern "C" fn vpkc_app_set_args(p_args: *mut *mut c_char, c_args: size_t) {
update_app_options(|opt| {
@@ -465,6 +539,7 @@ pub extern "C" fn vpkc_app_set_args(p_args: *mut *mut c_char, c_args: size_t) {
}
/// VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
/// @param p_locator The locator configuration to use.
#[no_mangle]
pub extern "C" fn vpkc_app_set_locator(p_locator: *mut vpkc_locator_config_t) {
update_app_options(|opt| {
@@ -472,10 +547,12 @@ pub extern "C" fn vpkc_app_set_locator(p_locator: *mut vpkc_locator_config_t) {
});
}
/// Sets a callback to be run after the app is installed.
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 30 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
/// @param cb_after_install The callback to run after the app is installed. The callback takes a user data pointer and the version of the app as a string.
#[no_mangle]
pub extern "C" fn vpkc_app_set_hook_after_install(cb_after_install: vpkc_hook_callback_t) {
update_app_options(|opt| {
@@ -483,10 +560,12 @@ pub extern "C" fn vpkc_app_set_hook_after_install(cb_after_install: vpkc_hook_ca
});
}
/// Sets a callback to be run before the app is uninstalled.
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 30 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
/// @param cb_before_uninstall The callback to run before the app is uninstalled. The callback takes a user data pointer and the version of the app as a string.
#[no_mangle]
pub extern "C" fn vpkc_app_set_hook_before_uninstall(cb_before_uninstall: vpkc_hook_callback_t) {
update_app_options(|opt| {
@@ -494,10 +573,12 @@ pub extern "C" fn vpkc_app_set_hook_before_uninstall(cb_before_uninstall: vpkc_h
});
}
/// Sets a callback to be run before the app is updated.
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 30 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
/// @param cb_before_update The callback to run before the app is updated. The callback takes a user data pointer and the version of the app as a string.
#[no_mangle]
pub extern "C" fn vpkc_app_set_hook_before_update(cb_before_update: vpkc_hook_callback_t) {
update_app_options(|opt| {
@@ -505,10 +586,12 @@ pub extern "C" fn vpkc_app_set_hook_before_update(cb_before_update: vpkc_hook_ca
});
}
/// Sets a callback to be run after the app is updated.
/// WARNING: FastCallback hooks are run during critical stages of Velopack operations.
/// Your code will be run and then the process will exit.
/// If your code has not completed within 30 seconds, it will be terminated.
/// Only supported on windows; On other operating systems, this will never be called.
/// @param cb_after_update The callback to run after the app is updated. The callback takes a user data pointer and the version of the app as a string.
#[no_mangle]
pub extern "C" fn vpkc_app_set_hook_after_update(cb_after_update: vpkc_hook_callback_t) {
update_app_options(|opt| {
@@ -517,6 +600,7 @@ pub extern "C" fn vpkc_app_set_hook_after_update(cb_after_update: vpkc_hook_call
}
/// This hook is triggered when the application is started for the first time after installation.
/// @param cb_first_run The callback to run on first run. The callback takes a user data pointer and the version of the app as a string.
#[no_mangle]
pub extern "C" fn vpkc_app_set_hook_first_run(cb_first_run: vpkc_hook_callback_t) {
update_app_options(|opt| {
@@ -525,6 +609,7 @@ pub extern "C" fn vpkc_app_set_hook_first_run(cb_first_run: vpkc_hook_callback_t
}
/// This hook is triggered when the application is restarted by Velopack after installing updates.
/// @param cb_restarted The callback to run after the app is restarted. The callback takes a user data pointer and the version of the app as a string.
#[no_mangle]
pub extern "C" fn vpkc_app_set_hook_restarted(cb_restarted: vpkc_hook_callback_t) {
update_app_options(|opt| {
@@ -533,6 +618,9 @@ pub extern "C" fn vpkc_app_set_hook_restarted(cb_restarted: vpkc_hook_callback_t
}
/// Get the last error message that occurred in the Velopack library.
/// @param psz_error A buffer to store the error message.
/// @param c_error The size of the `psz_error` buffer.
/// @returns The number of characters written to `psz_error` (including null terminator). If the return value is greater than `c_error`, the buffer was too small and the message was truncated.
#[no_mangle]
pub extern "C" fn vpkc_get_last_error(psz_error: *mut c_char, c_error: size_t) -> size_t {
let error = get_last_error();
@@ -540,6 +628,8 @@ pub extern "C" fn vpkc_get_last_error(psz_error: *mut c_char, c_error: size_t) -
}
/// Set a custom log callback. This will be called for all log messages generated by the Velopack library.
/// @param cb_log The callback to call with log messages. The callback takes a user data pointer, a log level, and the log message as a string.
/// @param p_user_data Optional user data to be passed to the callback.
#[no_mangle]
pub extern "C" fn vpkc_set_logger(cb_log: vpkc_log_callback_t, p_user_data: *mut c_void) {
set_log_callback(cb_log, p_user_data);

View File

@@ -446,7 +446,7 @@ pub struct vpkc_update_options_t {
/// ExplicitChannel to switch channels to another channel where the latest version on that
/// channel is lower than the current version.
pub AllowVersionDowngrade: bool,
/// **This option should usually be left None**.
/// **This option should usually be left None/NULL**.
/// Overrides the default channel used to fetch updates.
/// The default channel will be whatever channel was specified on the command line when building this release.
/// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.

View File

@@ -128,13 +128,21 @@ namespace Velopack.Sources
/// Given a repository URL (e.g. https://Gitea.com/myuser/myrepo) this function
/// returns the API base for performing requests. (eg. "https://api.Gitea.com/"
/// or http://internal.Gitea.server.local/api/v1)
/// or http://localhost:3000/api/v1
/// </summary>
/// <param name="repoUrl"></param>
/// <returns></returns>
protected virtual Uri GetApiBaseUrl(Uri repoUrl)
{
Uri baseAddress = new Uri(string.Format("{0}{1}{2}/api/v1/", repoUrl.Scheme, Uri.SchemeDelimiter, repoUrl.Host));
// above ^^ notice the end slashes for the baseAddress, explained here: http://stackoverflow.com/a/23438417/162694
Uri baseAddress;
if (repoUrl.IsDefaultPort) {
baseAddress = new Uri(string.Format("{0}{1}{2}/api/v1/", repoUrl.Scheme, Uri.SchemeDelimiter, repoUrl.Host));
// above ^^ notice the end slashes for the baseAddress, explained here: http://stackoverflow.com/a/23438417/162694
} else {
baseAddress = new Uri(string.Format("{0}{1}{2}:{3}/api/v1/", repoUrl.Scheme, Uri.SchemeDelimiter, repoUrl.Host, repoUrl.Port));
}
return baseAddress;
}
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
namespace Velopack.Util
{
internal static partial class SymbolicLink
{
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
[DllImport("libc", SetLastError = true)]
private static extern nint readlink(string path, byte[] buffer, ulong bufferSize);
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
[DllImport("libc", SetLastError = true)]
private static extern int symlink(string target, string linkPath);
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
private static string UnixReadLink(string symlinkPath)
{
const int bufferSize = 1024;
const int EINTR = 4;
byte[] buffer = new byte[bufferSize];
nint bytesWritten;
do {
bytesWritten = readlink(symlinkPath, buffer, bufferSize);
} while (bytesWritten == -1 && Marshal.GetLastWin32Error() == EINTR);
if (bytesWritten < 1) {
throw new InvalidOperationException($"Error resolving symlink: {Marshal.GetLastWin32Error()}");
}
return Encoding.UTF8.GetString(buffer, 0, (int) bytesWritten);
}
[SupportedOSPlatform("linux")]
[SupportedOSPlatform("macos")]
private static void UnixCreateSymlink(string target, string linkPath)
{
const int EINTR = 4;
int result;
do {
result = symlink(target, linkPath);
} while (result == -1 && Marshal.GetLastWin32Error() == EINTR);
if (result == -1) {
throw new InvalidOperationException($"Error creating symlink: {Marshal.GetLastWin32Error()}");
}
}
}
}

View File

@@ -0,0 +1,211 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace Velopack.Util
{
internal static partial class SymbolicLink
{
private const string Kernel32 = "kernel32.dll";
private const uint FSCTL_GET_REPARSE_POINT = 0x000900A8;
private const uint IO_REPARSE_TAG_SYMLINK = 0xA000000C;
private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
private const int ERROR_NOT_A_REPARSE_POINT = 4390;
private const int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024;
private const uint SYMLINK_FLAG_RELATIVE = 1;
private const uint GENERIC_READ = 0x80000000;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const uint OPEN_EXISTING = 3;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
private const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
[StructLayout(LayoutKind.Sequential)]
private struct ReparseHeader
{
public uint ReparseTag;
public ushort ReparseDataLength;
public ushort Reserved;
}
[StructLayout(LayoutKind.Sequential)]
private struct SymbolicData
{
public ushort SubstituteNameOffset;
public ushort SubstituteNameLength;
public ushort PrintNameOffset;
public ushort PrintNameLength;
public uint Flags;
}
[StructLayout(LayoutKind.Sequential)]
private struct JunctionData
{
public ushort SubstituteNameOffset;
public ushort SubstituteNameLength;
public ushort PrintNameOffset;
public ushort PrintNameLength;
}
[SupportedOSPlatform("windows")]
[DllImport(Kernel32, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "CreateSymbolicLinkW")]
[return: MarshalAs(UnmanagedType.I1)]
private static extern bool PInvokeWindowsCreateSymlink(
[In]
string lpSymlinkFileName,
[In]
string lpTargetFileName,
[In]
SymbolicLinkFlag dwFlags);
[DllImport(Kernel32, SetLastError = true, CharSet = CharSet.Unicode)]
private static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport(Kernel32, SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeviceIoControl(
SafeFileHandle hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
[SupportedOSPlatform("windows")]
private static string WindowsReadLink(string symlinkPath)
{
using (var hReparsePoint = CreateFile(
symlinkPath,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
IntPtr.Zero)) {
if (hReparsePoint.IsInvalid) {
throw new Win32Exception(Marshal.GetLastWin32Error());
}
var buffer = Marshal.AllocHGlobal(MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
try {
int bytesReturned;
var success = DeviceIoControl(
hReparsePoint,
FSCTL_GET_REPARSE_POINT,
IntPtr.Zero,
0,
buffer,
MAXIMUM_REPARSE_DATA_BUFFER_SIZE,
out bytesReturned,
IntPtr.Zero);
if (!success) {
int error = Marshal.GetLastWin32Error();
// The file or directory is not a reparse point.
if (error == ERROR_NOT_A_REPARSE_POINT) {
throw new InvalidOperationException($"Path is not a symbolic link: {symlinkPath}");
}
throw new Win32Exception(error);
}
var reparseHeader = Marshal.PtrToStructure<ReparseHeader>(buffer);
var reparseHeaderSize = Marshal.SizeOf<ReparseHeader>();
// We always use SubstituteName instead of PrintName,
// the latter is just the display name and can show something unrelated to the target.
if (reparseHeader.ReparseTag == IO_REPARSE_TAG_SYMLINK) {
var symbolicData = Marshal.PtrToStructure<SymbolicData>(buffer + reparseHeaderSize);
var offset = Marshal.SizeOf<SymbolicData>() + reparseHeaderSize;
var target = ReadStringFromBuffer(buffer, offset + symbolicData.SubstituteNameOffset, symbolicData.SubstituteNameLength);
bool isRelative = (symbolicData.Flags & SYMLINK_FLAG_RELATIVE) != 0;
if (!isRelative) {
// Absolute target is in NT format and we need to clean it up
return ParseNTPath(target);
}
// Return relative path as-is
return target;
} else if (reparseHeader.ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
var junctionData = Marshal.PtrToStructure<JunctionData>(buffer + reparseHeaderSize);
var offset = Marshal.SizeOf<JunctionData>() + reparseHeaderSize;
var target = ReadStringFromBuffer(buffer, offset + junctionData.SubstituteNameOffset, junctionData.SubstituteNameLength);
// Mount points are always absolute and in NT format
return ParseNTPath(target);
}
throw new InvalidOperationException($"Unsupported reparse point type: 0x{reparseHeader.ReparseTag:X}");
} finally {
Marshal.FreeHGlobal(buffer);
}
}
}
private static string ReadStringFromBuffer(IntPtr buffer, int offset, int byteCount)
{
var bytes = new byte[byteCount];
Marshal.Copy(buffer + offset, bytes, 0, byteCount);
return Encoding.Unicode.GetString(bytes);
}
private static string ParseNTPath(string path)
{
// NT paths come in different forms:
// \??\C:\foo - DOS device path
// \DosDevices\C:\foo - DOS device path
// \Global??\C:\foo - DOS device path
// \??\UNC\server\share - UNC path
const string NTPathPrefix = "\\??\\";
const string UNCNTPathPrefix = "\\??\\UNC\\";
const string UNCPathPrefix = "\\\\";
if (path.StartsWith(UNCNTPathPrefix, StringComparison.OrdinalIgnoreCase)) {
// Convert \??\UNC\server\share to \\server\share
return UNCPathPrefix + path.Substring(UNCNTPathPrefix.Length);
}
string[] dosDevicePrefixes = { NTPathPrefix, "\\DosDevices\\", "\\Global??\\" };
foreach (var prefix in dosDevicePrefixes) {
if (path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) {
path = path.Substring(prefix.Length);
break;
}
}
// Remove trailing backslash except for root paths like C:\
if (path.Length > 3 && path.EndsWith("\\")) {
path = path.TrimEnd('\\');
}
return path;
}
[SupportedOSPlatform("windows")]
private static void WindowsCreateSymlink(string target, string linkPath, SymbolicLinkFlag mode)
{
if (!PInvokeWindowsCreateSymlink(linkPath, target, mode)) {
var errorCode = Marshal.GetLastWin32Error();
throw new InvalidOperationException($"Error creating symlink: {errorCode}", new Win32Exception());
}
}
}
}

View File

@@ -1,13 +1,10 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using NCode.ReparsePoints;
namespace Velopack.Util
{
internal static class SymbolicLink
internal static partial class SymbolicLink
{
/// <summary>
/// Creates a symlink from the specified directory to the specified target directory.
@@ -38,9 +35,9 @@ namespace Velopack.Util
: targetPath;
if (Directory.Exists(targetPath)) {
CreateDirectoryLink(linkPath, finalTarget, targetPath);
CreateSymlink(linkPath, finalTarget, SymbolicLinkFlag.Directory);
} else if (File.Exists(targetPath)) {
CreateFileLink(linkPath, finalTarget);
CreateSymlink(linkPath, finalTarget, SymbolicLinkFlag.File);
} else {
throw new IOException("Target path does not exist.");
}
@@ -63,7 +60,7 @@ namespace Velopack.Util
{
var isLink = TryGetLinkFsi(linkPath, out var fsi);
if (fsi != null && !isLink) {
throw new IOException("Path is not a junction point / symlink.");
ThrowPathNotASymlinkException(linkPath);
} else {
fsi?.Delete();
}
@@ -77,6 +74,7 @@ namespace Velopack.Util
public static string GetTarget(string linkPath, bool relative = false)
{
var target = GetUnresolvedTarget(linkPath);
if (relative) {
if (Path.IsPathRooted(target)) {
return PathUtil.MakePathRelativeTo(Path.GetDirectoryName(linkPath)!, target);
@@ -92,63 +90,62 @@ namespace Velopack.Util
}
}
private static void CreateFileLink(string linkPath, string targetPath)
[Serializable]
private enum SymbolicLinkFlag : uint
{
if (VelopackRuntimeInfo.IsWindows) {
var rp = new ReparsePointProvider();
rp.CreateSymbolicLink(linkPath, targetPath, false);
} else {
#if NETSTANDARD
UnixCreateSymlink(targetPath, linkPath);
#elif NET6_0_OR_GREATER
File.CreateSymbolicLink(linkPath, targetPath);
#else
throw new NotSupportedException();
#endif
}
File = 0,
Directory = 1,
}
private static void CreateDirectoryLink(string linkPath, string targetPath, string absoluteTargetPath)
private static void CreateSymlink(string linkPath, string targetPath, SymbolicLinkFlag mode)
{
if (VelopackRuntimeInfo.IsWindows) {
var rp = new ReparsePointProvider();
try {
rp.CreateSymbolicLink(linkPath, targetPath, true);
} catch (Win32Exception ex) when (ex.NativeErrorCode == 1314) {
// on windows 10 and below, symbolic links can only be created by an administrator
// junctions also do not support relative target path's
rp.CreateJunction(linkPath, absoluteTargetPath);
}
} else {
#if NETSTANDARD
UnixCreateSymlink(targetPath, linkPath);
#elif NET6_0_OR_GREATER
linkPath = linkPath.TrimEnd('\\', '/');
#if NET6_0_OR_GREATER
if (mode == SymbolicLinkFlag.File) {
File.CreateSymbolicLink(linkPath, targetPath);
} else if (mode == SymbolicLinkFlag.Directory) {
Directory.CreateSymbolicLink(linkPath, targetPath);
#else
throw new NotSupportedException();
#endif
} else {
throw new ArgumentOutOfRangeException(nameof(mode), mode, "Invalid symbolic link mode.");
}
#else
if (VelopackRuntimeInfo.IsWindows) {
WindowsCreateSymlink(targetPath, linkPath, mode);
} else if (VelopackRuntimeInfo.IsLinux || VelopackRuntimeInfo.IsOSX) {
UnixCreateSymlink(targetPath, linkPath);
} else {
throw new NotSupportedException("Symbolic links are not supported on this platform.");
}
#endif
}
private static string GetUnresolvedTarget(string linkPath)
{
linkPath = linkPath.TrimEnd('\\', '/');
if (!TryGetLinkFsi(linkPath, out var fsi)) {
throw new IOException("Path does not exist or is not a junction point / symlink.");
ThrowPathNotASymlinkException(linkPath);
}
if (VelopackRuntimeInfo.IsWindows) {
var rp = new ReparsePointProvider();
var link = rp.GetLink(linkPath);
return link.Target;
} else {
#if NETSTANDARD
return UnixReadLink(linkPath);
#elif NET6_0_OR_GREATER
return fsi!.LinkTarget!;
string target;
#if NET6_0_OR_GREATER
target = fsi!.LinkTarget!;
#else
throw new NotSupportedException();
#endif
if (VelopackRuntimeInfo.IsWindows) {
target = WindowsReadLink(linkPath);
} else if (VelopackRuntimeInfo.IsLinux || VelopackRuntimeInfo.IsOSX) {
target = UnixReadLink(linkPath);
} else {
throw new NotSupportedException("Symbolic links are not supported on this platform.");
}
#endif
if (String.IsNullOrEmpty(target)) {
ThrowPathNotASymlinkException(linkPath);
}
return target;
}
private static bool TryGetLinkFsi(string path, out FileSystemInfo? fsi)
@@ -167,36 +164,12 @@ namespace Velopack.Util
return (fsi.Attributes & FileAttributes.ReparsePoint) != 0;
}
#if NETSTANDARD
[DllImport("libc", SetLastError = true)]
private static extern nint readlink(string path, byte[] buffer, ulong bufferSize);
[DllImport("libc", SetLastError = true)]
private static extern int symlink(string target, string linkPath);
private static string UnixReadLink(string symlinkPath)
{
const int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
nint bytesWritten = readlink(symlinkPath, buffer, bufferSize);
if (bytesWritten < 1) {
throw new InvalidOperationException($"Error resolving symlink: {Marshal.GetLastWin32Error()}");
}
return Encoding.UTF8.GetString(buffer, 0, (int) bytesWritten);
}
private static void UnixCreateSymlink(string target, string linkPath)
{
// Call the symlink function from libc
int result = symlink(target, linkPath);
// Check for errors (-1 return value indicates failure)
if (result == -1) {
throw new InvalidOperationException($"Error creating symlink: {Marshal.GetLastWin32Error()}");
}
}
#if NET6_0_OR_GREATER
[DoesNotReturn]
#endif
private static void ThrowPathNotASymlinkException(string path)
{
throw new IOException($"The path '{path}' is not a symbolic link or junction point.");
}
}
}

View File

@@ -1459,9 +1459,9 @@
"license": "MIT"
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1566,9 +1566,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001721",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001721.tgz",
"integrity": "sha512-cOuvmUVtKrtEaoKiO0rSc29jcjwMwX5tOHDy4MgVFEWiUXj4uBMJkwI8MDySkgXidpMiHUcviogAvFi4pA2hDQ==",
"version": "1.0.30001723",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz",
"integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==",
"dev": true,
"funding": [
{
@@ -1898,9 +1898,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.165",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz",
"integrity": "sha512-naiMx1Z6Nb2TxPU6fiFrUrDTjyPMLdTtaOd2oLmG8zVSg2hCWGkhPyxwk+qRmZ1ytwVqUv0u7ZcDA5+ALhaUtw==",
"version": "1.5.167",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz",
"integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==",
"dev": true,
"license": "ISC"
},
@@ -2073,9 +2073,9 @@
}
},
"node_modules/filelist/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,25 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { VelopackAsset } from "./VelopackAsset";
/**
* Holds information about the current version and pending updates, such as how many there are, and access to release notes.
*/
export type UpdateInfo = {
/**
* The available version that we are updating to.
*/
TargetFullRelease: VelopackAsset,
/**
* The base release that this update is based on. This is only available if the update is a delta update.
*/
BaseRelease: VelopackAsset | null,
/**
* The list of delta updates that can be applied to the base version to get to the target version.
*/
DeltasToTarget: Array<VelopackAsset>,
/**
* True if the update is a version downgrade or lateral move (such as when switching channels to the same version number).
* In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
* deleted.
*/
IsDowngrade: boolean, };

View File

@@ -1,28 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* Options to customise the behaviour of UpdateManager.
*/
export type UpdateOptions = {
/**
* Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading).
* This could happen if a release has bugs and was retracted from the release feed, or if you're using
* ExplicitChannel to switch channels to another channel where the latest version on that
* channel is lower than the current version.
*/
AllowVersionDowngrade: boolean,
/**
* **This option should usually be left None**.
* Overrides the default channel used to fetch updates.
* The default channel will be whatever channel was specified on the command line when building this release.
* For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
* This allows users to automatically receive updates from the same channel they installed from. This options
* allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel
* without having to reinstall the application.
*/
ExplicitChannel: string | null,
/**
* Sets the maximum number of deltas to consider before falling back to a full update.
* The default is 10. Set to a negative number (eg. -1) to disable deltas.
*/
MaximumDeltasBeforeFallback: number, };

View File

@@ -1,42 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
*/
export type VelopackAsset = {
/**
* The name or Id of the package containing this release.
*/
PackageId: string,
/**
* The version of this release.
*/
Version: string,
/**
* The type of asset (eg. "Full" or "Delta").
*/
Type: string,
/**
* The filename of the update package containing this release.
*/
FileName: string,
/**
* The SHA1 checksum of the update package containing this release.
*/
SHA1: string,
/**
* The SHA256 checksum of the update package containing this release.
*/
SHA256: string,
/**
* The size in bytes of the update package containing this release.
*/
Size: bigint,
/**
* The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string.
*/
NotesMarkdown: string,
/**
* The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string.
*/
NotesHtml: string, };

View File

@@ -1,30 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
/**
* VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
*/
export type VelopackLocatorConfig = {
/**
* The root directory of the current app.
*/
RootAppDir: string,
/**
* The path to the Update.exe binary.
*/
UpdateExePath: string,
/**
* The path to the packages' directory.
*/
PackagesDir: string,
/**
* The current app manifest.
*/
ManifestPath: string,
/**
* The directory containing the application's user binaries.
*/
CurrentBinaryDir: string,
/**
* Whether the current application is portable or installed.
*/
IsPortable: boolean, };

View File

@@ -1,9 +1,7 @@
import * as addon from "./load";
import type { UpdateInfo } from "./bindings/UpdateInfo";
import type { UpdateOptions } from "./bindings/UpdateOptions";
import type { VelopackLocatorConfig } from "./bindings/VelopackLocatorConfig";
export { UpdateInfo, UpdateOptions, VelopackLocatorConfig };
import type { UpdateInfo, UpdateOptions, VelopackLocatorConfig, VelopackAsset } from "./types";
export { UpdateInfo, UpdateOptions, VelopackLocatorConfig, VelopackAsset };
type UpdateManagerOpaque = {};
declare module "./load" {
@@ -21,7 +19,7 @@ declare module "./load" {
function js_update_pending_restart(
um: UpdateManagerOpaque,
): UpdateInfo | null;
): string | null;
function js_check_for_updates_async(
um: UpdateManagerOpaque,
@@ -241,11 +239,15 @@ export class UpdateManager {
}
/**
* Returns an UpdateInfo object if there is an update downloaded which still needs to be applied.
* You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update.
* Returns an VelopackAsset object if there is an update downloaded which still needs to be applied.
* You can pass the VelopackAsset object to waitExitThenApplyUpdate to apply the update.
*/
getUpdatePendingRestart(): UpdateInfo | null {
return addon.js_update_pending_restart(this.opaque);
getUpdatePendingRestart(): VelopackAsset | null {
let json: string | null = addon.js_update_pending_restart(this.opaque);
if (json && json.length > 0) {
return JSON.parse(json);
}
return null;
}
/**
@@ -292,7 +294,7 @@ export class UpdateManager {
* optionally restart your app. The updater will only wait for 60 seconds before giving up.
*/
waitExitThenApplyUpdate(
update: UpdateInfo,
update: UpdateInfo | VelopackAsset,
silent: boolean = false,
restart: boolean = true,
restartArgs: string[] = [],
@@ -300,6 +302,12 @@ export class UpdateManager {
if (!update) {
throw new Error("update is required");
}
// the backend API only accepts VelopackAsset, so we need to extract it from UpdateInfo
if ("TargetFullRelease" in update && typeof update.TargetFullRelease === "object") {
update = update.TargetFullRelease;
}
addon.js_wait_exit_then_apply_update(
this.opaque,
JSON.stringify(update),

View File

@@ -0,0 +1,82 @@
// This file is auto-generated. Do not edit by hand.
/** VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). */
export type VelopackLocatorConfig = {
/** The root directory of the current app. */
RootAppDir: string,
/** The path to the Update.exe binary. */
UpdateExePath: string,
/** The path to the packages' directory. */
PackagesDir: string,
/** The current app manifest. */
ManifestPath: string,
/** The directory containing the application's user binaries. */
CurrentBinaryDir: string,
/** Whether the current application is portable or installed. */
IsPortable: boolean,
}
/** An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. */
export type VelopackAsset = {
/** The name or Id of the package containing this release. */
PackageId: string,
/** The version of this release. */
Version: string,
/** The type of asset (eg. "Full" or "Delta"). */
Type: string,
/** The filename of the update package containing this release. */
FileName: string,
/** The SHA1 checksum of the update package containing this release. */
SHA1: string,
/** The SHA256 checksum of the update package containing this release. */
SHA256: string,
/** The size in bytes of the update package containing this release. */
Size: number,
/** The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. */
NotesMarkdown: string,
/** The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. */
NotesHtml: string,
}
/** Holds information about the current version and pending updates, such as how many there are, and access to release notes. */
export type UpdateInfo = {
/** The available version that we are updating to. */
TargetFullRelease: VelopackAsset,
/** The base release that this update is based on. This is only available if the update is a delta update. */
BaseRelease?: VelopackAsset,
/** The list of delta updates that can be applied to the base version to get to the target version. */
DeltasToTarget: VelopackAsset[],
/**
* True if the update is a version downgrade or lateral move (such as when switching channels to the same version number).
* In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be
* deleted.
*/
IsDowngrade: boolean,
}
/** Options to customise the behaviour of UpdateManager. */
export type UpdateOptions = {
/**
* Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading).
* This could happen if a release has bugs and was retracted from the release feed, or if you're using
* ExplicitChannel to switch channels to another channel where the latest version on that
* channel is lower than the current version.
*/
AllowVersionDowngrade: boolean,
/**
* **This option should usually be left None**.
* Overrides the default channel used to fetch updates.
* The default channel will be whatever channel was specified on the command line when building this release.
* For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'.
* This allows users to automatically receive updates from the same channel they installed from. This options
* allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel
* without having to reinstall the application.
*/
ExplicitChannel?: string,
/**
* Sets the maximum number of deltas to consider before falling back to a full update.
* The default is 10. Set to a negative number (eg. -1) to disable deltas.
*/
MaximumDeltasBeforeFallback: number,
}

View File

@@ -24,8 +24,4 @@ semver.workspace = true
log.workspace = true
simplelog.workspace = true
lazy_static.workspace = true
neon.workspace = true
[build-dependencies]
velopack = { workspace = true, features = ["typescript"] }
ts-rs.workspace = true
neon.workspace = true

View File

@@ -1,12 +0,0 @@
use std::{env, path::Path};
use locator::VelopackLocatorConfig;
use ts_rs::TS;
use velopack::*;
fn main() {
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let bindings_dir = Path::new(&manifest_dir).join("..").join("src").join("bindings");
UpdateInfo::export_all_to(&bindings_dir).unwrap();
UpdateOptions::export_all_to(&bindings_dir).unwrap();
VelopackLocatorConfig::export_all_to(&bindings_dir).unwrap();
}

View File

@@ -209,12 +209,12 @@ fn js_wait_exit_then_apply_update(mut cx: FunctionContext) -> JsResult<JsUndefin
let arg_silent = cx.argument::<JsBoolean>(2)?.value(&mut cx);
let arg_restart = cx.argument::<JsBoolean>(3)?.value(&mut cx);
let update_info = serde_json::from_str::<UpdateInfo>(&arg_update).or_else(|e| cx.throw_error(e.to_string()))?;
let asset = serde_json::from_str::<VelopackAsset>(&arg_update).or_else(|e| cx.throw_error(e.to_string()))?;
let arg_restart_args = cx.argument::<JsArray>(4)?;
let restart_args = args_array_to_vec_string(&mut cx, arg_restart_args)?;
mgr_ref.wait_exit_then_apply_updates(update_info, arg_silent, arg_restart, restart_args).or_else(|e| cx.throw_error(e.to_string()))?;
mgr_ref.wait_exit_then_apply_updates(asset, arg_silent, arg_restart, restart_args).or_else(|e| cx.throw_error(e.to_string()))?;
Ok(cx.undefined())
}

View File

@@ -10,11 +10,28 @@ use app::VelopackAppWrapper;
mod manager;
use manager::UpdateManagerWrapper;
use ::velopack::VelopackAsset;
#[derive(FromPyObject)]
pub enum PyUpdateInfoOrAsset {
UpdateInfo(PyUpdateInfo),
Asset(PyVelopackAsset),
}
impl PyUpdateInfoOrAsset {
pub fn into_asset(self) -> VelopackAsset {
match self {
PyUpdateInfoOrAsset::UpdateInfo(update_info) => update_info.TargetFullRelease.into(),
PyUpdateInfoOrAsset::Asset(asset) => asset.into(),
}
}
}
#[pymodule]
#[pyo3(name = "velopack")]
fn velopack(m: &Bound<'_, PyModule>) -> PyResult<()> {
pyo3_log::init();
// auto-generated DTO's
m.add_class::<PyVelopackAsset>()?;
m.add_class::<PyUpdateInfo>()?;

View File

@@ -4,9 +4,9 @@ use std::sync::mpsc;
use std::thread;
use velopack::sources::AutoSource;
use velopack::{UpdateCheck, UpdateInfo, UpdateManager as VelopackUpdateManagerRust};
use velopack::{UpdateCheck, UpdateInfo, UpdateManager as VelopackUpdateManagerRust, VelopackAsset};
use crate::types::*;
use crate::{types::*, PyUpdateInfoOrAsset};
#[pyclass(name = "UpdateManager")]
pub struct UpdateManagerWrapper {
@@ -19,13 +19,30 @@ impl UpdateManagerWrapper {
#[pyo3(signature = (source, options = None, locator = None))]
pub fn new(source: String, options: Option<PyUpdateOptions>, locator: Option<PyVelopackLocatorConfig>) -> Result<Self> {
let source = AutoSource::new(&source);
// set myinner to a new VelopackUpdateManager with the source
let inner = VelopackUpdateManagerRust::new(source, options.map(Into::into), locator.map(Into::into))?;
Ok(UpdateManagerWrapper { inner })
}
pub fn check_for_updates(&mut self) -> Result<Option<PyUpdateInfo>> {
let update_check = self.inner.check_for_updates()?;
pub fn get_current_version(&self) -> String {
self.inner.get_current_version_as_string()
}
pub fn get_app_id(&self) -> String {
self.inner.get_app_id().to_string()
}
pub fn get_is_portable(&self) -> bool {
self.inner.get_is_portable()
}
pub fn get_update_pending_restart(&self) -> Option<PyVelopackAsset> {
let pending = self.inner.get_update_pending_restart();
pending.map(Into::into)
}
pub fn check_for_updates(&mut self, py: Python) -> Result<Option<PyUpdateInfo>> {
// Release GIL during network operation
let update_check = py.allow_threads(|| self.inner.check_for_updates())?;
match update_check {
UpdateCheck::UpdateAvailable(updates) => {
let py_updates = PyUpdateInfo::from(updates);
@@ -37,44 +54,80 @@ impl UpdateManagerWrapper {
}
#[pyo3(signature = (update_info, progress_callback = None))]
pub fn download_updates(&mut self, update_info: &PyUpdateInfo, progress_callback: Option<PyObject>) -> Result<()> {
pub fn download_updates(&mut self, py: Python, update_info: PyUpdateInfo, progress_callback: Option<PyObject>) -> Result<()> {
// Convert PyUpdateInfo back to rust UpdateInfo
let rust_update_info: UpdateInfo = update_info.clone().into();
let rust_update_info: UpdateInfo = update_info.into();
if let Some(callback) = progress_callback {
// Create a channel for progress updates
let (sender, receiver) = mpsc::channel::<i16>();
// Spawn a thread to handle progress updates
let progress_thread = thread::spawn(move || {
Python::with_gil(|py| {
// Clone the callback for the thread
let callback_clone = callback.clone_ref(py);
// Release the GIL before starting the download
py.allow_threads(|| {
// Spawn a thread to handle progress updates
let progress_thread = thread::spawn(move || {
while let Ok(progress) = receiver.recv() {
if let Err(e) = callback.call1(py, (progress,)) {
// Log error but continue - don't break the download
eprintln!("Progress callback error: {}", e);
break;
}
// Acquire GIL only when needed to call the callback
Python::with_gil(|py| {
if let Err(e) = callback_clone.call1(py, (progress,)) {
// Log error but continue - don't break the download
eprintln!("Progress callback error: {}", e);
}
});
}
});
});
// Call download with the sender
let result = self.inner.download_updates(&rust_update_info, Some(sender))?;
// Call download with the sender
let result = self.inner.download_updates(&rust_update_info, Some(sender));
// Wait for the progress thread to finish
let _ = progress_thread.join();
Ok(result)
// Wait for the progress thread to finish
let _ = progress_thread.join();
result
})?;
Ok(())
} else {
// No progress callback provided
self.inner.download_updates(&rust_update_info, None)?;
// No progress callback provided - still release GIL for the download
py.allow_threads(|| {
self.inner.download_updates(&rust_update_info, None)
})?;
Ok(())
}
}
pub fn apply_updates_and_restart(&mut self, update_info: &PyUpdateInfo) -> Result<()> {
// Convert PyUpdateInfo back to rust UpdateInfo
let rust_update_info: UpdateInfo = update_info.clone().into();
self.inner.apply_updates_and_restart(&rust_update_info)?;
pub fn apply_updates_and_restart(&mut self, update: PyUpdateInfoOrAsset) -> Result<()> {
let asset: VelopackAsset = update.into_asset();
self.inner.apply_updates_and_restart(&asset)?;
Ok(())
}
pub fn apply_updates_and_restart_with_args(&mut self, update: PyUpdateInfoOrAsset, restart_args: Vec<String>) -> Result<()> {
let asset: VelopackAsset = update.into_asset();
self.inner.apply_updates_and_restart_with_args(&asset, restart_args)?;
Ok(())
}
pub fn apply_updates_and_exit(&mut self, update: PyUpdateInfoOrAsset) -> Result<()> {
let asset: VelopackAsset = update.into_asset();
self.inner.apply_updates_and_exit(&asset)?;
Ok(())
}
#[pyo3(signature = (update, silent = false, restart = true, restart_args = None))]
pub fn wait_exit_then_apply_updates(
&mut self,
update: PyUpdateInfoOrAsset,
silent: bool,
restart: bool,
restart_args: Option<Vec<String>>,
) -> Result<()> {
let asset: VelopackAsset = update.into_asset();
let args = restart_args.unwrap_or_default();
self.inner.wait_exit_then_apply_updates(&asset, silent, restart, args)?;
Ok(())
}
}

69
src/lib-python/uv.lock generated
View File

@@ -38,7 +38,7 @@ resolution-markers = [
"python_full_version >= '3.9'",
]
dependencies = [
{ name = "zipp", version = "3.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
{ name = "zipp", version = "3.23.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
wheels = [
@@ -59,25 +59,25 @@ wheels = [
[[package]]
name = "maturin"
version = "1.8.6"
version = "1.8.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tomli", marker = "python_full_version < '3.11'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/34/bc/c7df50a359c3a31490785c77d1ddd5fc83cc8cc07a4eddd289dbae53545a/maturin-1.8.6.tar.gz", hash = "sha256:0e0dc2e0bfaa2e1bd238e0236cf8a2b7e2250ccaa29c1aa8d0e61fa664b0289d", size = 203320, upload-time = "2025-05-13T13:56:11.033Z" }
sdist = { url = "https://files.pythonhosted.org/packages/5d/67/71098eb8cac6dacfc71b6b81645017811ad377c22c959cff524505cbc8d4/maturin-1.8.7.tar.gz", hash = "sha256:96c76353f94a153c5dc1a9d3916e75fcd17e6bf216a06dcdc2f84b9f98f374af", size = 205116, upload-time = "2025-06-09T13:58:15.09Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d3/f1/e493add40aebdab88ac1aefb41b9c84ac288fb00025bc96b9213ee02c958/maturin-1.8.6-py3-none-linux_armv6l.whl", hash = "sha256:1bf4c743dd2b24448e82b8c96251597818956ddf848e1d16b59356512c7e58d8", size = 7831195, upload-time = "2025-05-13T13:55:42.634Z" },
{ url = "https://files.pythonhosted.org/packages/f0/1c/588afdb7bf79c4f15e33e9af6d7f3b12ec662bc63a22919e3bf39afa2a1e/maturin-1.8.6-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4ea89cf76048bc760e12b36b608fc3f5ef4f7359c0895e9afe737be34041d948", size = 15276471, upload-time = "2025-05-13T13:55:46.196Z" },
{ url = "https://files.pythonhosted.org/packages/62/0b/4ce97f7f3a42068fbb42ba47d8b072e098c060fc1f64d8523e5588c57543/maturin-1.8.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4dd2e2f005ca63ac7ef0dddf2d65324ee480277a11544dcc4e7e436af68034dd", size = 7966129, upload-time = "2025-05-13T13:55:48.633Z" },
{ url = "https://files.pythonhosted.org/packages/84/bf/4eae9d12920c580baf9d47ee63fec3ae0233e90a3aa8987bd7909cdc36a0/maturin-1.8.6-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:b0637604774e2c50ab48a0e9023fe2f071837ecbc817c04ec28e1cfcc25224c2", size = 7835935, upload-time = "2025-05-13T13:55:51.235Z" },
{ url = "https://files.pythonhosted.org/packages/f9/aa/8090f8b3f5f7ec46bc95deb0f5b29bf52c98156ef594f2e65d20bf94cea1/maturin-1.8.6-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:bec5948c6475954c8089b17fae349966258756bb2ca05e54099e476a08070795", size = 8282553, upload-time = "2025-05-13T13:55:53.266Z" },
{ url = "https://files.pythonhosted.org/packages/bc/50/4348da6cc16c006dab4e6cd479cf00dc0afa80db289a115a314df9909ee6/maturin-1.8.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:62a65f70ebaadd6eb6083f5548413744f2ef12400445778e08d41d4facf15bbe", size = 7618339, upload-time = "2025-05-13T13:55:55.706Z" },
{ url = "https://files.pythonhosted.org/packages/ce/77/16458e29487d068c8cdb7f06a4403393568a10b44993fe2ec9c3b29fdccd/maturin-1.8.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:5c0ff7ad43883920032b63c94c76dcdd5758710d7b72b68db69e7826c40534ac", size = 7686748, upload-time = "2025-05-13T13:55:57.97Z" },
{ url = "https://files.pythonhosted.org/packages/0c/c1/a52f3c1171c053810606c7c7fae5ce4637446ef9df44f281862d2bef3750/maturin-1.8.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:ca30fdb158a24cf312f3e53072a6e987182c103fa613efea2d28b5f52707d04a", size = 9767394, upload-time = "2025-05-13T13:56:00.694Z" },
{ url = "https://files.pythonhosted.org/packages/e2/ab/abae74f36a0f200384eda985ebeb9ee5dcbb19bfe1558c3335ef6f297094/maturin-1.8.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b1e786ec9b5f7315c7e3fcc62b0715f9d99ffe477b06d0d62655a71e6a51a67b", size = 10995574, upload-time = "2025-05-13T13:56:03.101Z" },
{ url = "https://files.pythonhosted.org/packages/8e/1c/c478578a62c1e34b5b0641a474de78cb56d6c4aad0ba88f90dfa9f2a15f7/maturin-1.8.6-py3-none-win32.whl", hash = "sha256:dade5edfaf508439ff6bbc7be4f207e04c0999c47d9ef7e1bae16258e76b1518", size = 7027171, upload-time = "2025-05-13T13:56:05.472Z" },
{ url = "https://files.pythonhosted.org/packages/c9/89/2c57d29f25e06696cb3c5b3770ba0b40dfe87f91a879ecbcdc92e071a26b/maturin-1.8.6-py3-none-win_amd64.whl", hash = "sha256:6bc9281b90cd37e2a7985f2e5d6e3d35a1d64cf6e4d04ce5ed25603d162995b9", size = 7947998, upload-time = "2025-05-13T13:56:07.428Z" },
{ url = "https://files.pythonhosted.org/packages/9d/f5/3ee1c6aa4e277323bef38ea0ec07262a9b88711d1a29cb5bb08ce3807a6f/maturin-1.8.6-py3-none-win_arm64.whl", hash = "sha256:24f66624db69b895b134a8f1592efdf04cd223c9b3b65243ad32080477936d14", size = 6684974, upload-time = "2025-05-13T13:56:09.415Z" },
{ url = "https://files.pythonhosted.org/packages/da/58/fcd05985cbd9edd0232146a768e5fdcbda1f954ea82ec1c81f4d75d275c2/maturin-1.8.7-py3-none-linux_armv6l.whl", hash = "sha256:43526cc7fdc025b0d134b09d2cdbbe8fe816c4d72351822fa967d36784764bab", size = 8097702, upload-time = "2025-06-09T13:57:49.497Z" },
{ url = "https://files.pythonhosted.org/packages/7e/a5/f02d9661fe21321f6852ef740eda54fe257e91c5c906265db902d1989bc8/maturin-1.8.7-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5e134fc67e7f28e9f57d01dc2603c243456f80e76f93ef54ee61a4403dccd7e3", size = 16059837, upload-time = "2025-06-09T13:57:52.713Z" },
{ url = "https://files.pythonhosted.org/packages/c5/bc/51c9ce286909831bee0c5a64d769355cd8a84f373e22f7d7d07c78a829ec/maturin-1.8.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b560b86d6119c82430f9682f76708b3ea4984e5976afab6b844c9c8094709f78", size = 8381884, upload-time = "2025-06-09T13:57:55.275Z" },
{ url = "https://files.pythonhosted.org/packages/4f/ed/a5d84adbd0df3bb1b1e0f75b13a89446455f66d0070562f29ac30db5054a/maturin-1.8.7-py3-none-manylinux_2_12_i686.manylinux2010_i686.musllinux_1_1_i686.whl", hash = "sha256:272f34df99ff9be27174b0d2afaec98bac5217670bceddd796f45a0095849dd9", size = 8141538, upload-time = "2025-06-09T13:57:56.941Z" },
{ url = "https://files.pythonhosted.org/packages/fd/f8/ef902fb50454fd57a514b6bc1203f55b8c5dd5b0c5439fabe22aca0ef60e/maturin-1.8.7-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:c39f288b72ceae9274e612131c8a1a18bc248170910e27eb39956ffbd62bd712", size = 8795313, upload-time = "2025-06-09T13:57:58.99Z" },
{ url = "https://files.pythonhosted.org/packages/d6/13/9cfb7d799ceda322651be21af7d2b541eb125477db30a8dba408ec4a9813/maturin-1.8.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:8766377b5339b354fc83195ee1d9879db2b1323ea485305c6932f97b1661334d", size = 7899633, upload-time = "2025-06-09T13:58:00.868Z" },
{ url = "https://files.pythonhosted.org/packages/c4/d2/46c98e5aef3011efa56dd0fc0ffc9bba4fef236339352af5876f0027af03/maturin-1.8.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:987b4e821c5ec2b5c6d75f4fcb6141d6418188356c3ff229c67f58c11ae54ded", size = 7960920, upload-time = "2025-06-09T13:58:02.826Z" },
{ url = "https://files.pythonhosted.org/packages/e4/b7/ec2dde6c5aefcf38279c6276a96f9351081f22174e9db56db394d0f32e67/maturin-1.8.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.musllinux_1_1_ppc64le.whl", hash = "sha256:15ec5919b334e421e97623446907a3f994fc04427ab2c9e5eab5a461565e6ce3", size = 10155481, upload-time = "2025-06-09T13:58:04.761Z" },
{ url = "https://files.pythonhosted.org/packages/35/38/ef38d4a7008068212f8d11643178e8b0291d7cdd047b47f449243be5e5cd/maturin-1.8.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec37762228b76d4763e0ad18f7d70d8dbe52298ecdb0737bb4fd383a49fc2f06", size = 8995379, upload-time = "2025-06-09T13:58:07.152Z" },
{ url = "https://files.pythonhosted.org/packages/29/79/f1b64d1d0e11ca4eb24a8b82d0916bc0e3e477b8a6879e9a6bee93b731f3/maturin-1.8.7-py3-none-win32.whl", hash = "sha256:834c2f8029c1e19e272b360102eead74fdb6df93d1cb6e645d6aeaec86b532f6", size = 7261393, upload-time = "2025-06-09T13:58:09.466Z" },
{ url = "https://files.pythonhosted.org/packages/53/1e/efc95e4cab8fbbede2f8d28481bae44eee4a43cfe456e78854ef9952ec6b/maturin-1.8.7-py3-none-win_amd64.whl", hash = "sha256:ef44ade7b2401ebbd4b0d268e4b953b4256295c827a21e806a51d29f629ab638", size = 8300461, upload-time = "2025-06-09T13:58:11.545Z" },
{ url = "https://files.pythonhosted.org/packages/17/2b/270b772f31844aae4297f178e81d5890c259973dedc68a48084a36c34ec1/maturin-1.8.7-py3-none-win_arm64.whl", hash = "sha256:20813b2262661a403fc0c695e3d4836257f992927fa2234928eb3510b13de2cd", size = 6973691, upload-time = "2025-06-09T13:58:13.223Z" },
]
[[package]]
@@ -100,7 +100,7 @@ wheels = [
[[package]]
name = "pyinstaller"
version = "6.13.0"
version = "6.14.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "altgraph" },
@@ -114,24 +114,24 @@ dependencies = [
{ name = "setuptools", version = "75.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
{ name = "setuptools", version = "80.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/b1/2949fe6d3874e961898ca5cfc1bf2cf13bdeea488b302e74a745bc28c8ba/pyinstaller-6.13.0.tar.gz", hash = "sha256:38911feec2c5e215e5159a7e66fdb12400168bd116143b54a8a7a37f08733456", size = 4276427, upload-time = "2025-04-15T23:25:31.646Z" }
sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/d66d3a9c34349d73eb099401060e2591da8ccc5ed427e54fff3961302513/pyinstaller-6.14.1.tar.gz", hash = "sha256:35d5c06a668e21f0122178dbf20e40fd21012dc8f6170042af6050c4e7b3edca", size = 4284317, upload-time = "2025-06-08T18:45:46.367Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/02/d1a347d35b1b627da1e148159e617576555619ac3bb8bbd5fed661fc7bb5/pyinstaller-6.13.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:aa404f0b02cd57948098055e76ee190b8e65ccf7a2a3f048e5000f668317069f", size = 1001923, upload-time = "2025-04-15T23:24:17.646Z" },
{ url = "https://files.pythonhosted.org/packages/6b/80/6da39f7aeac65c9ca5afad0fac37887d75fdfd480178a7077c9d30b0704c/pyinstaller-6.13.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:92efcf2f09e78f07b568c5cb7ed48c9940f5dad627af4b49bede6320fab2a06e", size = 718135, upload-time = "2025-04-15T23:24:22.385Z" },
{ url = "https://files.pythonhosted.org/packages/05/2c/d21d31f780a489609e7bf6385c0f7635238dc98b37cba8645b53322b7450/pyinstaller-6.13.0-py3-none-manylinux2014_i686.whl", hash = "sha256:9f82f113c463f012faa0e323d952ca30a6f922685d9636e754bd3a256c7ed200", size = 728543, upload-time = "2025-04-15T23:24:27.02Z" },
{ url = "https://files.pythonhosted.org/packages/e1/20/e6ca87bbed6c0163533195707f820f05e10b8da1223fc6972cfe3c3c50c7/pyinstaller-6.13.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:db0e7945ebe276f604eb7c36e536479556ab32853412095e19172a5ec8fca1c5", size = 726868, upload-time = "2025-04-15T23:24:31.779Z" },
{ url = "https://files.pythonhosted.org/packages/20/d5/53b19285f8817ab6c4b07c570208d62606bab0e5a049d50c93710a1d9dc6/pyinstaller-6.13.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:92fe7337c5aa08d42b38d7a79614492cb571489f2cb0a8f91dc9ef9ccbe01ed3", size = 725037, upload-time = "2025-04-15T23:24:36.244Z" },
{ url = "https://files.pythonhosted.org/packages/84/5b/08e0b305ba71e6d7cb247e27d714da7536895b0283132d74d249bf662366/pyinstaller-6.13.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:bc09795f5954135dd4486c1535650958c8218acb954f43860e4b05fb515a21c0", size = 721027, upload-time = "2025-04-15T23:24:40.16Z" },
{ url = "https://files.pythonhosted.org/packages/1f/9c/d8d0a7120103471be8dbe1c5419542aa794b9b9ec2ef628b542f9e6f9ef0/pyinstaller-6.13.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:589937548d34978c568cfdc39f31cf386f45202bc27fdb8facb989c79dfb4c02", size = 723443, upload-time = "2025-04-15T23:24:50.63Z" },
{ url = "https://files.pythonhosted.org/packages/52/c7/8a9d81569dda2352068ecc6ee779d5feff6729569dd1b4ffd1236ecd38fe/pyinstaller-6.13.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:b7260832f7501ba1d2ce1834d4cddc0f2b94315282bc89c59333433715015447", size = 719915, upload-time = "2025-04-15T23:24:54.917Z" },
{ url = "https://files.pythonhosted.org/packages/d5/e6/cccadb02b90198c7ed4ffb8bc34d420efb72b996f47cbd4738067a602d65/pyinstaller-6.13.0-py3-none-win32.whl", hash = "sha256:80c568848529635aa7ca46d8d525f68486d53e03f68b7bb5eba2c88d742e302c", size = 1294997, upload-time = "2025-04-15T23:25:01.391Z" },
{ url = "https://files.pythonhosted.org/packages/1a/06/15cbe0e25d1e73d5b981fa41ff0bb02b15e924e30b8c61256f4a28c4c837/pyinstaller-6.13.0-py3-none-win_amd64.whl", hash = "sha256:8d4296236b85aae570379488c2da833b28828b17c57c2cc21fccd7e3811fe372", size = 1352714, upload-time = "2025-04-15T23:25:08.061Z" },
{ url = "https://files.pythonhosted.org/packages/83/ef/74379298d46e7caa6aa7ceccc865106d3d4b15ac487ffdda2a35bfb6fe79/pyinstaller-6.13.0-py3-none-win_arm64.whl", hash = "sha256:d9f21d56ca2443aa6a1e255e7ad285c76453893a454105abe1b4d45e92bb9a20", size = 1293589, upload-time = "2025-04-15T23:25:14.523Z" },
{ url = "https://files.pythonhosted.org/packages/43/f6/fa56e547fe849db4b8da0acaad6101a6382c18370c7e0f378a1cf0ea89f0/pyinstaller-6.14.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:da559cfe4f7a20a7ebdafdf12ea2a03ea94d3caa49736ef53ee2c155d78422c9", size = 999937, upload-time = "2025-06-08T18:44:26.429Z" },
{ url = "https://files.pythonhosted.org/packages/af/a6/a2814978f47ae038b1ce112717adbdcfd8dfb9504e5c52437902331cde1a/pyinstaller-6.14.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:f040d1e3d42af3730104078d10d4a8ca3350bd1c78de48f12e1b26f761e0cbc3", size = 719569, upload-time = "2025-06-08T18:44:30.948Z" },
{ url = "https://files.pythonhosted.org/packages/35/f0/86391a4c0f558aef43a7dac8f678d46f4e5b84bd133308e3ea81f7384ab9/pyinstaller-6.14.1-py3-none-manylinux2014_i686.whl", hash = "sha256:7b8813fb2d5a82ef4ceffc342ed9a11a6fc1ef21e68e833dbd8fedb8a188d3f5", size = 729824, upload-time = "2025-06-08T18:44:34.983Z" },
{ url = "https://files.pythonhosted.org/packages/e5/88/446814e335d937406e6e1ae4a77ed922b8eea8b90f3aaf69427a16b58ed2/pyinstaller-6.14.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e2cfdbc6dd41d19872054fc233da18856ec422a7fdea899b6985ae04f980376a", size = 727937, upload-time = "2025-06-08T18:44:38.954Z" },
{ url = "https://files.pythonhosted.org/packages/c6/0f/5aa891c61d303ad4a794b7e2f864aacf64fe0f6f5559e2aec0f742595fad/pyinstaller-6.14.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:a4d53b3ecb5786b097b79bda88c4089186fc1498ef7eaa6cee57599ae459241e", size = 724762, upload-time = "2025-06-08T18:44:42.768Z" },
{ url = "https://files.pythonhosted.org/packages/c5/92/e32ec0a1754852a8ed5a60f6746c6483e3da68aee97d314f3a3a99e0ed9e/pyinstaller-6.14.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c48dd257f77f61ebea2d1fdbaf11243730f2271873c88d3b5ecb7869525d3bcb", size = 724957, upload-time = "2025-06-08T18:44:46.829Z" },
{ url = "https://files.pythonhosted.org/packages/c3/66/1260f384e47bf939f6238f791d4cda7edb94771d2fa0a451e0edb21ac9c7/pyinstaller-6.14.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5b05cbb2ffc033b4681268159b82bac94b875475c339603c7e605f00a73c8746", size = 724132, upload-time = "2025-06-08T18:44:51.081Z" },
{ url = "https://files.pythonhosted.org/packages/d2/8b/8570ab94ec07e0b2b1203f45840353ee76aa067a2540c97da43d43477b26/pyinstaller-6.14.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:d5fd73757c8ea9adb2f9c1f81656335ba9890029ede3031835d768fde36e89f0", size = 723847, upload-time = "2025-06-08T18:44:54.896Z" },
{ url = "https://files.pythonhosted.org/packages/d5/43/6c68dc9e53b09ff948d6e46477932b387832bbb920c48061d734ef089368/pyinstaller-6.14.1-py3-none-win32.whl", hash = "sha256:547f7a93592e408cbfd093ce9fd9631215387dab0dbf3130351d3b0b1186a534", size = 1299744, upload-time = "2025-06-08T18:45:00.781Z" },
{ url = "https://files.pythonhosted.org/packages/7c/dd/bb8d5bcb0592f7f5d454ad308051d00ed34f8b08d5003400b825cfe35513/pyinstaller-6.14.1-py3-none-win_amd64.whl", hash = "sha256:0794290b4b56ef9d35858334deb29f36ec1e1f193b0f825212a0aa5a1bec5a2f", size = 1357625, upload-time = "2025-06-08T18:45:06.826Z" },
{ url = "https://files.pythonhosted.org/packages/89/57/8a8979737980e50aa5031b77318ce783759bf25be2956317f2e1d7a65a09/pyinstaller-6.14.1-py3-none-win_arm64.whl", hash = "sha256:d9d99695827f892cb19644106da30681363e8ff27b8326ac8416d62890ab9c74", size = 1298607, upload-time = "2025-06-08T18:45:12.766Z" },
]
[[package]]
name = "pyinstaller-hooks-contrib"
version = "2025.4"
version = "2025.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "importlib-metadata", version = "8.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
@@ -140,9 +140,9 @@ dependencies = [
{ name = "setuptools", version = "75.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" },
{ name = "setuptools", version = "80.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e3/94/dfc5c7903306211798f990e6794c2eb7b8685ac487b26979e9255790419c/pyinstaller_hooks_contrib-2025.4.tar.gz", hash = "sha256:5ce1afd1997b03e70f546207031cfdf2782030aabacc102190677059e2856446", size = 162628, upload-time = "2025-05-03T20:15:55.983Z" }
sdist = { url = "https://files.pythonhosted.org/packages/5f/ff/e3376595935d5f8135964d2177cd3e3e0c1b5a6237497d9775237c247a5d/pyinstaller_hooks_contrib-2025.5.tar.gz", hash = "sha256:707386770b8fe066c04aad18a71bc483c7b25e18b4750a756999f7da2ab31982", size = 163124, upload-time = "2025-06-08T18:47:53.26Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d3/e1/ed48c7074145898e5c5b0072e87be975c5bd6a1d0f08c27a1daa7064fca0/pyinstaller_hooks_contrib-2025.4-py3-none-any.whl", hash = "sha256:6c2d73269b4c484eb40051fc1acee0beb113c2cfb3b37437b8394faae6f0d072", size = 434451, upload-time = "2025-05-03T20:15:54.579Z" },
{ url = "https://files.pythonhosted.org/packages/0c/2c/b4d317534e17dd1df95c394d4b37febb15ead006a1c07c2bb006481fb5e7/pyinstaller_hooks_contrib-2025.5-py3-none-any.whl", hash = "sha256:ebfae1ba341cb0002fb2770fad0edf2b3e913c2728d92df7ad562260988ca373", size = 437246, upload-time = "2025-06-08T18:47:51.516Z" },
]
[[package]]
@@ -219,6 +219,7 @@ wheels = [
[[package]]
name = "velopack"
version = "0.0.1"
source = { editable = "." }
[package.dev-dependencies]
@@ -253,12 +254,12 @@ wheels = [
[[package]]
name = "zipp"
version = "3.22.0"
version = "3.23.0"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.9'",
]
sdist = { url = "https://files.pythonhosted.org/packages/12/b6/7b3d16792fdf94f146bed92be90b4eb4563569eca91513c8609aebf0c167/zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", size = 25257, upload-time = "2025-05-26T14:46:32.217Z" }
sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ad/da/f64669af4cae46f17b90798a827519ce3737d31dbafad65d391e49643dc4/zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343", size = 9796, upload-time = "2025-05-26T14:46:30.775Z" },
{ url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
]

View File

@@ -16,7 +16,6 @@ rust-version.workspace = true
[features]
default = []
async = ["async-std"]
typescript = ["ts-rs"]
file-logging = ["log-panics", "simplelog", "time"]
[package.metadata.docs.rs]
@@ -50,9 +49,6 @@ sha1.workspace = true
sha2.workspace = true
uuid.workspace = true
# typescript
ts-rs = { workspace = true, optional = true }
# async
async-std = { workspace = true, optional = true }

View File

@@ -51,7 +51,7 @@ pub fn load_bundle_from_file<'a, P: AsRef<Path>>(file_name: P) -> Result<BundleZ
})
}
pub fn load_bundle_from_memory(zip_range: &[u8]) -> Result<BundleZip, Error> {
pub fn load_bundle_from_memory<'a>(zip_range: &'a [u8]) -> Result<BundleZip<'a>, Error> {
info!("Loading bundle from embedded zip...");
let cursor: Box<dyn ReadSeek> = Box::new(Cursor::new(zip_range));
let zip = ZipArchive::new(cursor)?;

View File

@@ -59,7 +59,6 @@ impl ShortcutLocationFlags {
/// VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth).
#[allow(non_snake_case)]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
pub struct VelopackLocatorConfig {
/// The root directory of the current app.
pub RootAppDir: PathBuf,

View File

@@ -52,7 +52,6 @@ impl VelopackAssetFeed {
/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed.
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(default)]
pub struct VelopackAsset {
/// The name or Id of the package containing this release.
@@ -78,7 +77,6 @@ pub struct VelopackAsset {
/// Holds information about the current version and pending updates, such as how many there are, and access to release notes.
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(default)]
pub struct UpdateInfo {
/// The available version that we are updating to.
@@ -118,7 +116,6 @@ impl AsRef<VelopackAsset> for VelopackAsset {
/// Options to customise the behaviour of UpdateManager.
#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
#[serde(default)]
pub struct UpdateOptions {
/// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading).

View File

@@ -1,4 +1,5 @@
using Azure;
using System.Text;
using Azure;
using Azure.Storage;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
@@ -19,6 +20,8 @@ public class AzureDownloadOptions : RepositoryOptions, IObjectDownloadOptions
public string Container { get; set; }
public string SasToken { get; set; }
public string Prefix { get; set; }
}
public class AzureUploadOptions : AzureDownloadOptions, IObjectUploadOptions
@@ -26,13 +29,26 @@ public class AzureUploadOptions : AzureDownloadOptions, IObjectUploadOptions
public int KeepMaxReleases { get; set; }
}
public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploadOptions, BlobContainerClient>
public class AzureBlobClient(BlobContainerClient client, string prefix)
{
public virtual Task DeleteBlobIfExistsAsync(string key, CancellationToken cancellationToken = default)
{
return client.DeleteBlobIfExistsAsync(prefix + key, cancellationToken: cancellationToken);
}
public virtual BlobClient GetBlobClient(string key)
{
return client.GetBlobClient(prefix + key);
}
}
public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploadOptions, AzureBlobClient>
{
public AzureRepository(ILogger logger) : base(logger)
{
}
protected override BlobContainerClient CreateClient(AzureDownloadOptions options)
protected override AzureBlobClient CreateClient(AzureDownloadOptions options)
{
var serviceUrl = options.Endpoint ?? "https://" + options.Account + ".blob.core.windows.net";
if (options.Endpoint == null) {
@@ -51,10 +67,21 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
client = new BlobServiceClient(new Uri(serviceUrl), new StorageSharedKeyCredential(options.Account, options.Key), clientOptions);
}
return client.GetBlobContainerClient(options.Container);
var containerClient = client.GetBlobContainerClient(options.Container);
var prefix = options.Prefix?.Trim();
if (prefix == null) {
prefix = "";
}
if (!string.IsNullOrEmpty(prefix) && !prefix.EndsWith("/")) {
prefix += "/";
}
return new AzureBlobClient(containerClient, prefix);
}
protected override async Task DeleteObject(BlobContainerClient client, string key)
protected override async Task DeleteObject(AzureBlobClient client, string key)
{
await RetryAsync(
async () => {
@@ -63,7 +90,7 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
"Deleting " + key);
}
protected override async Task<byte[]> GetObjectBytes(BlobContainerClient client, string key)
protected override async Task<byte[]> GetObjectBytes(AzureBlobClient client, string key)
{
return await RetryAsyncRet(
async () => {
@@ -79,6 +106,17 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
$"Downloading {key}...");
}
protected override async Task<VelopackAssetFeed> GetReleasesAsync(AzureDownloadOptions options)
{
var releasesName = CoreUtil.GetVeloReleaseIndexName(options.Channel);
var client = CreateClient(options);
var bytes = await GetObjectBytes(client, releasesName);
if (bytes == null || bytes.Length == 0) {
return new VelopackAssetFeed();
}
return VelopackAssetFeed.FromJson(Encoding.UTF8.GetString(bytes));
}
protected override async Task SaveEntryToFileAsync(AzureDownloadOptions options, VelopackAsset entry, string filePath)
{
await RetryAsync(
@@ -90,7 +128,8 @@ public class AzureRepository : ObjectRepository<AzureDownloadOptions, AzureUploa
$"Downloading {entry.FileName}...");
}
protected override async Task UploadObject(BlobContainerClient client, string key, FileInfo f, bool overwriteRemote, bool noCache)
protected override async Task UploadObject(AzureBlobClient client, string key, FileInfo f, bool overwriteRemote, bool noCache)
{
var blobClient = client.GetBlobClient(key);
try {

View File

@@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="4.0.1.3" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.24.0" />
<PackageReference Include="Gitea.Net.API" Version="25.3.5" />
<PackageReference Include="Gitea.Net.API" Version="25.8.18" />
<PackageReference Include="Octokit" Version="14.0.0" />
<PackageReference Include="RestSharp" Version="112.1.0" />
</ItemGroup>

View File

@@ -11,7 +11,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.10" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.11" />
</ItemGroup>
</Project>

View File

@@ -34,6 +34,10 @@ public class LinuxPackCommandRunner : PackageBuilder<LinuxPackOptions>
File.Copy(icon, Path.Combine(dir.FullName, iconFilename), true);
File.Copy(icon, Path.Combine(dir.FullName, ".DirIcon"), true);
string iconDirPath = Path.Combine(dir.FullName, "usr", "share", "icons", "hicolor", "scalable", "apps");
Directory.CreateDirectory(iconDirPath);
File.Copy(icon, Path.Combine(iconDirPath, iconFilename), true);
var categories = String.IsNullOrWhiteSpace(Options.Categories)
? "Utility"
: Options.Categories.TrimEnd(';');

View File

@@ -46,7 +46,6 @@ public class OsxPackCommandRunner : PackageBuilder<OsxPackOptions>
var structure = new OsxStructureBuilder(dir.FullName);
var macosdir = structure.MacosDirectory;
File.WriteAllText(Path.Combine(macosdir, "sq.version"), GenerateNuspecContent());
File.Copy(HelperFile.GetUpdatePath(Options.TargetRuntime, Log), Path.Combine(macosdir, "UpdateMac"), true);
foreach (var f in Directory.GetFiles(macosdir, "*", SearchOption.AllDirectories)) {
@@ -56,6 +55,12 @@ public class OsxPackCommandRunner : PackageBuilder<OsxPackOptions>
}
}
// Files in the MacOS directory need to be signed, but text files are signed via xattrs, which we don't yet preserve
// in nupkg releases. Instead we can put it in the Resources dir and symlink to it. Symlinks don't need to be signed.
var resourcesdir = structure.ResourcesDirectory;
File.WriteAllText(Path.Combine(resourcesdir, "sq.version"), GenerateNuspecContent());
SymbolicLink.Create(Path.Combine(macosdir, "sq.version"), Path.Combine(resourcesdir, "sq.version"), false, true);
progress(100);
return Task.FromResult(dir.FullName);
}
@@ -96,9 +101,6 @@ public class OsxPackCommandRunner : PackageBuilder<OsxPackOptions>
var structure = new OsxStructureBuilder(packDir);
var updateMacPath = Path.Combine(structure.MacosDirectory, "UpdateMac");
helper.CodeSign(Options.SignAppIdentity, entitlements, updateMacPath, false, keychainPath);
signProgress(25);
var versionPath = Path.Combine(structure.MacosDirectory, "sq.version");
helper.CodeSign(Options.SignAppIdentity, entitlements, versionPath, false, keychainPath);
signProgress(50);
Log.Info("Code signing application bundle...");

View File

@@ -12,31 +12,37 @@ public class AzureBaseCommand : OutputCommand
public string SasToken { get; private set; }
public string Prefix { get; private set; }
public double Timeout { get; private set; }
protected AzureBaseCommand(string name, string description)
: base(name, description)
{
AddOption<string>((v) => Account = v, "--account")
.SetDescription("Account name")
.SetDescription("Account name.")
.SetArgumentHelpName("ACCOUNT")
.SetRequired();
var key = AddOption<string>((v) => Key = v, "--key")
.SetDescription("Account secret key")
.SetDescription("Account secret key.")
.SetArgumentHelpName("KEY");
var sas = AddOption<string>((v) => SasToken = v, "--sas")
.SetDescription("Shared access signature token (not the url)")
.SetDescription("Shared access signature token (not the url).")
.SetArgumentHelpName("TOKEN");
AddOption<string>((v) => Container = v, "--container")
.SetDescription("Azure container name")
.SetDescription("Azure storage container name.")
.SetArgumentHelpName("NAME")
.SetRequired();
AddOption<string>((v) => Prefix = v, "--prefix")
.SetDescription("Optional blob filename path prefix.")
.SetArgumentHelpName("PREFIX");
AddOption<Uri>((v) => Endpoint = v.ToAbsoluteOrNull(), "--endpoint")
.SetDescription("Service url (eg. https://<account-name>.blob.core.windows.net)")
.SetDescription("Service url (eg. https://<account-name>.blob.core.windows.net).")
.SetArgumentHelpName("URL")
.MustBeValidHttpUri();

View File

@@ -55,7 +55,7 @@ public class S3BaseCommand : OutputCommand
.SetRequired();
AddOption<string>((v) => Prefix = v, "--prefix")
.SetDescription("Prefix to the S3 url.")
.SetDescription("Optional filename path prefix.")
.SetArgumentHelpName("PREFIX");
AddOption<bool>((v) => DisablePathStyle = v, "--disablePathStyle")

View File

@@ -6,6 +6,11 @@ namespace Velopack.CommandLine.Tests.Commands;
public abstract class AzureCommandTests<T> : BaseCommandTests<T>
where T : AzureBaseCommand, new()
{
protected override string GetRequiredDefaultOptions()
{
return "--account \"test-account\" --key \"test-key\" --container \"test-container\" ";
}
[Fact]
public void Command_WithRequiredEndpointOptions_ParsesValue()
{
@@ -23,20 +28,37 @@ public abstract class AzureCommandTests<T> : BaseCommandTests<T>
}
public class AzureDownloadCommandTests : AzureCommandTests<AzureDownloadCommand>
{ }
{
[Fact]
public void Folder_WithPath_ParsesValue()
{
var command = new AzureDownloadCommand();
string cli = GetRequiredDefaultOptions() + " --prefix \"releases/v1\"";
ParseResult parseResult = command.ParseAndApply(cli);
Assert.Equal("releases/v1", command.Prefix);
}
}
public class AzureUploadCommandTests : AzureCommandTests<AzureUploadCommand>
{
public override bool ShouldBeNonEmptyReleaseDir => true;
protected override string GetRequiredDefaultOptions()
{
return base.GetRequiredDefaultOptions() + "--releaseDir \"./releases\" ";
}
//[Fact]
//public void KeepMaxReleases_WithNumber_ParsesValue()
//{
// var command = new S3UploadCommand();
[Fact]
public void Folder_WithPath_ParsesValue()
{
var command = new AzureUploadCommand();
// string cli = GetRequiredDefaultOptions() + "--keepMaxReleases 42";
// ParseResult parseResult = command.ParseAndApply(cli);
string cli = GetRequiredDefaultOptions() + " --prefix \"releases/v1\"";
ParseResult parseResult = command.ParseAndApply(cli);
Assert.Equal("releases/v1", command.Prefix);
}
// Assert.Equal(42, command.KeepMaxReleases);
//}
}

View File

@@ -71,7 +71,7 @@ public class DeploymentTests
};
var updateUrl = $"https://{AZ_ENDPOINT}/{AZ_CONTAINER}";
await Deploy<AzureRepository, AzureDownloadOptions, AzureUploadOptions, BlobContainerClient>("AZTestApp", repo, options, releaseDir, updateUrl, logger);
await Deploy<AzureRepository, AzureDownloadOptions, AzureUploadOptions, AzureBlobClient>("AZTestApp", repo, options, releaseDir, updateUrl, logger);
}
static SemanticVersion GenerateSemverFromDateTime()

View File

@@ -1,8 +1,8 @@
using System.ComponentModel;
using System.IO.Compression;
using NCode.ReparsePoints;
using System.IO.Compression;
using System.Runtime.InteropServices;
using Velopack.Logging;
using Velopack.Util;
using NCode.ReparsePoints;
namespace Velopack.Tests;
@@ -270,4 +270,356 @@ public class SymbolicLinkTests
Assert.True(File.Exists(currentSym));
Assert.Equal("A/", File.ReadAllText(currentSym));
}
// ===== New comprehensive tests =====
[Fact]
public void Create_SymlinkToNonExistentTarget_ShouldWork()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target = Path.Combine(tempFolder, "NonExistent");
var link = Path.Combine(tempFolder, "Link");
// Should be able to create symlink to non-existent target
File.WriteAllText(target, "test");
SymbolicLink.Create(link, target);
Assert.True(SymbolicLink.Exists(link));
Assert.Equal(target, SymbolicLink.GetTarget(link));
// Clean up
SymbolicLink.Delete(link);
}
[Fact]
public void Create_MultipleLevelsOfSymlinks()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var file = Path.Combine(tempFolder, "Original.txt");
var link1 = Path.Combine(tempFolder, "Link1.txt");
var link2 = Path.Combine(tempFolder, "Link2.txt");
var link3 = Path.Combine(tempFolder, "Link3.txt");
File.WriteAllText(file, "Hello");
// Create chain: link3 -> link2 -> link1 -> file
SymbolicLink.Create(link1, file);
SymbolicLink.Create(link2, link1);
SymbolicLink.Create(link3, link2);
// All should resolve to the same content
Assert.Equal("Hello", File.ReadAllText(link1));
Assert.Equal("Hello", File.ReadAllText(link2));
Assert.Equal("Hello", File.ReadAllText(link3));
// Each should point to their immediate target
Assert.Equal(file, SymbolicLink.GetTarget(link1));
Assert.Equal(link1, SymbolicLink.GetTarget(link2));
Assert.Equal(link2, SymbolicLink.GetTarget(link3));
}
[Fact]
public void GetTarget_WithTrailingSlash_ShouldWork()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target = Path.Combine(tempFolder, "Target");
var link = Path.Combine(tempFolder, "Link");
Directory.CreateDirectory(target);
SymbolicLink.Create(link, target);
// Should work with and without trailing slash
Assert.Equal(target, SymbolicLink.GetTarget(link));
Assert.Equal(target, SymbolicLink.GetTarget(link + Path.DirectorySeparatorChar));
}
[Fact]
public void Create_WithSpecialCharactersInPath()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target = Path.Combine(tempFolder, "Target With Spaces & Special-Chars");
var link = Path.Combine(tempFolder, "Link With Spaces & Special-Chars");
Directory.CreateDirectory(target);
File.WriteAllText(Path.Combine(target, "file.txt"), "content");
SymbolicLink.Create(link, target);
Assert.True(SymbolicLink.Exists(link));
Assert.Equal(target, SymbolicLink.GetTarget(link));
Assert.Equal("content", File.ReadAllText(Path.Combine(link, "file.txt")));
}
[Fact]
public void Create_AbsoluteVsRelativeComparison()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var subdir = Directory.CreateDirectory(Path.Combine(tempFolder, "subdir")).FullName;
var target = Path.Combine(tempFolder, "target.txt");
var linkAbs = Path.Combine(subdir, "link_abs.txt");
var linkRel = Path.Combine(subdir, "link_rel.txt");
File.WriteAllText(target, "test");
// Create absolute and relative links
SymbolicLink.Create(linkAbs, target, relative: false);
SymbolicLink.Create(linkRel, target, relative: true);
// Both should work
Assert.Equal("test", File.ReadAllText(linkAbs));
Assert.Equal("test", File.ReadAllText(linkRel));
// Check targets
Assert.Equal(target, SymbolicLink.GetTarget(linkAbs));
Assert.Equal(target, SymbolicLink.GetTarget(linkRel));
// Relative target should be different when requested
var relTarget = SymbolicLink.GetTarget(linkRel, relative: true);
Assert.Contains("..", relTarget);
}
[Fact]
public void FileSymlink_AllImplementationsAgree()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target = Path.Combine(tempFolder, "target.txt");
var link = Path.Combine(tempFolder, "link.txt");
File.WriteAllText(target, "test content");
// Create with our implementation
SymbolicLink.Create(link, target);
// Verify all implementations agree
var ourTarget = SymbolicLink.GetTarget(link);
Assert.Equal(target, ourTarget);
// Compare with NCode.ReparsePoints (Windows only)
if (VelopackRuntimeInfo.IsWindows) {
var provider = new ReparsePointProvider();
var linkInfo = provider.GetLink(link);
Assert.Equal(LinkType.Symbolic, linkInfo.Type);
Assert.Equal(target, linkInfo.Target);
}
#if NET6_0_OR_GREATER
var fileInfo = new FileInfo(link);
Assert.NotNull(fileInfo.LinkTarget);
Assert.Equal(target, Path.GetFullPath(fileInfo.LinkTarget));
// Test interoperability: create with framework, read with ours
var link2 = Path.Combine(tempFolder, "link2.txt");
File.CreateSymbolicLink(link2, target);
Assert.Equal(target, SymbolicLink.GetTarget(link2));
#endif
}
[Fact]
public void DirectorySymlink_AllImplementationsAgree()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target = Path.Combine(tempFolder, "targetDir");
var link = Path.Combine(tempFolder, "linkDir");
Directory.CreateDirectory(target);
File.WriteAllText(Path.Combine(target, "file.txt"), "content");
// Create with our implementation
SymbolicLink.Create(link, target);
// Verify all implementations agree
var ourTarget = SymbolicLink.GetTarget(link);
Assert.Equal(target, ourTarget);
// Compare with NCode.ReparsePoints (Windows only)
if (VelopackRuntimeInfo.IsWindows) {
var provider = new ReparsePointProvider();
var linkInfo = provider.GetLink(link);
// Directory symlinks on Windows are actually junctions
Assert.True(linkInfo.Type == LinkType.Junction || linkInfo.Type == LinkType.Symbolic);
Assert.Equal(target, linkInfo.Target);
}
#if NET6_0_OR_GREATER
var dirInfo = new DirectoryInfo(link);
Assert.NotNull(dirInfo.LinkTarget);
Assert.Equal(target, Path.GetFullPath(dirInfo.LinkTarget));
// Test interoperability: create with framework, read with ours
var link2 = Path.Combine(tempFolder, "linkDir2");
Directory.CreateSymbolicLink(link2, target);
Assert.Equal(target, SymbolicLink.GetTarget(link2));
#endif
}
[Fact]
public void RelativeSymlink_AllImplementationsAgree()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var subdir = Directory.CreateDirectory(Path.Combine(tempFolder, "subdir")).FullName;
var target = Path.Combine(tempFolder, "target.txt");
var link = Path.Combine(subdir, "link.txt");
File.WriteAllText(target, "test");
// Create relative symlink with our implementation
SymbolicLink.Create(link, target, relative: true);
// Verify our implementation handles relative vs absolute correctly
var ourAbsoluteTarget = SymbolicLink.GetTarget(link);
var ourRelativeTarget = SymbolicLink.GetTarget(link, relative: true);
Assert.Equal(target, ourAbsoluteTarget);
Assert.Contains("..", ourRelativeTarget);
// Compare with NCode.ReparsePoints (Windows only)
if (VelopackRuntimeInfo.IsWindows) {
var provider = new ReparsePointProvider();
var linkInfo = provider.GetLink(link);
Assert.Equal(LinkType.Symbolic, linkInfo.Type);
// NCode returns the raw target path as stored in the symlink
// For relative symlinks, this will be the relative path, not absolute
Assert.Equal(ourRelativeTarget, linkInfo.Target);
}
#if NET6_0_OR_GREATER
var fileInfo = new FileInfo(link);
Assert.NotNull(fileInfo.LinkTarget);
// Framework returns the relative path for relative symlinks
Assert.Contains("..", fileInfo.LinkTarget);
Assert.Equal(fileInfo.LinkTarget, ourRelativeTarget);
#endif
}
[Fact]
public void MultipleLevelsOfSymlinks_AllImplementationsAgree()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var file = Path.Combine(tempFolder, "original.txt");
var link1 = Path.Combine(tempFolder, "link1.txt");
var link2 = Path.Combine(tempFolder, "link2.txt");
File.WriteAllText(file, "content");
// Create chain: link2 -> link1 -> file
SymbolicLink.Create(link1, file);
SymbolicLink.Create(link2, link1);
// Verify our implementation
Assert.Equal(file, SymbolicLink.GetTarget(link1));
Assert.Equal(link1, SymbolicLink.GetTarget(link2));
// Verify content access works through the chain
Assert.Equal("content", File.ReadAllText(link1));
Assert.Equal("content", File.ReadAllText(link2));
// Compare with NCode.ReparsePoints (Windows only)
if (VelopackRuntimeInfo.IsWindows) {
var provider = new ReparsePointProvider();
var link1Info = provider.GetLink(link1);
var link2Info = provider.GetLink(link2);
Assert.Equal(LinkType.Symbolic, link1Info.Type);
Assert.Equal(LinkType.Symbolic, link2Info.Type);
Assert.Equal(file, link1Info.Target);
Assert.Equal(link1, link2Info.Target);
}
#if NET6_0_OR_GREATER
var fileInfo1 = new FileInfo(link1);
var fileInfo2 = new FileInfo(link2);
Assert.NotNull(fileInfo1.LinkTarget);
Assert.NotNull(fileInfo2.LinkTarget);
Assert.Equal(file, Path.GetFullPath(fileInfo1.LinkTarget));
Assert.Equal(link1, Path.GetFullPath(fileInfo2.LinkTarget));
// Test ResolveLinkTarget for final resolution
var finalTarget = fileInfo2.ResolveLinkTarget(true);
Assert.Equal(file, finalTarget?.FullName);
#endif
}
[Fact]
public void Delete_OnlyDeletesLinkNotTarget()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target = Path.Combine(tempFolder, "target.txt");
var link = Path.Combine(tempFolder, "link.txt");
File.WriteAllText(target, "important data");
SymbolicLink.Create(link, target);
// Delete the link
SymbolicLink.Delete(link);
// Link should be gone, but target should remain
Assert.False(File.Exists(link));
Assert.False(SymbolicLink.Exists(link));
Assert.True(File.Exists(target));
Assert.Equal("important data", File.ReadAllText(target));
}
[SkippableFact]
public void Exists_ReturnsFalseForHardLink()
{
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target = Path.Combine(tempFolder, "target.txt");
var hardLink = Path.Combine(tempFolder, "hardlink.txt");
File.WriteAllText(target, "test");
// Create hard link using P/Invoke
if (!CreateHardLink(hardLink, target, IntPtr.Zero)) {
// Skip test if hard link creation fails (may need elevation)
return;
}
// Hard link should exist as a file but not as a symbolic link
Assert.True(File.Exists(hardLink));
Assert.False(SymbolicLink.Exists(hardLink));
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
static extern bool CreateHardLink(string lpFileName, string lpExistingFileName, IntPtr lpSecurityAttributes);
[Fact]
public void GetTarget_ErrorMessages_AreDescriptive()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var regularFile = Path.Combine(tempFolder, "regular.txt");
var regularDir = Path.Combine(tempFolder, "regularDir");
var nonExistent = Path.Combine(tempFolder, "nonExistent");
File.WriteAllText(regularFile, "test");
Directory.CreateDirectory(regularDir);
// Test various error conditions
var ex1 = Assert.Throws<IOException>(() => SymbolicLink.GetTarget(regularFile));
Assert.Contains("junction", ex1.Message.ToLower());
var ex2 = Assert.Throws<IOException>(() => SymbolicLink.GetTarget(regularDir));
Assert.Contains("junction", ex2.Message.ToLower());
var ex3 = Assert.Throws<IOException>(() => SymbolicLink.GetTarget(nonExistent));
Assert.Contains("junction", ex2.Message.ToLower());
}
[Fact]
public void Create_OverwriteExistingSymlink()
{
using var _1 = TempUtil.GetTempDirectory(out var tempFolder);
var target1 = Path.Combine(tempFolder, "target1.txt");
var target2 = Path.Combine(tempFolder, "target2.txt");
var link = Path.Combine(tempFolder, "link.txt");
File.WriteAllText(target1, "content1");
File.WriteAllText(target2, "content2");
// Create initial symlink
SymbolicLink.Create(link, target1);
Assert.Equal("content1", File.ReadAllText(link));
// Overwrite with new target
SymbolicLink.Create(link, target2, overwrite: true);
Assert.Equal("content2", File.ReadAllText(link));
Assert.Equal(target2, SymbolicLink.GetTarget(link));
}
}