Skip to content

Commit 8057b38

Browse files
committed
feat(ci): add GitHub Actions workflows for CI, audit, and publishing
Introduce three workflows: CI (lint, format, build, test, bundle freshness), audit (dependency vulnerability scan), and publish (automatic npm + VS Code marketplace publishing on version bumps). npm publishing uses OIDC trusted publishing (pnpm pack + npm publish --provenance) — no NPM_TOKEN secret required. Partial-publish recovery is handled via continue-on-error on the core publish step. Add reusable composite action for pnpm/Node setup, and helper scripts for version checking, registry comparison, and npm publishing. Assisted-by: Claude
1 parent 3e3d799 commit 8057b38

11 files changed

Lines changed: 261 additions & 10 deletions

File tree

.github/actions/setup/action.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
name: Setup
3+
description: Install pnpm + Node.js and install dependencies.
4+
5+
inputs:
6+
registry-url:
7+
description: npm registry URL (only needed for publishing)
8+
required: false
9+
default: ""
10+
11+
runs:
12+
using: composite
13+
steps:
14+
- uses: pnpm/action-setup@v4
15+
16+
- uses: actions/setup-node@v4
17+
with:
18+
node-version-file: package.json
19+
cache: pnpm
20+
registry-url: ${{ inputs.registry-url }}
21+
22+
- run: pnpm install --frozen-lockfile
23+
shell: bash

.github/workflows/audit.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
name: Audit
3+
4+
on:
5+
push:
6+
branches: [main]
7+
pull_request:
8+
branches:
9+
- main
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
audit:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: ./.github/actions/setup
20+
21+
- run: pnpm audit --audit-level=moderate

.github/workflows/ci.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
name: CI
3+
4+
on:
5+
push:
6+
branches: [main]
7+
pull_request:
8+
branches:
9+
- main
10+
11+
permissions:
12+
contents: read
13+
14+
jobs:
15+
ci:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: ./.github/actions/setup
20+
21+
- run: pnpm lint
22+
23+
- run: pnpm format:check
24+
25+
- run: pnpm build
26+
27+
- run: pnpm test
28+
29+
- run: pnpm check:bundles

.github/workflows/publish.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
---
2+
name: Publish
3+
4+
on:
5+
push:
6+
branches: [main]
7+
paths:
8+
- 'packages/*/package.json'
9+
workflow_dispatch:
10+
11+
concurrency:
12+
group: publish
13+
cancel-in-progress: false
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
detect:
20+
runs-on: ubuntu-latest
21+
outputs:
22+
npm: ${{ steps.npm.outputs.publish }}
23+
vscode: ${{ steps.vscode.outputs.publish }}
24+
steps:
25+
- uses: actions/checkout@v4
26+
- uses: ./.github/actions/setup
27+
28+
- id: npm
29+
run: pnpm check:published:npm
30+
31+
- id: vscode
32+
run: pnpm check:published:vscode
33+
34+
npm:
35+
needs: detect
36+
if: needs.detect.outputs.npm == 'true'
37+
runs-on: ubuntu-latest
38+
environment: npm
39+
permissions:
40+
contents: read
41+
id-token: write
42+
steps:
43+
- uses: actions/checkout@v4
44+
- uses: ./.github/actions/setup
45+
with:
46+
registry-url: https://registry.npmjs.org
47+
48+
- run: pnpm check:versions:npm
49+
50+
- run: pnpm run ci
51+
52+
- run: pnpm publish:npm:core
53+
continue-on-error: true
54+
55+
- run: pnpm publish:npm:cli
56+
57+
vscode:
58+
needs: detect
59+
if: needs.detect.outputs.vscode == 'true'
60+
runs-on: ubuntu-latest
61+
environment: vscode-marketplace
62+
steps:
63+
- uses: actions/checkout@v4
64+
- uses: ./.github/actions/setup
65+
66+
- run: pnpm run ci
67+
68+
- run: pnpm package:vscode
69+
70+
- run: pnpm publish:vscode
71+
env:
72+
VSCE_PAT: ${{ secrets.VSCE_PAT }}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
.env
21
.claude
2+
.env
3+
*.tgz
34
*.tsbuildinfo
45
*.vsix
56
coverage/

CONTRIBUTING.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ TypeScript 6 strict, ESM-only (`"type": "module"`) | pnpm 11 workspaces, Node 22
3434
| `pnpm package:vscode` | Build + package VS Code extension (`.vsix`) |
3535
| `pnpm cli` | Run the CLI (after building) |
3636
| `pnpm audit` | Check dependencies for known vulnerabilities |
37+
| `pnpm check:bundles` | Verify tracked Claude plugin bundles match build output |
38+
| `pnpm check:versions:npm` | Verify core/cli versions match |
39+
| `pnpm check:versions:vscode` | Print VS Code extension version |
40+
| `pnpm check:published:npm` | Check if npm packages need publishing |
41+
| `pnpm check:published:vscode` | Check if VS Code extension needs publishing |
42+
| `pnpm publish:vscode` | Publish VS Code extension to marketplace (requires `VSCE_PAT`) |
3743
| `pnpm reset` | Wipe and reinstall `node_modules` |
3844

