Add your extension
Open SSMS VSIX Gallery is a simple HTTP API. You can publish a .vsix file by uploading it from a script, a manual curl command, or a CI system like GitHub Actions or AppVeyor. Pick whichever fits your project.
- How publishing works
- Upload manually with curl
- Use PowerShell
- Automate with GitHub Actions (recommended)
- Use AppVeyor
- Managing your extension
- Unlisted extensions
How publishing works
Publishing is a single POST to https://www.vsixgallery.com/api/upload with the .vsix file as the request body. The server reads the manifest, stores the package, and returns a JSON response with the extension URL and a manage URL.
Optional headers and query-string parameters:
X-Manage-Token— a value of your choice that becomes the “password” for the extension’s manage page. If you omit it on the first upload, the server generates one and embeds it in the manage URL it returns. See Managing your extension.?repo=<url>— the source repository URL.?issuetracker=<url>— the issue tracker URL.?readmeUrl=<url>— a URL to aREADME.mdthat should be rendered on the extension details page.
Once that one HTTP call works, automating it with PowerShell, GitHub Actions, or AppVeyor is just glue around the same call.
Upload manually with curl
Useful for one-off uploads or for trying the API before wiring it into a build.
curl -X POST "https://www.vsixgallery.com/api/upload?repo=https://github.com/me/MyExtension" \
-H "X-Manage-Token: my-secret-token" \
--data-binary @MyExtension.vsixThe response is JSON containing the extension page URL and the manage URL. On the very first upload of a new extension ID, if you didn’t supply X-Manage-Token, the manage URL will contain a one-time token as a query string — save the URL.
Use PowerShell
First you must execute the VSIX script:
(new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iexThat allows you to call methods to upload the .vsix extension file to the gallery:
Vsix-PublishToGallery
That will find all .vsix files in the working directory recursively and upload them. To specify the path, simply pass it in as the first parameter:
Vsix-PublishToGallery .\src\WebCompilerVsix\**\*.vsix
Automate with GitHub Actions
GitHub Actions is the recommended way to build and publish your SQL Server Management Studio extension. It provides free CI/CD directly in your GitHub repository, and the publish-vsixgallery action wraps the POST /api/upload call described above.
Create a file at .github/workflows/build.yaml in your repository with the following content:
Full workflow example (build, test, publish, release)
# yaml-language-server: $schema=https://www.schemastore.org/github-workflow.json
name: "Build"
permissions:
actions: write
contents: write
on:
push:
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
jobs:
build:
outputs:
version: ${{ steps.vsix_version.outputs.version-number }}
name: Build
runs-on: windows-latest
env:
Configuration: Release
DeployExtension: False
Solution: MyExtension.sln
TestProject: tests\MyExtension.Tests\MyExtension.Tests.csproj
VsixManifestPath: src\source.extension.vsixmanifest
VsixManifestSourcePath: src\source.extension.cs
steps:
- uses: actions/checkout@v6
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v3
- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.slnx', '**/*.sln', '**/*.csproj', '**/packages.config', '**/packages.lock.json', '**/nuget.config') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Increment VSIX version
id: vsix_version
uses: madskristensen/vsix-version-stamp@v1.1
with:
manifest-file: ${{ env.VsixManifestPath }}
vsix-token-source-file: ${{ env.VsixManifestSourcePath }}
- name: Restore NuGet packages
run: msbuild ${{ env.Solution }} /v:m -restore /t:Restore
- name: Build
run: msbuild ${{ env.Solution }} /v:m /m /p:Configuration=${{ env.Configuration }} /p:DeployExtension=${{ env.DeployExtension }}
- name: Run tests
run: dotnet test ${{ env.TestProject }} --configuration ${{ env.Configuration }} --no-build --no-restore --verbosity normal --logger:"trx;LogFileName=test-results.trx"
- name: Report test results
uses: dorny/test-reporter@v3
if: ${{ always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }}
with:
name: Test Results
path: '**/test-results.trx'
reporter: dotnet-trx
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: ${{ github.event.repository.name }}.vsix
path: src\bin\${{ env.Configuration }}\net48\MyExtension.vsix
publish:
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
needs: build
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Download Package artifact
uses: actions/download-artifact@v8
with:
name: ${{ github.event.repository.name }}.vsix
- name: Upload to Open VSIX
uses: madskristensen/publish-vsixgallery@v1
with:
vsix-file: ${{ github.event.repository.name }}.vsix
manage-token: ${{ secrets.VS_PUBLISHER_ACCESS_TOKEN }}
- name: Publish extension to Marketplace
if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[release]') }}
uses: madskristensen/publish-marketplace@v1.16
with:
extension-file: '${{ github.event.repository.name }}.vsix'
publish-manifest-file: 'vs-publish.json'
personal-access-code: ${{ secrets.VS_PUBLISHER_ACCESS_TOKEN }}
- name: Tag and release
if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.head_commit.message, '[release]') }}
id: tag_release
uses: softprops/action-gh-release@v3
with:
body: release ${{ needs.build.outputs.version }}
generate_release_notes: true
tag_name: ${{ needs.build.outputs.version }}
files: |
**/*.vsix
Key GitHub Actions
- microsoft/setup-msbuild — adds MSBuild to the runner’s PATH
- madskristensen/vsix-version-stamp — auto-increments the VSIX version number and stamps it into the manifest
- dorny/test-reporter — renders test results in the workflow run summary
- madskristensen/publish-vsixgallery — publishes to Open SSMS VSIX Gallery (writes a run summary with the extension link, badge, and a self-service manage link for deleting the extension)
- madskristensen/publish-marketplace — publishes to the SQL Server Management Studio Marketplace
- softprops/action-gh-release — creates a GitHub release and attaches the .vsix as an asset
Real-world examples using GitHub Actions:
Use AppVeyor
AppVeyor is a build server hosted in the cloud and it’s free.
After you’ve created an account, you can start doing automated builds. A really nice thing is that AppVeyor can automatically kick off a new build when you commit code to either GitHub, VSO or other code repositories.
To automatically upload your extension to vsixgallery.com when the build has succeeded, all you have to do is to add an appveyor.yml file to the root of your repository. The content of the file should look like this:
appveyor.yml example
version: 1.0.{build}
install:
- ps: (new-object Net.WebClient).DownloadString("https://raw.github.com/madskristensen/ExtensionScripts/master/AppVeyor/vsix.ps1") | iex
before_build:
- ps: Vsix-IncrementVsixVersion | Vsix-UpdateBuildVersion
build_script:
- msbuild /p:configuration=Release /p:DeployExtension=false /p:ZipPackageCompressionLevel=normal /v:m
after_test:
- ps: Vsix-PushArtifacts | Vsix-PublishToGallery
You might want to check out these real-world uses:
Managing your extension
Every extension has a manage page at https://www.vsixgallery.com/extension/<id>/manage where you (the publisher) can delete it. Access is gated by a per-extension manage token that acts as a password. The token is a soft “don’t let randos delete my listing” speed bump — not strong authentication.
How the token gets set depends on what you do at upload time. This applies to any upload method — manual curl, PowerShell, GitHub Actions, AppVeyor, etc.
- First upload, no token supplied — the gallery auto-generates a token and embeds it in the manage URL it returns (printed by the GitHub Action’s run summary, or visible in the JSON response for manual uploads). That URL is shown only once, so save it somewhere safe.
- First upload, token supplied — the gallery stores the value you supply (typically a CI secret). The manage URL won’t contain it.
- Republish, no token supplied — the existing token stays in place. The manage URL won’t contain it.
- Republish, token supplied — whatever value you pass becomes the new manage token, replacing whatever was on file. Lost your old token? Just republish with a fresh one and the new value takes over.
With the GitHub Action, the token is supplied via the manage-token input. With curl it’s the X-Manage-Token request header. Same value either way.
Unlisted extensions
An unlisted extension is one that you need to know the direct link to, otherwise it won’t show up anywhere. The only places it will show up are:
- Direct link (example: vsixgallery.com/extension/[ID])
- Author page
- Author gallery feed
- Extension gallery feed
That means it won’t show up in the usual places on this website such as:
- Front page
- Search results
- Main gallery feed
To unlist an extension, simply add the tag “unlisted” in the .vsixmanifest file. The tags specified in the .vsixmanifest are not shown on either the Open SSMS VSIX Gallery nor inside SQL Server Management Studio, so it won’t be visible to anyone.
Here’s what it could look like:
<Tags>foo, bar, unlisted</Tags>