Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ jobs:
run: >-
./build/Publish-ReleaseCandidate.ps1
-OutputDirectory artifacts/rc
-Version 0.1.0-ci.${{ github.run_number }}
-Version 0.1.2-ci.${{ github.run_number }}

- name: Publish per-user MSI
if: ${{ github.event_name != 'pull_request' }}
run: >-
./build/Publish-Installer.ps1
-OutputDirectory artifacts/installer
-Version 0.1.0-ci.${{ github.run_number }}
-Version 0.1.2-ci.${{ github.run_number }}
-PayloadDirectory artifacts/rc/QuickPods
-SkipPayloadPublish
-SkipRestore
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ on:
workflow_dispatch:
inputs:
version:
description: Semantic release version (for example 0.1.1)
description: Semantic release version (for example 0.1.2)
required: true
default: 0.1.1
default: 0.1.2
type: string

permissions:
Expand Down Expand Up @@ -89,6 +89,10 @@ jobs:
throw 'Portable manifest version or signing state is invalid.'
}

./build/Test-PortablePayload.ps1 `
-PayloadDirectory artifacts/release/QuickPods `
-ExpectedVersion $env:QUICKPODS_RELEASE_VERSION | Out-Null

$expectedHash = ((Get-Content -LiteralPath $checksumFile -Raw).Trim() -split '\s+')[0]
$actualHash = (Get-FileHash -LiteralPath $archive -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actualHash -ne $expectedHash) {
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ All notable changes to QuickPods will be documented in this file. The format fol

## [Unreleased]

### Changed

- Reorganized the portable package around an obvious root `QuickPods.exe` launcher.
- Moved the application runtime and helper executables into an internal `app` directory.
- Collected license and third-party notice files in a dedicated `licenses` directory.
- Added a bilingual package README with startup, security, update, and internal-file guidance.

## [0.1.1] - 2026-08-08

### Changed
Expand Down
6 changes: 3 additions & 3 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
<VersionPrefix>0.1.1</VersionPrefix>
<AssemblyVersion>0.1.1.0</AssemblyVersion>
<FileVersion>0.1.1.0</FileVersion>
<VersionPrefix>0.1.2</VersionPrefix>
<AssemblyVersion>0.1.2.0</AssemblyVersion>
<FileVersion>0.1.2.0</FileVersion>
<Product>QuickPods</Product>
<Company>QuickPods contributors</Company>
<Authors>QuickPods contributors</Authors>
Expand Down
14 changes: 14 additions & 0 deletions QuickPods.sln
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickPods.BluetoothWorker",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickPods.Presentation", "src\QuickPods.Presentation\QuickPods.Presentation.csproj", "{FCDDB738-2751-4E22-861F-D2B40F208C8A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickPods.Launcher", "src\QuickPods.Launcher\QuickPods.Launcher.csproj", "{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -57,6 +59,18 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Debug|x64.ActiveCfg = Debug|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Debug|x64.Build.0 = Debug|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Debug|x86.ActiveCfg = Debug|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Debug|x86.Build.0 = Debug|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Release|Any CPU.Build.0 = Release|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Release|x64.ActiveCfg = Release|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Release|x64.Build.0 = Release|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Release|x86.ActiveCfg = Release|Any CPU
{C81E23D4-44F8-4C63-9D8D-A6C6D1B5E521}.Release|x86.Build.0 = Release|Any CPU
{61B767B0-94E2-449F-AE59-335FEDD7A800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61B767B0-94E2-449F-AE59-335FEDD7A800}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61B767B0-94E2-449F-AE59-335FEDD7A800}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand Down
4 changes: 2 additions & 2 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@
1. [最新のRelease](https://github.com/rimtty/QuickPods/releases/latest)から`QuickPods-<version>-win-x64.zip`と`SHA256SUMS.txt`をダウンロードします。
2. ZIPのSHA-256チェックサムが`SHA256SUMS.txt`と一致することを確認します。
3. ZIP内の`QuickPods`フォルダー全体を、自分のWindowsユーザーが所有する固定の場所へ展開します。
4. 展開したフォルダーの`QuickPods.exe`を起動します。
4. そのフォルダーの直下にある`QuickPods.exe`を起動します。

パッケージは自己完結型で、.NETを別途インストールする必要はありません。未署名のため、初回起動時にWindowsがSmartScreen警告を表示する場合があります。実行を判断する前に、ダウンロード元とチェックサムを確認してください。すべてのファイルを同じフォルダーに保ち、Windowsログイン時の自動起動を有効にした後はフォルダーを移動しないでください。
パッケージは自己完結型で、.NETを別途インストールする必要はありません。ルートの`QuickPods.exe`は軽量ランチャーです。ランタイムと補助プロセスは内部の`app`フォルダー、ライセンス関連ファイルは`licenses`フォルダーにまとめられています。`app`内のファイルを直接起動したり移動したりしないでください。未署名のため、初回起動時にWindowsがSmartScreen警告を表示する場合があります。実行を判断する前に、ダウンロード元とチェックサムを確認してください。すべてのファイルを同じフォルダーに保ち、Windowsログイン時の自動起動を有効にした後はフォルダーを移動しないでください。

ポータブル版の更新・削除、ソースからのビルド、パッケージ作成については[ユーザーガイド](docs/user-guide.md)、[更新ポリシー](docs/release/update-policy.md)、[リリースガイド](docs/release/README.md)を参照してください。

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ QuickPods continues to work from the notification area when the taskbar is left-
1. Download `QuickPods-<version>-win-x64.zip` and `SHA256SUMS.txt` from the [latest release](https://github.com/rimtty/QuickPods/releases/latest).
2. Verify the ZIP's SHA-256 checksum against `SHA256SUMS.txt`.
3. Extract the complete `QuickPods` folder to a permanent location owned by your Windows user.
4. Run `QuickPods.exe` from that folder.
4. Run the `QuickPods.exe` located directly in that folder.

The package is self-contained and does not require a separate .NET installation. Because it is unsigned, Windows may display a SmartScreen warning on first launch. Confirm the download source and checksum before deciding whether to run it. Keep the extracted files together and do not move the folder after enabling start at sign-in.
The package is self-contained and does not require a separate .NET installation. The root `QuickPods.exe` is a lightweight launcher; runtime files and helper processes are kept in the internal `app` folder, while license files are collected under `licenses`. Do not start or move files from `app` manually. Because the package is unsigned, Windows may display a SmartScreen warning on first launch. Confirm the download source and checksum before deciding whether to run it. Keep the extracted files together and do not move the folder after enabling start at sign-in.

For portable updates, removal, source builds, and packaging details, see the [user guide](docs/user-guide.md), [update policy](docs/release/update-policy.md), and [release guide](docs/release/README.md).

Expand Down
11 changes: 6 additions & 5 deletions build/New-InstallerPayloadFragment.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ if (-not (Test-Path -LiteralPath $payloadRoot -PathType Container)) {
throw "Payload directory does not exist: $payloadRoot"
}

$selfContainedProof = & (Join-Path $PSScriptRoot "Test-SelfContainedPayload.ps1") `
$portableProof = & (Join-Path $PSScriptRoot "Test-PortablePayload.ps1") `
-PayloadDirectory $payloadRoot
$requiredFiles = @($selfContainedProof.Executables) +
@($selfContainedProof.RuntimeFiles) +
@($selfContainedProof.RuntimeConfigs) +
@("artifact-manifest.json")
$requiredFiles = @($portableProof.RootFiles) +
@($portableProof.LicenseFiles) +
@($portableProof.Executables) +
@($portableProof.RuntimeFiles) +
@($portableProof.RuntimeConfigs)
$componentIds = [System.Collections.Generic.List[string]]::new()
foreach ($requiredFile in $requiredFiles) {
if (-not (Test-Path -LiteralPath (Join-Path $payloadRoot $requiredFile) -PathType Leaf)) {
Expand Down
69 changes: 61 additions & 8 deletions build/Publish-ReleaseCandidate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,32 @@ if ($outputRoot -eq $repositoryRoot) {
}

$payloadDirectory = Join-Path $outputRoot "QuickPods"
$applicationDirectory = Join-Path $payloadDirectory "app"
$licenseDirectory = Join-Path $payloadDirectory "licenses"
$buildArtifactsDirectory = Join-Path $outputRoot ".build-artifacts"
$launcherPublishDirectory = Join-Path $buildArtifactsDirectory "launcher-publish"
if (Test-Path -LiteralPath $outputRoot) {
Remove-Item -LiteralPath $outputRoot -Recurse -Force
}

New-Item -ItemType Directory -Path $payloadDirectory -Force | Out-Null
New-Item -ItemType Directory -Path $applicationDirectory -Force | Out-Null
New-Item -ItemType Directory -Path $licenseDirectory -Force | Out-Null

$projects = @(
$applicationProjects = @(
"src/QuickPods.App/QuickPods.App.csproj",
"src/QuickPods.TaskbarObserver/QuickPods.TaskbarObserver.csproj",
"src/QuickPods.TaskbarHost/QuickPods.TaskbarHost.csproj",
"src/QuickPods.BluetoothWorker/QuickPods.BluetoothWorker.csproj"
)
$launcherProject = "src/QuickPods.Launcher/QuickPods.Launcher.csproj"

Push-Location $repositoryRoot
try {
if (-not $SkipRestore) {
# The app project graph includes every shipped executable. The normal solution lock
# files intentionally describe the framework-dependent build, so use RID-specific
# locks under ignored obj directories and never rewrite the committed locks.
& dotnet restore $projects[0] `
& dotnet restore $applicationProjects[0] `
--runtime win-x64 `
--artifacts-path $buildArtifactsDirectory `
-p:NuGetLockFilePath=obj/project.rc.packages.lock.json `
Expand All @@ -52,16 +57,26 @@ try {
if ($LASTEXITCODE -ne 0) {
throw "Release-candidate restore failed."
}

& dotnet restore $launcherProject `
--runtime win-x64 `
--artifacts-path $buildArtifactsDirectory `
-p:NuGetLockFilePath=obj/project.rc.packages.lock.json `
-p:RestoreLockedMode=false `
-p:RestoreForceEvaluate=true
if ($LASTEXITCODE -ne 0) {
throw "Portable launcher restore failed."
}
}

foreach ($project in $projects) {
foreach ($project in $applicationProjects) {
& dotnet publish $project `
--configuration Release `
--runtime win-x64 `
--self-contained true `
--no-restore `
--artifacts-path $buildArtifactsDirectory `
--output $payloadDirectory `
--output $applicationDirectory `
-p:Version=$Version `
-p:ContinuousIntegrationBuild=true `
-p:DebugSymbols=false `
Expand All @@ -72,6 +87,28 @@ try {
throw "Publish failed for $project."
}
}

& dotnet publish $launcherProject `
--configuration Release `
--runtime win-x64 `
--self-contained true `
--no-restore `
--artifacts-path $buildArtifactsDirectory `
--output $launcherPublishDirectory `
-p:Version=$Version `
-p:ContinuousIntegrationBuild=true `
-p:DebugSymbols=false `
-p:DebugType=None `
-p:PublishAot=true
if ($LASTEXITCODE -ne 0) {
throw "Publish failed for $launcherProject."
}

$launcherExecutable = Join-Path $launcherPublishDirectory "QuickPods.exe"
if (-not (Test-Path -LiteralPath $launcherExecutable -PathType Leaf)) {
throw "Native portable launcher was not produced."
}
Copy-Item -LiteralPath $launcherExecutable -Destination $payloadDirectory
} finally {
Pop-Location
if (Test-Path -LiteralPath $buildArtifactsDirectory -PathType Container) {
Expand All @@ -91,12 +128,19 @@ foreach ($legalFile in $legalFiles.GetEnumerator()) {
throw "Required legal file is missing: $($legalFile.Key)"
}

Copy-Item -LiteralPath $legalFile.Key -Destination (Join-Path $payloadDirectory $legalFile.Value)
Copy-Item -LiteralPath $legalFile.Key -Destination (Join-Path $licenseDirectory $legalFile.Value)
}

Copy-Item `
-LiteralPath (Join-Path $repositoryRoot "packaging\PORTABLE-README.txt") `
-Destination (Join-Path $payloadDirectory "README.txt")

$selfContainedProof = & (Join-Path $PSScriptRoot "Test-SelfContainedPayload.ps1") `
-PayloadDirectory $payloadDirectory
$requiredExecutables = @($selfContainedProof.Executables)
-PayloadDirectory $applicationDirectory
$requiredExecutables = @("QuickPods.exe") + @(
$selfContainedProof.Executables |
ForEach-Object { "app/$($_)" }
)

$executables = foreach ($requiredFile in $requiredExecutables) {
$versionInfo = (Get-Item -LiteralPath (Join-Path $payloadDirectory $requiredFile)).VersionInfo
Expand Down Expand Up @@ -126,6 +170,10 @@ $files = Get-ChildItem -LiteralPath $payloadDirectory -File -Recurse |
$manifest = [ordered]@{
product = "QuickPods"
version = $Version
layoutVersion = 2
entryPoint = "QuickPods.exe"
applicationDirectory = "app"
licenseDirectory = "licenses"
runtimeIdentifier = "win-x64"
selfContained = $true
includedFrameworks = $selfContainedProof.IncludedFrameworks
Expand All @@ -135,6 +183,10 @@ $manifest = [ordered]@{
}
$manifest | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $manifestPath -Encoding utf8NoBOM

& (Join-Path $PSScriptRoot "Test-PortablePayload.ps1") `
-PayloadDirectory $payloadDirectory `
-ExpectedVersion $Version | Out-Null

$archivePath = Join-Path $outputRoot "QuickPods-$Version-win-x64.zip"
Add-Type -AssemblyName System.IO.Compression
$archiveStream = [System.IO.File]::Open(
Expand Down Expand Up @@ -188,6 +240,7 @@ $checksumPath = Join-Path $outputRoot "SHA256SUMS.txt"
[pscustomobject]@{
Version = $Version
PayloadDirectory = $payloadDirectory
EntryPoint = (Join-Path $payloadDirectory "QuickPods.exe")
ArchivePath = $archivePath
ArchiveSha256 = $archiveHash
FileCount = @($files).Count + 1
Expand Down
10 changes: 6 additions & 4 deletions build/Test-InstallerLifecycle.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,10 @@ try {
-Force | Out-Null

$stage = "launch-previous"
$previousProcess = Start-Process -FilePath $installedExecutable -ArgumentList "--background" -PassThru
$previousLauncher = Start-Process -FilePath $installedExecutable -ArgumentList "--background" -PassThru
$previousLauncher.WaitForExit(5000)
Start-Sleep -Seconds 3
Assert-Condition -Condition (-not $previousProcess.HasExited) -Message "Previous QuickPods process did not remain resident."
Assert-Condition -Condition (@(Get-QuickPodsProcesses).Count -gt 0) -Message "Previous QuickPods process did not remain resident."

$stage = "upgrade-running-product"
Invoke-MsiExecution `
Expand All @@ -260,9 +261,10 @@ try {
$checks.runningUpgrade = $true

$stage = "launch-current"
$currentProcess = Start-Process -FilePath $installedExecutable -ArgumentList "--background" -PassThru
$currentLauncher = Start-Process -FilePath $installedExecutable -ArgumentList "--background" -PassThru
$currentLauncher.WaitForExit(5000)
Start-Sleep -Seconds 3
Assert-Condition -Condition (-not $currentProcess.HasExited) -Message "Current QuickPods process did not remain resident."
Assert-Condition -Condition (@(Get-QuickPodsProcesses).Count -gt 0) -Message "Current QuickPods process did not remain resident."

$stage = "uninstall-running-product"
Invoke-MsiExecution `
Expand Down
1 change: 1 addition & 0 deletions build/Test-InstallerPackage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ try {
"ThirdPartyNotices.txt",
"DOTNET-LICENSE.txt",
"DOTNET-THIRD-PARTY-NOTICES.txt",
"README.txt",
"artifact-manifest.json"
)) {
if (-not ($files | Where-Object { ($_[1] -split '\|')[-1] -eq $requiredFile })) {
Expand Down
Loading