Skip to content

Ship the vsce toolchain as a Fallout plugin, and align the release pipeline with the framework - #3

Merged
ChrisonSimtian merged 7 commits into
mainfrom
feature/vsce-plugin-and-release-pipeline
Aug 13, 2026
Merged

Ship the vsce toolchain as a Fallout plugin, and align the release pipeline with the framework#3
ChrisonSimtian merged 7 commits into
mainfrom
feature/vsce-plugin-and-release-pipeline

Conversation

@ChrisonSimtian

Copy link
Copy Markdown
Contributor

Targets Fallout 10.4.0 GA, moves the vsce/ovsx toolchain into a Fallout plugin, and brings changelog, versioning and release channels in line with the framework repo.

Fallout.Vsce — the toolchain as a plugin

plugins/Fallout.Vsce is the pilot for shipping provider integrations as plugins rather than in-tree components, the shape ADR-0001 proposes for Octopus. It's the smaller sibling that proves it first.

  • Tool wrappers (VsceTasks, OvsxTasks) in the exact form Tooling.Generator emits — ToolTasks + [Command] ToolOptions + [Argument] properties + fluent [Builder] setters.
  • IPackVsix / IPublishVsix mirroring Fallout.Components' IPack / IPublish; registries are declared as data (VsixPublishTarget, the VSIX analogue of PublishTarget) and narrowed with --publish-vsix-to. Adding Open VSX was a list entry, not a new target.

The plugin shape holds up. Nothing needed an escape hatch into framework internals. Two things made it work, both worth knowing for the Octopus plugin: Fallout.SourceGenerators ships as an analyzer inside Fallout.Common, so a downstream project gets the codegen for free; and ToolOptions.ProcessToolPath is the right seam for tools that live in node_modules/.bin rather than on PATH. It depends on Fallout.Common alone and is packable, so extracting it to its own feed is a build-file change.

Versioning

Nerdbank.GitVersioning over version.json, as in the framework repo, replacing a hand-rolled parse of the pinned assembly's informational version. The build now asserts the declared line still matches the referenced Fallout.Common, so major.minor remains an enforced promise about which framework line the extension targets rather than a hopeful comment.

MarketplaceVersion holds the rule neither NB.GV nor the registries enforce: three integers, no prerelease.

Release channels

validate-ref → pack → three channel jobs, each bound to its own GitHub Environment, mirroring publish-packages-release.yml.

Channel Trigger Gating
github-releases any release tag none — the pre-stage
vs-marketplace dispatch opt-in flag flag + approval
open-vsx dispatch opt-in flag flag + approval

A tag push can no longer reach a marketplace; promotion needs the flag and an approval, the two layers Fallout puts in front of nuget.org. Promotion publishes the exact packed artifact (--skip PackVsix), so what's approved is what ships.

Why release candidates aren't -rc.N

Both registries take exactly three integers — vsce throws The VS Marketplace doesn't support prerelease versions. An RC is an ordinary triple carrying a pre-release bit in the VSIX manifest, with -rc.N living only on the tag. Since a version is pre-release or stable and never both, publishing an RC to a marketplace would burn that number for the GA — so RCs stop at GitHub.

Changelog

.github/release.yml mirrors the framework's label taxonomy; --generate-notes turns PR labels into release notes. CHANGELOG.md is removed: it duplicated the labels, and couldn't state its own version correctly since the patch is a git height not settled until the release is cut.

Also

  • Generated PR gate. .github/workflows/build.yml is emitted from [GitHubActions]. The repo previously ran nothing on a PR. publish.yml stays hand-written — the generator can't set up Node or express per-channel environment gating — matching how the framework splits generated CI from hand-written publish workflows.
  • Repo renamed to Fallout.Extensions.VSCode. Verified nothing upstream depends on the old name: vsce reads the repository URL from package.json, and extension identity is publisher.name.
  • Dropped FALLOUT_PACKAGES_TOKEN and the private feed — Fallout 10.4.0 restores from nuget.org.

