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
5 changes: 3 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ jobs:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ github.ref_name }}
run: |
$archive = "releases/ClipDiff-$env:RELEASE_VERSION-win-x64.zip"
gh release create $env:RELEASE_TAG $archive --verify-tag --generate-notes --title "ClipDiff $env:RELEASE_TAG"
$selfContained = "releases/ClipDiff-$env:RELEASE_VERSION-win-x64-self-contained.zip"
$net10 = "releases/ClipDiff-$env:RELEASE_VERSION-win-x64-net10.zip"
gh release create $env:RELEASE_TAG $selfContained $net10 --verify-tag --generate-notes --title "ClipDiff $env:RELEASE_TAG"
14 changes: 11 additions & 3 deletions .github/workflows/windows-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,18 @@ jobs:
shell: pwsh
run: ./scripts/create-local-release.ps1

- name: Upload portable Windows build
- name: Upload self-contained Windows build
uses: actions/upload-artifact@v7
with:
name: ClipDiff-win-x64
path: releases/ClipDiff-*-win-x64/
name: ClipDiff-win-x64-self-contained
path: releases/ClipDiff-*-win-x64-self-contained/
if-no-files-found: error
retention-days: 14

- name: Upload .NET 10 Windows build
uses: actions/upload-artifact@v7
with:
name: ClipDiff-win-x64-net10
path: releases/ClipDiff-*-win-x64-net10/
if-no-files-found: error
retention-days: 14
46 changes: 30 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,18 @@ Windows Server Core is not supported. The initial release target is `win-x64`.

## Install or update

Download the `ClipDiff-<version>-win-x64.zip` asset from the GitHub release and
extract the whole archive into its own directory. The complete application is
exactly these two files:
Download one of these assets from the GitHub release and extract the whole
archive into its own directory:

- `ClipDiff-<version>-win-x64-self-contained.zip` includes its own .NET runtime
and works without a separately installed runtime. It is the larger download.
- `ClipDiff-<version>-win-x64-net10.zip` is much smaller but requires the x64
**.NET 10 Desktop Runtime** (`Microsoft.WindowsDesktop.App`), not merely the
base `Microsoft.NETCore.App` runtime.

Check an existing installation with `dotnet --list-runtimes`; the smaller build
requires a `Microsoft.WindowsDesktop.App 10.x` entry for x64. Both archives
contain exactly these two files:

```text
ClipDiff.exe
Expand Down Expand Up @@ -189,10 +198,11 @@ An older test launcher could print `elevated=0, elevationType=2` after dropping
If the native test run fails, include the full output from `Native build:` through the final `[FAIL]` line and script error. The output identifies the test case, expected visibility, direct-handler and Windows-aggregate results, HRESULT/Win32 errors, selection counts and Shell attributes, and effective COM/menu registration. A visibility mismatch also prints menu IDs, states, separator labels, and a read-only snapshot of Shell restriction settings and ClipDiff's Approved/Blocked entries. These probes do not change policy or read clipboard contents or selected file paths. Passing the direct-handler check but failing the Windows-aggregate check narrows the problem to Shell discovery/aggregation; it does not by itself establish that a work-machine policy is responsible. Microsoft's [Shell extension approval policy documentation](https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-admx-windowsexplorer#enforceshellextensionsecurity) explains one possible restriction.

GitHub Actions runs the same release script on `windows-latest` for every push
and pull request. Each successful run provides a `ClipDiff-win-x64` artifact
containing only the executable and native DLL; extract both into the same
directory. It can be run manually from the repository's **Actions** tab as well.
Actual Explorer desktop interaction remains a separate manual check.
and pull request. Each successful run provides `ClipDiff-win-x64-self-contained`
and `ClipDiff-win-x64-net10` artifacts. Each contains only the executable and
native DLL; extract both into the same directory. The workflow can be run
manually from the repository's **Actions** tab as well. Actual Explorer desktop
interaction remains a separate manual check.

## Local release

Expand All @@ -208,16 +218,20 @@ output in a clean private directory, then creates both of these gitignored
outputs:

```text
releases\ClipDiff-<version>-win-x64\
releases\ClipDiff-<version>-win-x64.zip
releases\ClipDiff-<version>-win-x64-self-contained\
releases\ClipDiff-<version>-win-x64-self-contained.zip
releases\ClipDiff-<version>-win-x64-net10\
releases\ClipDiff-<version>-win-x64-net10.zip
```

The directory and ZIP are validated to contain exactly `ClipDiff.exe` and
Both directories and ZIPs are validated to contain exactly `ClipDiff.exe` and
`ClipDiff.ShellExtension.dll`. Those are the complete release; keep them
together. PDB, `deps.json`, `runtimeconfig.json`, `bin`, `obj`, and the rest of
the raw publish directory are not release payload. Pass `-Launch` to start the
packaged executable after publishing. The personal build is unsigned, so
Windows SmartScreen may warn before first launch.
together. The self-contained variant bundles .NET; the `net10` variant expects
the x64 .NET 10 Desktop Runtime on the target computer. PDB, `deps.json`,
`runtimeconfig.json`, `bin`, `obj`, and the rest of the raw publish directories
are not release payload. Pass `-Launch` to start the self-contained packaged
executable after publishing. The personal builds are unsigned, so Windows
SmartScreen may warn before first launch.

To skip the native Explorer integration tests for a local release, pass `-SkipNativeTests`:

Expand All @@ -241,8 +255,8 @@ Set-ExecutionPolicy -Scope Process Bypass
.\scripts\create-local-release.ps1
```

