Skip to content

Use PR-specific version for dogfood VS Code extension#15892

Open
adamint wants to merge 3 commits intomicrosoft:mainfrom
adamint:fix/extension-pr-version
Open

Use PR-specific version for dogfood VS Code extension#15892
adamint wants to merge 3 commits intomicrosoft:mainfrom
adamint:fix/extension-pr-version

Conversation

@adamint
Copy link
Copy Markdown
Member

@adamint adamint commented Apr 4, 2026

Description

When installing the VS Code extension from a PR dogfood build, the version is always the same as the marketplace release (currently 1.0.7 from package.json), making it impossible to distinguish a dogfooded extension from a released one.

This PR sets the extension VSIX version to 0.0.<PR_NUMBER> for PR builds, so dogfooded extensions are clearly distinguishable from marketplace releases. The version 0.0.X was chosen because:

  • VS Code extensions don't support semver pre-release labels in version strings
  • 0.0.X is obviously non-release (major=minor=0)
  • The PR number identifies which PR the extension came from

Changes

  1. ci.yml: Compute EXTENSION_VERSION_OVERRIDE=0.0.<PR_NUMBER> alongside the existing VERSION_SUFFIX_OVERRIDE and pass it to tests.yml
  2. tests.yml: Accept extensionVersionOverride input and pass --version to vsce package when set
  3. Extension.proj: Support ExtensionVersionOverride MSBuild property for local builds (dotnet build Extension.proj /p:ExtensionVersionOverride=0.0.99)

Non-PR builds (push to main/release) continue using the version from package.json.

Fixes #15589

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
    • No

For PR builds, set the extension VSIX version to 0.0.<PR_NUMBER> so
dogfooded extensions are clearly distinguishable from marketplace
releases. The version override flows from ci.yml through tests.yml to
the vsce package command.

Also support ExtensionVersionOverride MSBuild property in Extension.proj
for local builds (e.g. dotnet build /p:ExtensionVersionOverride=0.0.99).

Fixes microsoft#15589
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 15892

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 15892"

vsce interprets --version as 'print tool version'. The package version
must be passed as a positional argument: vsce package <version>.
@adamint adamint marked this pull request as ready for review April 4, 2026 04:27
@adamint adamint requested review from Copilot and radical April 4, 2026 04:28
@adamint adamint self-assigned this Apr 4, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the VS Code extension build/packaging pipeline so PR dogfood builds can use a PR-specific extension version (intended as 0.0.<PR_NUMBER>) instead of the Marketplace package.json version, making dogfooded builds distinguishable.

Changes:

  • Add an EXTENSION_VERSION_OVERRIDE output in ci.yml for PR runs and pass it into the reusable tests.yml workflow.
  • Add a extensionVersionOverride input to tests.yml and thread it into the VSIX packaging command.
  • Add an ExtensionVersionOverride MSBuild property to extension/Extension.proj to influence VSIX artifact naming and intended packaging behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
.github/workflows/ci.yml Computes and forwards an extension version override for PR builds.
.github/workflows/tests.yml Accepts the override input and attempts to pass it to vsce package during VSIX creation.
extension/Extension.proj Adds an override property intended to affect the packaged VSIX version and output artifact naming.

vsce package does not have a --version flag for setting the package
version (--version prints the tool version). The [version] positional
arg triggers npm version bump semantics, not an explicit version set.

Instead, use 'npm version --no-git-tag-version' in CI and 'node -e'
in Extension.proj to update package.json before packaging.
Write-Host "Computed VERSION_SUFFIX_OVERRIDE=$VERSION_SUFFIX"
"VERSION_SUFFIX_OVERRIDE=$VERSION_SUFFIX" | Out-File -FilePath $Env:GITHUB_OUTPUT -Append -Encoding utf8

$EXTENSION_VERSION = "0.0.$($Env:PR_NUMBER)"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if someone already has the marketplace build installed and then dogfoods this PR? VS Code's pre-release docs say it always updates extensions to the highest version available, and it explicitly recommends keeping pre-release versions numerically above the release track. Because this resets the PR build to 0.0.<PR>, any existing Aspire release like 1.0.7 outranks it, so brownfield users can be moved back to stable even though they intentionally installed the PR extension. That makes the dogfood path unreliable for existing users and conflicts with the sibling VS Code contract for pre-release versioning. Likely fix: derive the PR version from the current release line and keep it numerically above stable (for example, an odd/even track), rather than forcing 0.0.x.

</PropertyGroup>

<!-- Override package.json version if ExtensionVersionOverride is set -->
<Exec Command="node -e &quot;const p=require('./package.json');p.version='$(ExtensionVersionOverride)';require('fs').writeFileSync('./package.json',JSON.stringify(p,null,2)+'\n')&quot;"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if a maintainer runs the documented local command dotnet build Extension.proj /p:ExtensionVersionOverride=0.0.99 and then does a normal build from the same checkout? This step rewrites the tracked extension/package.json in place and never restores it. After that the tree stays dirty, and the next non-override build reads 0.0.99 back as the source version, so it keeps producing dogfood-versioned VSIXes until the file is manually reset. That's a hidden contract violation in the new local-build path, not just a cosmetic diff. Likely fix: package from a temp/staging copy or restore the original package.json after vsce runs.

Copy link
Copy Markdown
Member

@JamesNK JamesNK left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two low-severity hardening suggestions around input interpolation in shell/JS contexts. The core logic looks correct.

run: npm test
- name: Override extension version for PR builds
if: ${{ inputs.extensionVersionOverride != '' }}
run: npm version "${{ inputs.extensionVersionOverride }}" --no-git-tag-version
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low-risk expression injection: ${{ inputs.extensionVersionOverride }} is interpolated directly into the run: shell script. While the source (github.event.number) is GitHub-controlled, direct interpolation in run: blocks is a documented anti-pattern. Assigning to an env var first avoids the class of issue entirely:

- name: Override extension version for PR builds
  if: ${{ inputs.extensionVersionOverride != '' }}
  env:
    EXTENSION_VERSION: ${{ inputs.extensionVersionOverride }}
  run: npm version "$EXTENSION_VERSION" --no-git-tag-version

</PropertyGroup>

<!-- Override package.json version if ExtensionVersionOverride is set -->
<Exec Command="node -e &quot;const p=require('./package.json');p.version='$(ExtensionVersionOverride)';require('fs').writeFileSync('./package.json',JSON.stringify(p,null,2)+'\n')&quot;"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low-risk command injection: $(ExtensionVersionOverride) is interpolated directly into a node -e JavaScript string literal. A value containing a single quote (') would break the JS syntax and could inject arbitrary Node code — e.g., /p:ExtensionVersionOverride="a';process.exit(1);//". The input is developer-controlled so the practical risk is low, but it can be hardened by passing the version via an environment variable:

<Exec Command="node -e &quot;const p=require('./package.json');p.version=process.env.EXT_VER;require('fs').writeFileSync('./package.json',JSON.stringify(p,null,2)+'\n')&quot;"
      EnvironmentVariables="EXT_VER=$(ExtensionVersionOverride)"
      Condition="'$(ExtensionVersionOverride)' != ''"
      WorkingDirectory="$(ExtensionSrcDir)" />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dogfood VS Code extension version should indicate PR hive

4 participants