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
63 changes: 63 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,40 @@ jobs:

Compress-Archive -Path "$packageDir/*" -DestinationPath "$artifacts/SqlPilot-$tag.zip"

# ─── Modern .vsix for gallery distribution (additive — payload ZIP above is untouched) ───
# A .vsix is an OPC ZIP of the SSMS22 layout + [Content_Types].xml.
# Built from a SEPARATE staging copy so the loose-file SSMS22/ folder
# shipped in the payload ZIP stays byte-for-byte identical.
$vsixStaging = "$artifacts/vsix-staging"
New-Item -ItemType Directory -Path $vsixStaging -Force | Out-Null
Copy-Item "$ssms22Dir/*" $vsixStaging -Recurse -Force
# Drop the loose-file deployment metadata from the .vsix only. catalog.json/
# manifest.json make the modern SSMS 22 VSIXInstaller take a v3-catalog path
# that rejects the manifest ("version Invalid"); a clean OPC vsix (manifest +
# payload) installs. The payload ZIP above still ships them for the installer.
Remove-Item "$vsixStaging/catalog.json","$vsixStaging/manifest.json" -Force -ErrorAction SilentlyContinue
# Build the content as an indented array-join (NOT a column-0 here-string,
# which would break this YAML literal block). Single-quoted lines keep the
# XML angle brackets/quotes literal.
$contentTypes = @(
'<?xml version="1.0" encoding="utf-8"?>'
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'
' <Default Extension="vsixmanifest" ContentType="text/xml" />'
' <Default Extension="xml" ContentType="text/xml" />'
' <Default Extension="json" ContentType="application/json" />'
' <Default Extension="dll" ContentType="application/octet-stream" />'
' <Default Extension="pkgdef" ContentType="text/plain" />'
'</Types>'
) -join "`r`n"
# Brackets in the filename are PowerShell wildcards — must use -LiteralPath.
Set-Content -LiteralPath "$vsixStaging/[Content_Types].xml" -Value $contentTypes -Encoding utf8 -NoNewline
$vsixPath = "$artifacts/SqlPilot-$tag.vsix"
Remove-Item $vsixPath -Force -ErrorAction SilentlyContinue
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory($vsixStaging, $vsixPath)
Write-Host "Built VSIX: $vsixPath"
Get-ChildItem $vsixStaging | Select-Object Name

# ─── Friendly WPF installer (separate ZIP) ───
# SqlPilotInstaller.exe + its dependency DLLs. End users download
# this small ZIP, extract it, and run the .exe — the installer
Expand All @@ -177,7 +211,36 @@ jobs:
with:
files: |
artifacts/SqlPilot-*.zip
artifacts/SqlPilot-*.vsix
artifacts/SqlPilotInstaller-*.zip
generate_release_notes: true
draft: false
prerelease: ${{ contains(github.ref, '-') }}

- name: Publish to extension galleries
# Skip prerelease tags (containing '-') so test tags don't hit the galleries.
if: ${{ !contains(github.ref, '-') }}
shell: pwsh
env:
VSIXGALLERY_TOKEN: ${{ secrets.VSIXGALLERY_TOKEN }}
SSMSGALLERY_TOKEN: ${{ secrets.SSMSGALLERY_TOKEN }}
run: |
$tag = "${{ steps.version.outputs.tag }}"
$vsix = "artifacts/SqlPilot-$tag.vsix"
if (-not (Test-Path $vsix)) { Write-Error "VSIX not found: $vsix"; exit 1 }
$repo = "https://github.com/mourier/sql-pilot"
$issues = "https://github.com/mourier/sql-pilot/issues"
$readme = "https://raw.githubusercontent.com/mourier/sql-pilot/main/README.md"

# VSIX Gallery (vsixgallery.com) — raw body + X-Manage-Token header.
$vg = "https://www.vsixgallery.com/api/upload?repo=$repo&issuetracker=$issues&readmeUrl=$readme"
Write-Host "Publishing to VSIX Gallery..."
curl.exe --fail --silent --show-error -X POST $vg -H "X-Manage-Token: $env:VSIXGALLERY_TOKEN" --data-binary "@$vsix"
if ($LASTEXITCODE -ne 0) { throw "VSIX Gallery publish failed with exit code $LASTEXITCODE" }

