diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..381fbed --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,147 @@ +# Hand-written (not auto-generated). The "preview" channel — per-commit builds of main. +# +# Counterpart to the framework repo's publish-packages-preview.yml, which pushes a +# -preview prerelease to GitHub Packages on every commit to main. The extension can't +# do the same thing: GitHub Packages does not speak the VS Code gallery protocol, and +# neither marketplace accepts a semver prerelease version. So the preview channel here +# is a ROLLING GitHub pre-release whose asset is replaced on every push. +# +# Why a rolling release rather than workflow artifacts: +# - the download URL is stable, so installing is one command with no run-hunting: +# +# gh release download preview -R Fallout-build/Fallout.Extensions.VSCode -p '*.vsix' --clobber +# code --install-extension fallout.vsix +# +# or, from a clone: dotnet fallout InstallVsix +# - workflow artifacts expire and need the run ID to fetch. +# +# The tag is `preview`, deliberately NOT matching `v*`. Two consequences, both wanted: +# publish.yml (which triggers on v* only) does not fire, and the v* tag-protection +# ruleset does not apply to a tag this workflow force-moves on every push. +# +# The .vsix is marked as a marketplace pre-release, so if it is ever sideloaded next to +# a stable build VS Code shows it as pre-release rather than silently as a release. +# +# NOTE: this publishes NOTHING to any marketplace. It is the GitHub pre-stage only. +name: preview + +on: + push: + branches: + - main + paths-ignore: + - '**/*.md' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + # Never cancel in progress: a cancelled run could leave the rolling release holding a + # half-uploaded asset. Queue instead, so the newest push wins by finishing last. + group: preview + cancel-in-progress: false + +jobs: + preview: + name: preview + runs-on: ubuntu-latest + permissions: + contents: write + environment: + name: github-releases + url: https://github.com/Fallout-build/Fallout.Extensions.VSCode/releases/tag/preview + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 # Nerdbank.GitVersioning needs full history + - uses: actions/setup-node@v6 + with: + node-version: 20 + cache: npm + - uses: actions/setup-dotnet@v6 + with: + global-json-file: global.json + + # PublicRelease: main IS in version.json's publicReleaseRefSpec, so this is only + # belt-and-braces for the workflow_dispatch case on a detached checkout. + - name: 'Fallout build (PackVsix)' + run: ./build.sh PackVsix + env: + PreRelease: true + PublicRelease: true + + # Per-run copy, alongside the rolling release asset below. The release is the thing + # you install from; this is the fixed record of what a specific commit produced, + # which the rolling asset can't be since it is replaced on the next push. + - uses: actions/upload-artifact@v7 + with: + name: fallout-vsix + path: fallout.vsix + retention-days: 14 + if-no-files-found: error + + - name: 'Read the packaged version' + id: version + run: | + set -euo pipefail + # Take it from the VSIX manifest rather than recomputing — this reports what was + # actually built, so a version bug shows up here instead of being masked. + # + # Scoped to the element on purpose: the FIRST Version= attribute in the + # manifest is , the manifest schema version, so a + # naive "first match" reports 2.0.0 for every build. + unzip -p fallout.vsix extension.vsixmanifest > manifest.xml + VERSION=$(grep -o ']*' manifest.xml | grep -o 'Version="[^"]*"' | cut -d'"' -f2) + if [ -z "$VERSION" ]; then + echo "::error::Could not read the version from the VSIX manifest." + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Packaged version: $VERSION" + + - name: 'Update the rolling preview release' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.version.outputs.version }} + run: | + set -euo pipefail + + NOTES=$(cat < /dev/null 2>&1; then + gh release edit preview --target "$GITHUB_SHA" --notes "$NOTES" --prerelease + gh release upload preview fallout.vsix --clobber + else + gh release create preview \ + --title 'Preview (rolling)' \ + --target "$GITHUB_SHA" \ + --notes "$NOTES" \ + --prerelease \ + fallout.vsix + fi diff --git a/RELEASING.md b/RELEASING.md index f3fe702..41f17d9 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -32,12 +32,39 @@ GitHub is the pre-stage; each marketplace is a promotion target with its own env | Channel | Trigger | Gating | |---|---|---| +| `preview` (rolling) | every push to `main` | none | | `github-releases` | any release tag | none | | `vs-marketplace` | `workflow_dispatch` opt-in flag | flag + approval | | `open-vsx` | `workflow_dispatch` opt-in flag | flag + approval | A tag push **never** reaches a marketplace. Promotion is deliberate: set the flag, then approve the environment — two independent layers, matching how Fallout gates nuget.org. +## The preview channel + +Every push to `main` builds a `.vsix` and replaces the asset on a rolling `preview` GitHub pre-release (`preview.yml`). It's the counterpart to the framework's per-commit `-preview` packages — reshaped because GitHub Packages doesn't speak the VS Code gallery protocol and neither marketplace accepts a semver prerelease. + +The `preview` tag deliberately doesn't match `v*`, so it neither triggers `publish.yml` nor falls under the `v*` tag-protection ruleset — which matters, because the workflow force-moves it on every push. + +### Installing a preview + +```bash +gh release download preview -R Fallout-build/Fallout.Extensions.VSCode -p '*.vsix' --clobber +code --install-extension fallout.vsix --force +``` + +The download URL is stable, so that pair of commands is the whole update story — no run IDs to hunt, no expiry. Each run also uploads the same `.vsix` as a workflow artifact, which is the fixed record of what a given commit produced; the rolling asset can't be, since the next push replaces it. + +Building locally, `dotnet fallout PackVsix` produces the `.vsix` and you install it the same way. Installing is deliberately a manual step — the build never touches your editor. + +### Why this isn't auto-updating + +VS Code will not auto-update a manually installed extension — it only tracks versions for extensions it got from a gallery, and this channel has no gallery. Manual download-and-install is the deliberate trade-off; the build never touches your editor. + +Two ways to get real automatic updates, if that ever becomes worth the cost: + +- **Marketplace pre-release channel.** The native mechanism: VS Code offers *"Switch to Pre-Release Version"* and updates it like anything else. Requires an actual marketplace presence. Note the version-burning concern doesn't apply to a preview stream — the patch is a git height, so every build has a unique number and stable is always a later height. +- **A self-hosted gallery at `gallery.fallout.build`** — tracked as [#6](https://github.com/Fallout-build/Fallout.Extensions.VSCode/issues/6), with hosting options and trade-offs in [Chrison-Homelab/Homelab#409](https://github.com/Chrison-Homelab/Homelab/issues/409). Candidates are Microsoft's own [Private Marketplace](https://github.com/microsoft/vsmarketplace/blob/main/privatemarketplace/latest/README.md) (stateless container, configured by supported device-management policy, but every consumer needs a Copilot Business/Enterprise seat), `coder/code-marketplace`, or self-hosted Open VSX. Since registries are already modelled as data in `IPublishVsix`, adding one is a target entry rather than a new pipeline. + ## Cutting a release candidate ```bash diff --git a/plugins/Fallout.Vsce/README.md b/plugins/Fallout.Vsce/README.md index 76ac697..9f43957 100644 --- a/plugins/Fallout.Vsce/README.md +++ b/plugins/Fallout.Vsce/README.md @@ -45,6 +45,7 @@ dotnet fallout PublishVsix --publish-vsix-to open-vsx - **`ovsx` ignores `--pre-release` for a prepackaged `.vsix`** — it reads the manifest. Package it correctly; don't rely on the publish flag. - **`vsce publish --pre-release` on a `--packagePath` is only an assertion** against the package, not what sets the status. - **Tool resolution** prefers `node_modules/.bin` over `PATH`, since both CLIs are conventionally dev dependencies. Override via `IHasVsix.VsceToolPath` / `OvsxToolPath`. +- **Installing is out of scope on purpose.** This plugin packages and publishes; it does not touch the local editor. A manually installed `.vsix` never auto-updates anyway, since VS Code only tracks versions for extensions it got from a gallery. - **Tokens** are left to the CLIs' own environment variables (`VSCE_PAT`, `OVSX_PAT`) unless you set `VsixPublishTarget.Pat`, so they stay out of process argument lists. ## Status