Copy the resulting ZIP to the target PC and extract both files together. No
separate .NET runtime is required.
Copy the chosen ZIP to the target PC and extract both files together. Choose the
`net10` archive only when the x64 .NET 10 Desktop Runtime is already installed.

No installer, automatic updater, or code signing is included in the initial release.

Expand Down
34 changes: 23 additions & 11 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Releasing ClipDiff

The release contract is the ZIP, not a `bin` or raw `dotnet publish` directory.
Every ClipDiff Windows x64 release contains exactly:
The release contract is the ZIPs, not a `bin` or raw `dotnet publish`
directory. Every ClipDiff Windows x64 package contains exactly:

```text
ClipDiff.exe
Expand All @@ -23,15 +23,25 @@ On Windows, from a normal non-administrator PowerShell in the repository root:
The script requires the .NET 10 SDK and Visual Studio C++ build tools with a
Windows SDK. It builds and tests the native extension, runs the .NET tests,
publishes the self-contained application in a clean staging directory, and
validates the final folder and ZIP. Output is:
validates both final folders and ZIPs. Output is:

```text
releases\ClipDiff-<version>-win-x64\
releases\ClipDiff-<version>-win-x64-self-contained\
ClipDiff.exe
ClipDiff.ShellExtension.dll
releases\ClipDiff-<version>-win-x64.zip
releases\ClipDiff-<version>-win-x64-self-contained.zip

releases\ClipDiff-<version>-win-x64-net10\
ClipDiff.exe
ClipDiff.ShellExtension.dll
releases\ClipDiff-<version>-win-x64-net10.zip
```

The self-contained package bundles .NET. The smaller `net10` package requires
the x64 .NET 10 Desktop Runtime and checks for `Microsoft.WindowsDesktop.App
10.x` when documenting compatibility; the base .NET runtime alone is not
sufficient for this WPF application.

The version defaults to the `Version` property in `Directory.Build.props`. A
specific version can be supplied when reproducing a tagged build:

Expand All @@ -55,15 +65,17 @@ a published release.
```

The **Release** workflow validates the tag, runs the complete release script,
and creates a GitHub release with generated notes and
`ClipDiff-1.2.3-win-x64.zip` attached. It will not publish if any build, test, or
payload validation fails.
and creates a GitHub release with generated notes and both
`ClipDiff-1.2.3-win-x64-self-contained.zip` and
`ClipDiff-1.2.3-win-x64-net10.zip` attached. It will not publish if any build,
test, or payload validation fails.

## Install or update the portable build

Extract the whole ZIP into a directory owned by the current user and run
`ClipDiff.exe`. Do not move the DLL elsewhere or run `regsvr32`; ClipDiff owns
its per-user Explorer registration.
Extract one whole ZIP into a directory owned by the current user and run
`ClipDiff.exe`. Use the `net10` ZIP only when `dotnet --list-runtimes` contains
an x64 `Microsoft.WindowsDesktop.App 10.x` entry. Do not move the DLL elsewhere
or run `regsvr32`; ClipDiff owns its per-user Explorer registration.

For an update, quit ClipDiff and extract the new version into a new directory.
This avoids overwriting a DLL that Explorer may still have loaded. Update any
Expand Down
9 changes: 5 additions & 4 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -1318,9 +1318,10 @@ It should:
1. Build and test the native Explorer DLL with Visual Studio C++ build tools and the Windows SDK.
1. Run core tests.
1. Publish a Release build through a clean private staging directory.
1. Place a versioned directory and ZIP in the gitignored `releases/` directory.
1. Place `ClipDiff.ShellExtension.dll` alongside `ClipDiff.exe`; keep the DLL outside the .NET single-file bundle.
1. Validate that the versioned directory and ZIP contain exactly `ClipDiff.exe` and `ClipDiff.ShellExtension.dll`, with no SDK by-products or stale files.
1. Place versioned self-contained and .NET 10 framework-dependent directories and ZIPs in the gitignored `releases/` directory.
1. Publish both variants as single-file applications. The framework-dependent variant requires the x64 .NET 10 Desktop Runtime.
1. Place `ClipDiff.ShellExtension.dll` alongside `ClipDiff.exe` in both variants; keep the DLL outside the .NET single-file bundle.
1. Validate that every versioned directory and ZIP contains exactly `ClipDiff.exe` and `ClipDiff.ShellExtension.dll`, with no SDK by-products or stale files.
1. Optionally launch the resulting executable.

Suggested publish properties:
Expand All @@ -1337,7 +1338,7 @@ Do not enable trimming for the initial WPF build.

The native projects are built separately with `scripts/build-shell-extension.ps1 -Test`, so macOS can still compile and test the .NET solution. The release script runs this native build first. Distribute the executable and DLL together. Explorer can keep a loaded DLL locked after ClipDiff quits; updating into a new directory avoids overwriting a loaded DLL, and restarting Explorer or signing out may be needed to unload an older handler. Do not restart Explorer automatically.

Continuous integration must call the same release script rather than duplicate the publish commands. A semantic-version tag in the form `v1.2.3` should run the complete build and test process, then create a GitHub release containing the validated versioned ZIP.
Continuous integration must call the same release script rather than duplicate the publish commands. A semantic-version tag in the form `v1.2.3` should run the complete build and test process, then create a GitHub release containing both validated versioned ZIPs.

For local releases, `create-local-release.ps1 -SkipNativeTests` may explicitly skip the native Explorer integration tests. It must still build and package the DLL and run the .NET tests. Native tests remain enabled by default and in CI.

Expand Down
Loading