Bugs this surfaced

  • .vscodeignore didn't exclude plugins/ — a package built after adding it shipped 30 files of C# source to the marketplace.
  • .gitignore only covered build/bin|obj, so plugin binaries were staged.
  • package.json hardcoded a version next to a computed one. Now 0.0.0; the build stamps the real one.

Verification

Packaged locally, stable and RC: 10.4.18, 12 files, Microsoft.VisualStudio.Code.PreRelease="true" present only on the RC. --publish-vsix-to and --skip PackVsix both parse. Nothing has been published to either marketplace.

🤖 Generated with Claude Code

ChrisonSimtian and others added 7 commits August 12, 2026 09:00
Fallout 10.4.0 shipped to nuget.org, so the dogfood build no longer needs
the private GitHub Packages feed or its token:

- Pin Fallout.Common and the CLI tool to 10.4.0. The tool's package id
  changed on GA (fallout.globaltools -> fallout.globaltool).
- Drop the github package source, its credentials, and the source mapping
  from nuget.config; nuget.org is now the only feed. Verified that
  Fallout.Common 10.4.0 resolves from api.nuget.org.
- Drop FALLOUT_PACKAGES_TOKEN from the publish workflow.

Nerdbank.GitVersioning stamps stable builds with a four-component version
(10.4.0.15+f16e0f1441), so FrameworkVersion() now picks the height out of
the fourth component rather than assuming a prerelease counter. Returning
the release verbatim would emit 10.4.0.15, which is not valid semver and
fails marketplace validation.

The extension version therefore moves from calendar versioning to tracking
the framework release line: 10.4.15 against Fallout 10.4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The VS Marketplace rejects semver prerelease versions outright, so an RC
cannot be expressed as 10.4.15-rc.1. Instead an RC is an ordinary
three-integer version whose VSIX manifest carries a pre-release bit
(Microsoft.VisualStudio.Code.PreRelease), set at package time. The rc
suffix lives only on the git tag and the GitHub release.

- Add a PreRelease build parameter, independent of the framework's own
  version state so the extension can cut an RC while Fallout is GA. It
  ORs with the framework-derived prerelease flag.
- Split the workflow into package / prerelease / publish. An rc tag now
  routes to a GitHub pre-release with the .vsix attached and never
  reaches a marketplace, which the previous 'v*' trigger would have done.
- Narrow the tag patterns so a stable tag and an rc tag cannot match each
  other's pattern, and anything else triggers nothing at all.
- Gate the stable publish on the 'marketplace' environment, so a tag push
  alone cannot publish without an approval.

Verified locally: PreRelease=true packages 10.4.15 with
Microsoft.VisualStudio.Code.PreRelease="true" in extension.vsixmanifest;
the default still packages it as a stable release.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Emitted by the Fallout build itself when the parameter was added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two changes that belong together, because the plugin is what makes the
version mapping expressible.

plugins/Fallout.Vsce — the vsce/ovsx toolchain as a Fallout plugin rather
than an in-tree framework component. This is the pilot for the plugin
shape ADR-0001 sketches for Octopus: tool wrappers in the same form the
Tooling.Generator emits (ToolTasks + [Command] ToolOptions + [Argument]
properties + fluent [Builder] setters), plus IPackVsix / IPublishVsix
components mirroring Fallout.Components' IPack / IPublish. Registries are
declared as data (VsixPublishTarget, the VSIX analogue of PublishTarget)
and narrowed per-run with --publish-vsix-to, so adding Open VSX was a list
entry rather than a new target.

Versioning now matches the framework: Nerdbank.GitVersioning over
version.json, replacing the hand-rolled parse of the pinned assembly's
informational version. version.json pins the 10.4 line and the build
asserts it still matches the referenced Fallout.Common, so the two cannot
drift apart silently — the extension's major.minor is a documented promise
about which framework line it targets.

MarketplaceVersion holds the one rule neither NB.GV nor the marketplaces
will enforce for us: three integers, no prerelease. It is where the
four-component stable-build case and the rc case both get normalised.

