Skip to content
Open
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
32 changes: 32 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: 2
updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'weekly'
cooldown:
default-days: 12
groups:
npm-dependencies:
applies-to: version-updates
patterns:
- '*'
npm-security:
applies-to: security-updates
patterns:
- '*'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'
cooldown:
default-days: 12
groups:
github-actions:
applies-to: version-updates
patterns:
- '*'
github-actions-security:
applies-to: security-updates
patterns:
- '*'
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Use Node.js 22
uses: actions/setup-node@v4
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.x
cache: 'npm'
Expand Down Expand Up @@ -53,4 +53,4 @@ jobs:
run: npm run memory:check

- name: Package extension
run: npm run package
run: npm run ext:package -- --out "${{ runner.temp }}/barrel-roll.vsix"
90 changes: 90 additions & 0 deletions .github/workflows/publish-vscode.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Publish to Visual Studio Marketplace

on:
workflow_dispatch:
inputs:
tag:
description: Existing release tag (vMAJOR.MINOR.PATCH or vMAJOR.MINOR.PATCH_COMMIT)
required: true
type: string

permissions:
contents: read

concurrency:
group: publish-vscode
cancel-in-progress: false

jobs:
publish:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
timeout-minutes: 20
env:
RELEASE_TAG: ${{ inputs.tag }}
steps:
- name: Validate release tag format
run: |
if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(_[0-9a-f]{7,40})?$ ]]; then
echo 'Expected a stable release tag, optionally suffixed with its commit hash.' >&2
exit 1
fi