# SSMS Gallery (ssmsgallery.azurewebsites.net) — multipart form per its devguide.
$sg = "https://ssmsgallery.azurewebsites.net/api/upload?repo=$repo&issuetracker=$issues&readmeUrl=$readme"
Write-Host "Publishing to SSMS Gallery..."
curl.exe --fail --silent --show-error -X POST $sg -H "X-Manage-Token: $env:SSMSGALLERY_TOKEN" -F "file=@$vsix"
if ($LASTEXITCODE -ne 0) { throw "SSMS Gallery publish failed with exit code $LASTEXITCODE" }
Write-Host "Gallery publish complete."
Comment thread
coderabbitai[bot] marked this conversation as resolved.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
<a href="https://github.com/mourier/sql-pilot/actions/workflows/ci.yml">
<img src="https://github.com/mourier/sql-pilot/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI" />
</a>
<a href="https://www.vsixgallery.com/extension/SqlPilot.8f4a3b2e-1c5d-4e6f-9a0b-7d8c2e3f4a5b/">
<img src="https://www.vsixgallery.com/badge/SqlPilot.8f4a3b2e-1c5d-4e6f-9a0b-7d8c2e3f4a5b.svg" alt="VSIX Gallery" />
</a>
<img src="https://img.shields.io/badge/SSMS-18%20%7C%2020%20%7C%2022-1f4e79" alt="SSMS 18, 20, 22" />
<img src="https://img.shields.io/badge/Language-C%23-239120" alt="Language: C#" />
<img src="https://img.shields.io/badge/Platform-Windows%20x64%20%7C%20Arm64-0078D4" alt="Windows x64 and Arm64" />
Expand Down Expand Up @@ -91,6 +94,10 @@ The installer downloads the matching release payload from GitHub itself, verifie

> **First run will show "Windows protected your PC"** because we don't pay for an EV code-signing certificate yet. Click **More info** → **Run anyway**. The installer source is in [`src/SqlPilot.Installer/`](src/SqlPilot.Installer) — verify before running if you want.

### SSMS 22 via VSIX Gallery (alternative)