Also fixes two things this surfaced:
- .vscodeignore did not exclude plugins/, so a package built after adding
  it shipped 30 files of C# source and build output to the marketplace.
- package.json's version is now 0.0.0. The build stamps the real one at
  package time, and a hardcoded number next to it was a second source of
  truth guaranteed to drift. The npm package/publish scripts are gone for
  the same reason — the plugin owns that path now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Brings the pipeline in line with the framework repo so both are read with
one mental model.

Changelog by PR label: .github/release.yml mirrors Fallout's taxonomy
(breaking-change / enhancement / bug / security / dependencies /
documentation, plus skip-changelog), and releases are cut with
--generate-notes so the label on a PR is what lands in the notes. The four
missing labels now exist on the repo with the same colours. CHANGELOG.md
stays hand-written and separate: it ships inside the .vsix and is what the
marketplace renders on the extension page.

Channels as environments, mirroring publish-packages-release.yml:
validate-ref -> pack -> three channel jobs, each in its own environment.
GitHub Releases is the ungated pre-stage; vs-marketplace and open-vsx are
promotion targets behind an opt-in workflow_dispatch flag AND an approval
gate, the same two layers Fallout puts in front of nuget.org. A tag push
can no longer reach a marketplace at all, which the previous single-gate
shape allowed. The old 'marketplace' environment is replaced by the three.

The promotion jobs publish the exact artifact the pack job produced
(--skip PackVsix) rather than rebuilding, so what gets approved is what
ships. The workflow_dispatch boolean is compared against both true and
'true' — the REST API can only send strings, and a bare == true would
silently skip every CLI-triggered promotion while reporting success.

RELEASING.md documents the runbook, including why a release candidate is
not -rc.N and why RCs deliberately stop at GitHub: publishing a version as
a marketplace pre-release burns that number for the GA.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ding

Fallout.Components was referenced by both projects but appeared only in doc
comments — the plugin's components mirror its shape without depending on it,
so a consumer can take Fallout.Vsce without pulling that package in.

CHANGELOG.md's top section is now "## Unreleased". The patch component is a
git height, so any commit touching a filtered path moves it; hardcoding a
number in a file that is itself a filtered path meant the heading was stale
the moment it was written. It gets renamed when the release is cut, which
RELEASING.md now spells out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Generated CI gate: .github/workflows/build.yml is now emitted from a
[GitHubActions] attribute in build/Build.CI.GitHubActions.cs. The repo had
no PR gate at all before this — nothing ran on a pull request.

publish.yml stays hand-written, for the same category of reason the
framework repo hand-writes publish-packages-release.yml. Two specifics: the
generator emits checkout/cache/setup-dotnet and has no hook for extra
steps, so it cannot set up Node with npm caching; and it emits one job per
image, with no notion of the per-channel fan-out into GitHub Environments
with approval gates that the release path needs. The gate can live with the
runner's preinstalled Node; a release cannot. Both files only provision
toolchains and route channels — every step that does anything is a Fallout
target, so the build itself is defined in C# either way.

Dropped CHANGELOG.md. The label taxonomy already records what changed, and
a hand-maintained file could not state its own version correctly: the patch
is a git height, so the number is not settled until the release is cut, and
the changelog commit itself moves it. If the marketplace page ever wants a
rendered changelog it should be generated into the .vsix at pack time.

Renamed the repo to Fallout.Extensions.VSCode. Verified nothing upstream
depends on the old name: vsce reads the repository URL from package.json
(for the marketplace link and for resolving relative README assets), never
from the GitHub repo name, and extension identity is publisher.name —
fallout.fallout. Unlike a Homebrew tap, neither registry constrains it.
package.json, the environment URLs and PackageProjectUrl are updated.

Action versions aligned with the rest of the org (checkout@v7, cache@v6,
setup-node@v6, setup-dotnet@v6, upload-artifact@v7, download-artifact@v8).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ChrisonSimtian ChrisonSimtian added the enhancement New feature or request label Aug 13, 2026
@ChrisonSimtian
ChrisonSimtian merged commit 1491dec into main Aug 13, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant