Skip to content
Merged
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
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ permissions:
contents: read

jobs:
secret-scan:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0

- name: Gitleaks
uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

build:
runs-on: ubuntu-latest
steps:
Expand All @@ -24,8 +39,24 @@ jobs:
- name: Install dependencies
run: npm ci

# SCA gate: fails on high/critical CVEs in the dependency tree. Kept in step
# with wfb_dep_update.sh's audit gate, but runs on every push/PR instead of
# only when that script is invoked manually.
- name: Audit dependencies
run: npm audit --audit-level=high

- name: Lint
run: npm run lint

- name: Test with coverage
run: npm run test:coverage

- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
fail_ci_if_error: false

- name: Build
run: npm run build
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

- uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
- uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4
with:
languages: javascript-typescript
queries: security-and-quality

- uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4
- uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4
with:
category: /language:javascript-typescript
4 changes: 2 additions & 2 deletions .github/workflows/promote-dev-to-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ name: Promote development to main

on:
schedule:
- cron: '23 12 * * 1' # Mondays ~12:23 UTC, off the top of the hour
- cron: '30 12 * * 1' # Mondays ~12:30 UTC, off the top of the hour
workflow_dispatch:
inputs:
reason:
description: 'Why are you running this manually?'
required: true
default: 'Ad-hoc promotion request'
default: 'Feature Release'
skip_health_check:
description: 'Skip the CI health check on development?'
required: false
Expand Down
30 changes: 23 additions & 7 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
attestations: write
env:
VS_MARKETPLACE_TOKEN: ${{ secrets.VS_MARKETPLACE_TOKEN }}
OPEN_VSX_TOKEN: ${{ secrets.OPEN_VSX_TOKEN }}
Expand Down Expand Up @@ -84,6 +86,19 @@ jobs:
- name: Package extension
run: npx @vscode/vsce package

# CycloneDX SBOM covering every prod dependency in the packaged .vsix, for
# downstream vulnerability/license auditing without re-resolving the tree.
- name: Generate SBOM
run: npx @cyclonedx/cyclonedx-npm --ignore-npm-errors --output-file sbom.cdx.json

# Cryptographically binds the .vsix and its SBOM to this workflow run and
# commit (Sigstore-backed, keyless — no secrets to manage). Verify with:
# gh attestation verify <file> -R ${{ github.repository }}
- name: Attest build provenance
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
with:
subject-path: '*.vsix, sbom.cdx.json'

# release-please-config.json sets draft:true for this package specifically so this step can
# run at all: GitHub's immutable-releases feature locks a release the instant it's published,
# so assets can only be attached while it's still a draft. We un-draft it in the next step,
Expand All @@ -99,19 +114,20 @@ jobs:
# Only meaningful right after release-please creates the (draft) release. On a manual
# re-run the release is already published and non-draft, so attaching again would just
# 422 on the duplicate asset name — skip both steps in that case.
- name: Attach VSIX to GitHub Release
- name: Attach VSIX and SBOM to GitHub Release
if: ${{ needs.release-please.outputs.release_created == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
UPLOAD_URL: ${{ steps.resolve.outputs.upload_url }}
run: |
VSIX_FILE=$(ls *.vsix)
BASE_URL="${UPLOAD_URL%%\{*}"
curl -sSf -X POST \
-H "Authorization: token ${GH_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${VSIX_FILE}" \
"${BASE_URL}?name=${VSIX_FILE}"
for ASSET in *.vsix sbom.cdx.json; do
curl -sSf -X POST \
-H "Authorization: token ${GH_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"${ASSET}" \
"${BASE_URL}?name=${ASSET}"
done

- name: Publish GitHub Release
if: ${{ needs.release-please.outputs.release_created == 'true' }}
Expand Down
4 changes: 4 additions & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
.github/**
node_modules/**
scripts/**
test/**
extension.ts
extension.ts.map
tsconfig.json
vitest.config.mts
codecov.yml
CLAUDE.md
.gitignore
.vscodeignore
**/*.map
Expand Down
39 changes: 39 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Workspace File Bookmarks

A VS Code extension (single-file `extension.ts`, esbuild-bundled) that lets
users bookmark files across a multi-root/multi-repo workspace.

## Workflow

Feature work happens on `feature/**` branches, one PR per feature. A PR may
be split into multiple commits, but never merge a feature that isn't
complete — don't land it half-done. If a task spans multiple features, split
it into multiple PRs (one per feature) rather than combining them into one.
`development` batches dependency updates so a merge into
`main` doesn't trigger a release per commit. `main` is only touched directly
for CI fixes or hotfixes, which then propagate back down to `development` via
the `propagate-main-to-development` workflow. `promote-dev-to-main` opens a
weekly PR the other direction once `development`'s CI is green. release-please
drives versioning and publishing off Conventional Commit prefixes on `main`.

## Definition of Done

Every change is expected to clear this bar before it's mergeable:

- **CI clean.** `npm run lint` (`tsc --noEmit`), `npm run test`, and
`npm run build` all pass — see `.github/workflows/ci.yml`.
- **Unit coverage ≥85%, both patch and project.** Codecov enforces this on
every PR (`codecov.yml`: `project` and `patch`, both `target: 85%,
threshold: 1%`), reported from `npm run test:coverage` (vitest + v8,
`vitest.config.mts` mirrors the same 85% thresholds so a local run fails
the same way CI's will). Before pushing, run
`scripts/local-patch-report.sh` to check the *patch* number locally — it
diffs your branch against `origin/development`, regenerates a fresh
coverage profile, and reports the same changed-lines coverage number
Codecov's patch gate computes, so a real gap shows up before a CI
round-trip instead of after. Write real tests that close gaps for real —
no padding, no vacuous assertions just to move a number.
- **Security scanning clean.** CodeQL runs on every push/PR
(`.github/workflows/codeql.yml`); `npm audit --audit-level=high` (via
`scripts/wfb_dep_update.sh`) gates dependency updates on high/critical
findings.
38 changes: 38 additions & 0 deletions codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# =============================================================================
# Codecov Configuration
# Require 85% overall (project) and 85% patch (changed-lines) coverage —
# matches the same bar used on this repo's other projects (Charon, Hestia).
# See CLAUDE.md's Definition of Done section.
# =============================================================================

coverage:
status:
project:
default:
target: 85%
threshold: 1%
patch:
default:
target: 85%
threshold: 1%

require_ci_to_pass: yes

comment:
require_changes: false
require_base: false
require_head: true
layout: "reach, diff, flags, files"
behavior: default

# Keep in sync with scripts/local-patch-report.sh's own exclude list.
ignore:
- "test/**"
- "**/*.test.ts"
- "**/*.d.ts"
- "scripts/**"
- "*.md"
- ".github/**"
- "vitest.config.mts"
- "dist/**"
- "coverage/**"
Loading