SSMS 22 users can also install from the [VSIX Gallery](https://www.vsixgallery.com/extension/SqlPilot.8f4a3b2e-1c5d-4e6f-9a0b-7d8c2e3f4a5b/) or [SSMS Gallery](https://ssmsgallery.azurewebsites.net/): download the `.vsix` and run it (or subscribe to the gallery feed for update notifications). **SSMS 18 and 20 must use the installer above** — the `.vsix` targets the modern shell (SSMS 21–22) only.

### Power user — scripted install

For CI pipelines, silent installs, or admins who script everything:
Expand Down
3 changes: 0 additions & 3 deletions build/SqlPilotDlls.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ SqlPilot.Core.dll
SqlPilot.Smo.dll
SqlPilot.UI.dll
CommunityToolkit.Mvvm.dll
System.Text.Json.dll
System.Text.Encodings.Web.dll
System.Buffers.dll
System.Memory.dll
System.Numerics.Vectors.dll
System.Runtime.CompilerServices.Unsafe.dll
System.Threading.Tasks.Extensions.dll
System.ValueTuple.dll
11 changes: 10 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,22 @@ C:\Program Files (x86)\Microsoft SQL Server Management Studio {18|20}\Common7\ID

## Release Pipeline

A tag push matching `v*` triggers `.github/workflows/release.yml`, which builds Modern + Legacy + the WPF installer in one `msbuild SqlPilot.sln` step and publishes **two release assets**:
A tag push matching `v*` triggers `.github/workflows/release.yml`, which builds Modern + Legacy + the WPF installer in one `msbuild SqlPilot.sln` step and publishes **three release assets**:

- **`SqlPilot-vX.Y.Z.zip`** — the payload. Contains `SSMS22/` and `SSMS18-20/` subfolders with the extension DLLs + manifests + pkgdef, plus `Install-SqlPilot.ps1`, `Uninstall-SqlPilot.ps1`, `_SsmsHelpers.ps1`, LICENSE, NOTICE, README at the ZIP root. This is the file the WPF installer downloads at runtime, and what power users extract for scripted installs.
- **`SqlPilotInstaller-vX.Y.Z.zip`** — the friendly user-facing installer. Contains `SqlPilotInstaller.exe` + its dependency DLLs. Users download this, extract, double-click the exe, approve UAC, and the installer fetches the matching payload ZIP from GitHub itself.
- **`SqlPilot-vX.Y.Z.vsix`** — the modern (SSMS 21–22) gallery artifact. See *Modern `.vsix` artifact* below.

The installer pins to its own assembly version: it asks GitHub for `releases/tags/v{Major.Minor.Build}`, not `/releases/latest`. This keeps a `v1.0.0` installer from accidentally pulling a `v2.0.0` payload.

### Modern `.vsix` artifact

`release.yml` also produces `SqlPilot-vX.Y.Z.vsix` as a *purely additive* output. A `.vsix` is just an OPC ZIP of the modern extension layout, so the workflow takes a **separate copy** of the already-assembled `SSMS22/` folder (DLLs from `build/SqlPilotDlls.txt` + `pkgdef` + `extension.vsixmanifest` (v2) + `manifest.json` + `catalog.json`), drops in a generated `[Content_Types].xml`, and zips it via `System.IO.Compression`. Because it reuses the same assembled folder, payload parity with the installer is automatic. The loose-file payload ZIP is built from its own folder and stays byte-for-byte unchanged — the `.vsix` is never substituted for it.

The `.vsix` is attached to the GitHub release and, for non-prerelease tags only (tags without a `-` suffix), pushed to the [VSIX Gallery](https://www.vsixgallery.com/) and [SSMS Gallery](https://ssmsgallery.azurewebsites.net/). Manage tokens come from the repo secrets `VSIXGALLERY_TOKEN` and `SSMSGALLERY_TOKEN`; both galleries key the listing off the manifest `Identity Id`, so re-publishing updates the same entry.

**Version-target policy:** the modern manifest targets SSMS `[21.0,23.0)` (SSMS 21–22). Keep the upper bound *pinned* — bump it per release only after testing on the new SSMS shell. Do **not** open-end the range.

### Version substitution

The version string `1.0.0` is hardcoded in **seven** files because we don't build a proper VSIX (no VSSDK packaging step — `dotnet build` doesn't produce one). The release workflow regex-patches all seven before calling `msbuild`:
Expand Down
4 changes: 4 additions & 0 deletions docs/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ For unattended deployment:

Per-version errors don't abort the others — if SSMS 20 is locked because it's still running, the uninstaller still cleans up 18 and 22 and tells you which versions to retry.

## SSMS 22 via VSIX Gallery (alternative)

SSMS 22 users can also install from the [VSIX Gallery](https://www.vsixgallery.com/extension/SqlPilot.8f4a3b2e-1c5d-4e6f-9a0b-7d8c2e3f4a5b/) or [SSMS Gallery](https://ssmsgallery.azurewebsites.net/): download the `.vsix` and run it (or subscribe to the gallery feed for update notifications). **SSMS 18 and 20 must use the installer or scripts above** — the `.vsix` targets the modern shell (SSMS 21–22) only.

## Manual install (last resort)

If both the WPF installer and the PowerShell scripts are unavailable for some reason, you can copy the files by hand. The release ZIP contains the exact file set under `SSMS22/` and `SSMS18-20/`.
Expand Down
2 changes: 2 additions & 0 deletions docs/SSMS_INTEGRATION_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,5 @@ For user-visible messages, use the SQL Pilot status bar (`IndexStatus.Text`).
- **`SynchronizeTree` without pre-expanded tree** — triggers connection dialogs
- **Building against SSMS 22's SMO and expecting it to work on SSMS 18** — version mismatch at runtime
- **Passing a `nodeInfo` from `FindNode` to `OpenTableHelperClass.EditTopNRows` without setting `Connection.DatabaseName` first** — triggers a new connection dialog because the connection defaults to master/empty
- **`<CreateVsixContainer>true</CreateVsixContainer>` on `SqlPilot.Package.csproj`** to get a `.vsix` out of the build — produces **no** `.vsix`. The VSSDK `CreateVsixContainer` target never imports because `source.extension.vsixmanifest` is only auto-globbed as a plain `None` item in this SDK-style project; the property is read but no target runs (verified via `-v:diagnostic`). Instead, the release builds the `.vsix` by zipping a copy of the assembled `SSMS22/` release folder plus a generated `[Content_Types].xml` (an OPC ZIP), which guarantees payload parity with the installer via the shared `build/SqlPilotDlls.txt`. See `release.yml` and `docs/ARCHITECTURE.md` § Modern `.vsix` artifact.
- **Shipping `catalog.json` / `manifest.json` *inside* the `.vsix`** — the modern SSMS 22 VSIXInstaller (the VS-Installer ServiceHub `VSIXInstaller.exe`) reads the metadata fine but **fails the actual install** with `InvalidExtensionManifestException: the provided vsixmanifest, version Invalid is not vsixV1, vsixV2, or vsixV3`. Their presence makes it take a v3-catalog install path that rejects the manifest. A clean OPC vsix containing only `extension.vsixmanifest` + `[Content_Types].xml` + payload (DLLs + pkgdef) installs with exit code 0 (to `%LOCALAPPDATA%\Microsoft\SSMS\<ver>\Extensions\<random>\`). So `release.yml` deletes `catalog.json`/`manifest.json` from the `.vsix` staging copy only — the loose-file payload ZIP still ships them for the file-copy installer. (`catalog.json`/`manifest.json` are the VS2017+ loose-file deployment descriptors, not VSIX-container files.)
6 changes: 5 additions & 1 deletion src/SqlPilot.Package/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
{"fileName": "/SqlPilot.UI.dll", "sha256": null},
{"fileName": "/SqlPilot.Package.pkgdef", "sha256": null},
{"fileName": "/CommunityToolkit.Mvvm.dll", "sha256": null},
{"fileName": "/System.Text.Json.dll", "sha256": null},
{"fileName": "/System.Buffers.dll", "sha256": null},
{"fileName": "/System.Memory.dll", "sha256": null},
{"fileName": "/System.Numerics.Vectors.dll", "sha256": null},
{"fileName": "/System.Runtime.CompilerServices.Unsafe.dll", "sha256": null},
{"fileName": "/System.Threading.Tasks.Extensions.dll", "sha256": null},
{"fileName": "/manifest.json", "sha256": null},
{"fileName": "/catalog.json", "sha256": null}
],
Expand Down
Loading