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
6 changes: 5 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.0.1/schema.json",
"$schema": "https://unpkg.com/@changesets/config@3.0.2/schema.json",
"changelog": ["@changesets/changelog-github", { "repo": "graphql/graphiql" }],
"commit": false,
"linked": [],
Expand All @@ -14,6 +14,10 @@
"example-monaco-graphql-webpack"
],
"updateInternalDependencies": "patch",
"privatePackages": {
"version": true,
"tag": true
},
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
}
Expand Down
7 changes: 7 additions & 0 deletions .changeset/vscode-publish-retry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'vscode-graphql-execution': patch
'vscode-graphql-syntax': patch
'vscode-graphql': patch
---

Burning patch version due to previous release failure.
65 changes: 63 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
# To enable trusted publishing
id-token: write

outputs:
vscode-published: ${{ steps.vscode.outputs.published }}
published-packages: ${{ steps.changesets.outputs.publishedPackages }}

steps:
- name: Checkout Code
uses: actions/checkout@v5
Expand All @@ -50,8 +54,65 @@ jobs:
version: yarn ci:version
# This expects you to have a script called release which does a build for your packages and calls changeset publish
publish: yarn release

- name: Note VSCode extension release
id: vscode
if: ${{ steps.changesets.outputs.published == 'true' && contains(steps.changesets.outputs.publishedPackages, '"name":"vscode-graphql') }}
run: echo "published=true" >> "$GITHUB_OUTPUT"

- name: Build VSCode extension .vsix files
if: ${{ steps.vscode.outputs.published == 'true' }}
env:
PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }}
run: node scripts/release-vscode.mts build