- name: Check out release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: refs/tags/${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.x
package-manager-cache: false

- name: Verify release commit and package metadata
run: |
git merge-base --is-ancestor HEAD origin/main
node <<'NODE'
const assert = require('node:assert/strict');
const { execFileSync } = require('node:child_process');
const manifest = require('./package.json');
const lockfile = require('./package-lock.json');
const [version, commit] = process.env.RELEASE_TAG.slice(1).split('_');
assert.equal(version, manifest.version, 'Tag must match package version');
assert.equal(lockfile.version, version, 'Lockfile version differs');
assert.equal(lockfile.packages[''].version, version, 'Lockfile root version differs');
assert.equal(manifest.publisher, 'coderrob', 'Unexpected Marketplace publisher');
assert.equal(manifest.name, 'barrel-roll', 'Unexpected extension name');
assert.ok(manifest.devDependencies['@vscode/vsce'], 'Tag must include the locked vsce dependency');
if (commit) {
const sha = execFileSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf8' }).trim();
assert.ok(sha.startsWith(commit), 'Tag commit suffix must match the release commit');
}
NODE

- name: Install locked dependencies
run: npm ci

- name: Run quality and type checks
run: npm run quality && npm run typecheck

- name: Verify coverage and memory retention
run: npm run coverage:verify && npm run memory:check

- name: Inspect package contents
run: npm exec --no -- vsce ls

- name: Build release VSIX
run: npm run ext:package -- --out "$RUNNER_TEMP/barrel-roll.vsix"

- name: Publish the validated VSIX
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
run: |
if [[ -z "$VSCE_PAT" ]]; then
echo 'Configure the VSCE_PAT repository Actions secret with a Marketplace publishing token. See README.md.' >&2
exit 1
fi
npm exec --no -- vsce publish --packagePath "$RUNNER_TEMP/barrel-roll.vsix"
20 changes: 8 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.x
cache: 'npm'
Expand All @@ -36,12 +36,14 @@ jobs:
release:
needs: test
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Use Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.x
cache: 'npm'
Expand All @@ -52,17 +54,11 @@ jobs:
- name: Check for unused dependencies
run: npm run deps:check

- name: Package extension
run: npm run package

- name: Install vsce
run: npm install -g @vscode/vsce

- name: Package VSIX
run: vsce package
run: npm run ext:package

- name: Create Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
with:
files: '*.vsix'
generate_release_notes: true
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20
22
27 changes: 26 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ This file documents automation, scripts, CI checks, and packaging conventions us
- Behavior: runs `depcheck` programmatically, writes `.depcheck.json`, filters references found in repository files/scripts, and exits non-zero when unused packages remain.
- Rationale: avoids `npx` platform inconsistencies and keeps local/CI behavior aligned.

## Automated dependency updates

- Configuration: `.github/dependabot.yml`.
- Dependabot checks npm packages and GitHub Actions weekly, grouping all version updates into one PR
per ecosystem.
- Routine updates wait until a release is at least 12 days old (`cooldown.default-days: 12`).
- Security updates use separate groups per ecosystem and bypass the version-update cooldown.
- Pin GitHub Actions to immutable commit hashes with release-version comments. Manual dependency
refreshes must also respect the 12-day release age and existing peer/API compatibility constraints.
- When consolidating existing PRs, preserve their lockfile updates, validate the combined changes,
and close superseded PRs only after the replacement PR exists.

## Testing conventions

- Unit test titles must start with `should` (enforced by ESLint).
Expand Down Expand Up @@ -82,6 +94,19 @@ All unit tests must follow the FAST principle:
- Install packaged VSIX: `npm run ext:install`
- Quick reinstall: `npm run ext:reinstall`

### Marketplace publishing

- `@vscode/vsce` is a locked development dependency. `npm run ext:package` uses its local CLI;
do not replace it with an unpinned global installation.
- CI creates a real VSIX on Linux, Windows, and macOS. The tag-based Release workflow uses the same
command to attach a VSIX to the GitHub release.
- `.github/workflows/publish-vscode.yml` is manually dispatched from `main` with an existing release
tag. It verifies ancestry on `main`, extension identity, package/lockfile versions, and any commit
suffix, then runs quality, coverage, memory, and packaging checks before publishing that VSIX.
- Authentication uses the `VSCE_PAT` repository Actions secret with a Marketplace publishing token
authorized for `coderrob` and scoped to Marketplace **Manage**. Expose it only to the publish step
as an environment variable. See the README for setup.

### Publish allowlist

`.vscodeignore` uses an allowlist strategy. Intended packaged files are:
Expand All @@ -92,7 +117,7 @@ All unit tests must follow the FAST principle:
- `dist/extension.js`
- `public/img/barrel-roll-icon.png`

Verify with `npx @vscode/vsce ls` before publishing.
Verify with `npm exec --no -- vsce ls` before publishing.

### Packaging note

Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

All notable changes to the "barrel-roll" extension will be documented in this file.

## [Unreleased]

### Added

- A manually dispatched Visual Studio Marketplace publishing workflow using the locked `vsce` CLI
and a Marketplace publishing token stored in the `VSCE_PAT` Actions secret.
- VSIX packaging checks on Linux, Windows, and macOS.

## [1.1.2] - 2026-08-08

### Added
Expand Down
27 changes: 27 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Thanks for contributing.

## Development setup

Use Node.js 22.22.2+ (or 24.15+) with npm 10+. `.nvmrc` selects the Node.js 22 line used by CI.

1. Fork and clone:

```bash
Expand Down Expand Up @@ -47,6 +49,16 @@ npm run coverage:verify
npm run memory:check
```

## Dependency updates

Dependabot checks npm packages and GitHub Actions weekly using `.github/dependabot.yml`. Version
updates are grouped into one PR per ecosystem and wait until releases are at least 12 days old.
Security updates are grouped separately per ecosystem and bypass this cooldown.

Review the combined dependency changes and require CI to pass before merging. When consolidating
existing dependency PRs, include their lockfile updates in the replacement and close superseded PRs
only after the replacement PR exists.

## Code style and quality

Run `npm run quality` before submitting changes. It includes formatting and ESLint checks,
Expand Down Expand Up @@ -90,6 +102,21 @@ To clean up ad-hoc error checks:
npm run lint:fix
```

## Marketplace releases

`npm run ext:package` uses the locked local `@vscode/vsce` and builds the production bundle through
`vscode:prepublish`. CI verifies VSIX creation on every supported platform. Inspect its file list with
`npm exec --no -- vsce ls` before publishing.

To publish, merge the release commit, tag it with the package version (`vMAJOR.MINOR.PATCH`, optionally
followed by `_COMMIT`), then run **Publish to Visual Studio Marketplace** from `main` with that tag.
The tag must include the locked CLI dependency; its version, lockfile, ancestry, identity, and any
commit suffix must agree. The workflow reruns validation and publishes the resulting VSIX.

Configure the `VSCE_PAT` repository Actions secret with a token authorized to publish as `coderrob`
and scoped to Marketplace **Manage**, using the README's setup instructions. The workflow passes it
to `vsce` only during publishing. Packaging and validation run locally without the token.

## Commit and PR guidance

- Use conventional prefixes when possible: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`
Expand Down
33 changes: 31 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export type { UserData } from './user.js';

### Prerequisites

- Node.js 22.13+
- Node.js 22.22.2+ (or 24.15+)
- npm 10+

### Common commands
Expand Down Expand Up @@ -134,6 +134,13 @@ duplicate code, and TypeScript circular/orphan modules.

`npm run deps:check` uses `scripts/run-depcheck.cjs` (programmatic depcheck runner). It writes `.depcheck.json`, filters known script/repository references, and fails when unused dependencies remain.

### Automated dependency updates

Dependabot checks npm packages and GitHub Actions weekly and groups version updates into one PR per
ecosystem. Routine updates wait until releases are at least 12 days old to reduce exposure to newly
published packages. Security updates are grouped separately and bypass this cooldown. See
[`.github/dependabot.yml`](.github/dependabot.yml) for the policy.

### Packaging and release

```bash
Expand All @@ -160,7 +167,29 @@ Publishing uses an allowlist in `.vscodeignore` so only runtime assets are inten
- `dist/extension.js`
- `public/img/barrel-roll-icon.png`

Use `npx @vscode/vsce ls` to inspect final package contents before publishing.
Use `npm exec --no -- vsce ls` to inspect final package contents before publishing. `@vscode/vsce`
is locked as a development dependency, and CI validates real VSIX packaging on all three platforms.

### Publish to Visual Studio Marketplace

After merging a release commit, create a tag matching the extension version, such as `v1.1.3`, or
the existing `v1.1.3_COMMIT` convention with its matching 7–40 character lowercase commit hash.
The tagged commit must be reachable from `main` and include the locked `@vscode/vsce` dependency.

Run **Publish to Visual Studio Marketplace** from the Actions tab, select `main`, and enter that tag.
The workflow checks tag/version/identity agreement, runs quality, type, coverage, and memory checks,
then builds and publishes the validated VSIX with `vsce`. The tag-triggered **Release** workflow
continues to attach VSIX files to GitHub releases.

Set up the publishing token once:

1. Create a [Marketplace publishing token](https://code.visualstudio.com/api/working-with-extensions/publishing-extension#get-a-personal-access-token)
using an account authorized to publish under `coderrob`, with the Marketplace **Manage** scope.
1. In this repository's **Settings → Secrets and variables → Actions**, add a repository secret named
`VSCE_PAT` containing that token.

The workflow passes `VSCE_PAT` only to the final publish step. `vsce` reads that environment variable
and publishes the validated VSIX as `coderrob.barrel-roll`. Rotate the secret when the token expires.

## Known limitations

Expand Down
Loading
Loading