3945
**Do NOT run bare `pnpm ci`** — that is pnpm's clean-install and wipes `node_modules`. Use `pnpm run ci`.
@@ -84,15 +90,14 @@ Shared ignores across `.gitignore`, `.prettierignore`, `eslint.config.ts`: `node
8490

8591
**Claude plugin** (`plugin.json`): independent version tracking marketplace releases. The plugin stays `private: true` — it is not published to npm.
8692

87-
### Publishing to npm
93+
### Publishing
8894

89-
Manual workflow:
95+
1. Bump versions in the relevant `package.json` files (core + cli must match).
96+
2. Merge to `main`.
9097

91-
1. Bump versions in `packages/core/package.json` and `packages/cli/package.json` (must match).
92-
2. Run `pnpm run ci` — lint, build, test must pass.
93-
3. `cd packages/core && npm publish --access public`
94-
4. `cd packages/cli && npm publish --access public`
95-
5. Verify: `npx @obsidian-vfs/cli@<version> --help`
98+
The `publish.yml` workflow runs automatically on every push to `main`, detects which packages have unpublished versions, and publishes only those. If no versions changed, nothing is published.
99+
100+
To publish manually: `gh workflow run publish.yml`
96101

97102
## Developing the CLI
98103

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
<img src="packages/vscode/obsidian-vfs.png" alt="Obsidian VFS" width="200" />
33
<br>
44
<br>
5+
<a href="https://github.com/otaviof/obsidian-vfs/actions/workflows/ci.yml"><img src="https://github.com/otaviof/obsidian-vfs/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
6+
<a href="https://github.com/otaviof/obsidian-vfs/actions/workflows/audit.yml"><img src="https://github.com/otaviof/obsidian-vfs/actions/workflows/audit.yml/badge.svg" alt="Audit"></a>
7+
<br>
58
<a href="https://www.npmjs.com/package/@obsidian-vfs/core"><img src="https://img.shields.io/npm/v/@obsidian-vfs/core?label=%40obsidian-vfs%2Fcore" alt="@obsidian-vfs/core"></a>
69
<a href="https://www.npmjs.com/package/@obsidian-vfs/cli"><img src="https://img.shields.io/npm/v/@obsidian-vfs/cli?label=%40obsidian-vfs%2Fcli" alt="@obsidian-vfs/cli"></a>
710
</p>

hack/check-published.sh

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Compare a local `package.json` version against the published registry version.
4+
# Outputs `publish=true/false` to `${GITHUB_OUTPUT}`` (CI) or prints a summary
5+
# (local).
6+
#
7+
8+
set -euo pipefail
9+
10+
if [[ $# -lt 3 ]]; then
11+
echo "Usage: check-published.sh <npm|vscode> <package-name> <package.json>" >&2
12+
exit 1
13+
fi
14+
15+
registry="${1}"
16+
name="${2}"
17+
pkg_json="${3}"
18+
19+
local=$(node --input-type=commonjs -p "require(\"./${pkg_json}\").version")
20+
21+
case "$registry" in
22+
npm)
23+
published=$(npm view "${name}" version 2>/dev/null || echo "")
24+
;;
25+
vscode)
26+
published=$(npx @vscode/vsce show "${name}" 2>/dev/null \
27+
| sed -n 's/^Version:[[:space:]]*//p' \
28+
|| echo "")
29+
;;
30+
*)
31+
echo "Unknown registry: $registry" >&2
32+
exit 1
33+
;;
34+
esac
35+
36+
if [[ "${local}" == "${published}" ]]; then
37+
echo "${name}: ${local} (already published)"
38+
echo "publish=false" >>"${GITHUB_OUTPUT:-/dev/null}"
39+
else
40+
echo "${name}: ${local} (new, published: ${published:-none})"
41+
echo "publish=true" >>"${GITHUB_OUTPUT:-/dev/null}"
42+
fi

hack/check-version.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Verify that one or more `package.json`` files share the same version. With a
4+
# single path, prints the version. With multiple, checks they match.
5+
#
6+
7+
set -euo pipefail
8+
9+
if [[ $# -eq 0 ]]; then
10+
echo "Usage: check-version.sh <package.json> [<package.json> ...]" >&2
11+
exit 1
12+
fi
13+
14+
version=""
15+
for pkg in "${@}"; do
16+
v=$(node --input-type=commonjs -p "require(\"./${pkg}\").version")
17+
if [[ -z "${version}" ]]; then
18+
version="${v}"
19+
elif [[ "${v}" != "${version}" ]]; then
20+
echo "version mismatch: ${pkg} (${v}) != ${1} (${version})" >&2
21+
exit 1
22+
fi
23+
echo "${pkg}: ${v}"
24+
done
25+
26+
echo "OK: ${version}"

hack/publish-npm.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Publish an npm package using OIDC trusted publishing. Runs `pnpm pack` (resolves
4+
# `workspace:` protocols) then `npm publish` with provenance.
5+
#
6+
7+
set -euo pipefail
8+
9+
if [[ ${#} -lt 1 ]]; then
10+
echo "Usage: publish-npm.sh <package-dir>" >&2
11+
exit 1
12+
fi
13+
14+
pkg_dir="${1}"
15+
16+
# The pnpm pack resolves "workspace:" protocols to real semver ranges.
17+
tarball=$(cd "${pkg_dir}" && pnpm pack 2>/dev/null | tail -1)
18+
19+
trap 'rm -f "${pkg_dir}/${tarball}"' EXIT
20+
21+
exec npm publish "${pkg_dir}/${tarball}" --access public --provenance

0 commit comments

Comments
 (0)