- name: Attach VSCode .vsix files to GitHub Releases
if: ${{ steps.vscode.outputs.published == 'true' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PUBLISHED_PACKAGES: ${{ steps.changesets.outputs.publishedPackages }}
run: node scripts/release-vscode.mts attach

- name: Upload VSCode extension .vsix artifacts
if: ${{ steps.vscode.outputs.published == 'true' }}
uses: actions/upload-artifact@v4
with:
name: vscode-extensions
path: packages/vscode-graphql*/*.vsix
if-no-files-found: error

publish-vscode-extensions:
name: Publish to ${{ matrix.registry }}
needs: release
if: ${{ needs.release.outputs.vscode-published == 'true' }}
runs-on: ubuntu-latest
permissions: {}
strategy:
fail-fast: false
matrix:
include:
- registry: VSCode Marketplace
command: publish-vsce
- registry: Open VSX Registry
command: publish-ovsx
steps:
- uses: actions/checkout@v5

- uses: actions/setup-node@v6
with:
node-version-file: '.node-version'
cache: yarn

- run: yarn install --frozen-lockfile --immutable

- uses: actions/download-artifact@v4
with:
name: vscode-extensions
path: vsix

- name: Publish
env:
# for vscode marketplace, see https://github.com/microsoft/vscode-vsce/blob/194d59b975523696362ead891dc0f3ddd277b3bd/README.md#linux
VSCE_PAT: ${{ secrets.VSCE_PAT }}
# for ovsx, see https://github.com/eclipse/openvsx/blob/master/cli/README.md#publish-extensions
OVSX_PAT: ${{ secrets.OPEN_VSX_TOKEN }}
PUBLISHED_PACKAGES: ${{ needs.release.outputs.published-packages }}
run: node scripts/release-vscode.mts ${{ matrix.command }}
24 changes: 24 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
# Cutting New Releases

TODO: Redo for `changesets`. See [`Changesets Readme`](./.changeset/README.md)

## VSCode extension releases

After Changesets publishes the npm packages, the release workflow hands the
VSCode extension pipeline to [`scripts/release-vscode.mts`](./scripts/release-vscode.mts).
It exposes four commands — `build`, `attach`, `publish-vsce`, `publish-ovsx` —
each of which operates only on the extensions actually released in this run
(filtered from the Changesets `publishedPackages` output).

### Why attach to GitHub Releases

We upload each built `.vsix` to its GitHub Release tag in addition to
publishing to the VSCode Marketplace and Open VSX. That makes the artifact
downloadable directly from GitHub — useful when a registry is degraded, when
a PAT has expired and a manual re-upload is needed, or for anyone who wants
the exact bits we shipped without going through a marketplace.

### Why a TS script, not workflow shell

Per-package iteration, version lookups, and registry-API calls are tidier
(and safer) in a type-checked TS file than spread across `run:` blocks of
bash in `release.yml`. The script type-checks under `scripts/tsconfig.json`,
can be run locally with a mocked `PUBLISHED_PACKAGES` env var, and aggregates
failures across packages rather than short-circuiting on the first error.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"svgo": "svgo --pretty --indent 2 --eol lf --final-newline -r -f . --exclude node_modules",
"svgo-check": "yarn svgo && git diff --exit-code -- '*.svg'",
"ci:version": "yarn changeset version && yarn install --no-immutable && yarn build && yarn fix",
"release": "yarn build && yarn build-bundles && (wsrun release --exclude-missing --serial --recursive --changedSince main -- || true) && yarn changeset publish",
"release": "yarn build && yarn build-bundles && yarn changeset publish",
"release:canary": "(node scripts/canary-release.mts && yarn build-bundles && yarn changeset publish --tag canary) || echo Skipping Canary...",
"repo:lint": "manypkg check",
"repo:fix": "manypkg fix",
Expand Down Expand Up @@ -126,7 +126,9 @@
"vite": "6.3.4"
},
"devDependencies": {
"@vscode/vsce": "^2.22.1-2",
"jsdom": "^29.0.2",
"ovsx": "0.8.3",
"svgo": "^4.0.1"
}
}
6 changes: 1 addition & 5 deletions packages/vscode-graphql-execution/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,7 @@
"types:check": "tsc --noEmit",
"compile": "node esbuild.js",
"build-bundles": "yarn run compile",
"vsce:package": "yarn compile && vsce package --yarn --no-dependencies",
"vsce:prepublish": "yarn run vsce:package",
"vsce:publish": "vsce publish --yarn --no-dependencies",
"open-vsx:publish": "ovsx publish --no-dependencies --pat $OVSX_PAT",
"release": "yarn run compile && yarn run vsce:publish && yarn run open-vsx:publish"
"vsce:package": "yarn compile && vsce package --yarn --no-dependencies"
},
"devDependencies": {
"@types/capitalize": "2.0.0",
Expand Down
4 changes: 0 additions & 4 deletions packages/vscode-graphql-syntax/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,6 @@
"scripts": {
"types:check": "tsc --noEmit",
"vsce:package": "vsce package --yarn --no-dependencies",
"vsce:prepublish": "npm run vsce:package",
"vsce:publish": "vsce publish --yarn --no-dependencies",
"open-vsx:publish": "ovsx publish --no-dependencies --pat $OVSX_PAT",
"release": "npm run vsce:publish && npm run open-vsx:publish",
"test": "vitest run"
}
}
5 changes: 1 addition & 4 deletions packages/vscode-graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,7 @@
"compile": "node esbuild",
"build-bundles": "npm run compile -- --sourcemap",
"vsce:package": "vsce package --yarn --no-dependencies",
"env:source": "export $(cat .envrc | xargs)",
"vsce:publish": "vsce publish --yarn --no-dependencies",
"open-vsx:publish": "ovsx publish --no-dependencies --pat $OVSX_PAT",
"release": "npm run vsce:publish && npm run open-vsx:publish"
"env:source": "export $(cat .envrc | xargs)"
},
"devDependencies": {
"@types/capitalize": "2.0.0",
Expand Down
166 changes: 166 additions & 0 deletions scripts/release-vscode.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { spawnSync } from 'node:child_process';
import { readFile } from 'node:fs/promises';
import { parseArgs } from 'node:util';
import { publishVSIX } from '@vscode/vsce';
import { publish as ovsxPublish } from 'ovsx';

const PACKAGES = [
'vscode-graphql',
'vscode-graphql-syntax',
'vscode-graphql-execution',
] as const;
type VscodePackage = (typeof PACKAGES)[number];

interface PublishedPackage {
name: string;
version: string;
}

function publishedVscodePackages(): VscodePackage[] {
const raw = process.env.PUBLISHED_PACKAGES;
if (!raw) {
return [];
}
const all = JSON.parse(raw) as PublishedPackage[];
const names = new Set(all.map(p => p.name));
return PACKAGES.filter(p => names.has(p));
}

async function readVersion(pkg: VscodePackage): Promise<string> {
const json = await readFile(`packages/${pkg}/package.json`, 'utf8');
return (JSON.parse(json) as { version: string }).version;
}

function vsixPath(
baseDir: string,
pkg: VscodePackage,
version: string,
): string {
return `${baseDir}/packages/${pkg}/${pkg}-${version}.vsix`;
}

function requireEnv(name: string): string {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required env var: ${name}`);
}
return value;
}

async function runForEach(
packages: VscodePackage[],
label: string,
fn: (pkg: VscodePackage) => Promise<void>,
): Promise<void> {
let failures = 0;
for (const pkg of packages) {
try {
await fn(pkg);
} catch (err) {
failures += 1;
console.error(`[${label}] failed for ${pkg}:`, err);
}
}
if (failures > 0) {
throw new Error(
`${failures}/${packages.length} ${label} operation(s) failed`,
);
}
}

async function build(packages: VscodePackage[]): Promise<void> {
await runForEach(packages, 'build', async pkg => {
console.log(`Building ${pkg}.vsix`);
const { status } = spawnSync('yarn', ['workspace', pkg, 'vsce:package'], {
stdio: 'inherit',
});
if (status !== 0) {
throw new Error(
`yarn workspace ${pkg} vsce:package exited with status ${status}`,
);
}
});
}

async function attach(packages: VscodePackage[]): Promise<void> {
await runForEach(packages, 'attach', async pkg => {
const version = await readVersion(pkg);
const tag = `${pkg}@${version}`;
const path = vsixPath('.', pkg, version);
console.log(`Attaching ${path} to release ${tag}`);
const { status } = spawnSync(
'gh',
['release', 'upload', tag, path, '--clobber'],
{ stdio: 'inherit' },
);
if (status !== 0) {
throw new Error(`gh release upload exited with status ${status}`);
}
});
}

async function publishToVsce(packages: VscodePackage[]): Promise<void> {
const pat = requireEnv('VSCE_PAT');
await runForEach(packages, 'vsce publish', async pkg => {
const version = await readVersion(pkg);
const path = vsixPath('vsix', pkg, version);
console.log(`Publishing ${path} to VSCode Marketplace`);
await publishVSIX(path, { pat, skipDuplicate: true });
});
}

async function publishToOvsx(packages: VscodePackage[]): Promise<void> {
const pat = requireEnv('OVSX_PAT');
await runForEach(packages, 'ovsx publish', async pkg => {
const version = await readVersion(pkg);
const path = vsixPath('vsix', pkg, version);
console.log(`Publishing ${path} to Open VSX Registry`);
const results = await ovsxPublish({
extensionFile: path,
pat,
skipDuplicate: true,
});
const rejected = results.filter(r => r.status === 'rejected');
if (rejected.length > 0) {
throw new AggregateError(
rejected.map(r => r.reason),
`${rejected.length} target(s) rejected by Open VSX`,
);
}
});
}

const { positionals } = parseArgs({
args: process.argv.slice(2),
allowPositionals: true,
});

const command = positionals[0];
const packages = publishedVscodePackages();

if (packages.length === 0) {
console.log('No VSCode extensions were published; nothing to do.');
process.exit(0);
}

console.log(`VSCode packages: ${packages.join(', ')}`);

switch (command) {
case 'build':
await build(packages);
break;
case 'attach':
await attach(packages);
break;
case 'publish-vsce':
await publishToVsce(packages);
break;
case 'publish-ovsx':
await publishToOvsx(packages);
break;
default:
console.error(
'Usage: node scripts/release-vscode.mts <build|attach|publish-vsce|publish-ovsx>',
);
process.exit(1);
}
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12921,6 +12921,7 @@ __metadata:
"@types/semver": "npm:^7.5.0"
"@types/ws": "npm:8.2.2"
"@typescript/native-preview": "npm:^7.0.0-dev"
"@vscode/vsce": "npm:^2.22.1-2"
babel-plugin-macros: "npm:^3.1.0"
babel-plugin-transform-import-meta: "npm:^2.2.1"
concurrently: "npm:^7.0.0"
Expand All @@ -12932,6 +12933,7 @@ __metadata:
jsdom: "npm:^29.0.2"
mkdirp: "npm:^1.0.4"
msw: "npm:^2.13.4"
ovsx: "npm:0.8.3"
oxfmt: "npm:^0.45.0"
oxlint: "npm:^1"
oxlint-plugin-eslint: "npm:^1"
Expand Down
Loading