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
213 changes: 213 additions & 0 deletions .github/workflows/release-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
name: Release (Linux x64)

on:
push:
branches: ["main"]
workflow_dispatch:

concurrency:
group: release-linux-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: write
issues: read
pull-requests: read

jobs:
build-and-release:
runs-on: ubuntu-latest
timeout-minutes: 90

env:
# Needed by semantic-release to publish a GitHub Release
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Disable accidental code signing detection
CSC_IDENTITY_AUTO_DISCOVERY: "false"
# Skip Playwright browser downloads
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
# Ensure CI mode in many tools
CI: "true"

# Speed up Electron binary downloads (NOT headers)
ELECTRON_MIRROR: https://npmmirror.com/mirrors/electron/
# node-gyp must fetch Electron HEADERS from the canonical host
npm_config_disturl: https://artifacts.electronjs.org/headers
# be explicit about building native modules against Electron
npm_config_runtime: electron

# Harden npm networking
npm_config_fetch_retries: "5"
npm_config_fetch_retry_factor: "2"
npm_config_fetch_retry_maxtimeout: "300000" # 5m
npm_config_network_timeout: "600000" # 10m

steps:
# ── Git & Node toolchain ───────────────────────────────────────────────────
- name: Checkout (full history for tags)
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
# keep only if package-lock.json exists in the repo
cache-dependency-path: package-lock.json

- name: Setup Electron cache env
run: echo "ELECTRON_CACHE=$RUNNER_TEMP/electron-cache" >> $GITHUB_ENV

# Optional: auto-sync npm_config_target with package.json devDependencies.electron
- name: Read Electron version from package.json
id: electron
run: echo "target=$(node -p \"require('./package.json').devDependencies.electron\")" >> "$GITHUB_OUTPUT"

# ── System toolchain & headers for native modules/Electron ─────────────────
- name: Install system libraries for native modules
env:
DEBIAN_FRONTEND: noninteractive
run: |
set -euxo pipefail
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential \
python3 \
pkg-config \
libkrb5-dev \
libsecret-1-dev \
libxkbfile-dev \
libxkbcommon-dev \
libxkbcommon-x11-dev \
libx11-dev \
libnss3 \
libgtk-3-0 \
libatk-bridge2.0-0 \
libgbm-dev
# NOTE: intentionally NOT installing libasound2 (not available on this image)

# ── JS deps ────────────────────────────────────────────────────────────────
- name: Install dependencies (robust with headers fallback)
env:
# use dynamic target; if you prefer hardcode, set "34.3.2" here instead
npm_config_target: ${{ steps.electron.outputs.target }}
run: |
set -euxo pipefail
# First attempt (using artifacts.electronjs.org/headers)
npm ci || {
echo "First npm ci failed. Cleaning and retrying with electronjs.org/headers fallback…"
rm -rf node_modules
npm_config_disturl="https://electronjs.org/headers" npm ci
}

- name: Verify gulp is available
run: npx --yes gulp --version

# ── Build React bundles required by TS imports (react/out/**) ─────────────
- name: Build React (void UI)
run: npm run buildreact

# ── Compile dev sources (not minified) ─────────────────────────────────────
- name: Compile sources (gulp compile)
run: npx --yes gulp compile

# ── Create a CI guard for empty/invalid vinyl-fs globs ────────────────────
- name: Create CI vinyl-fs patch (CommonJS)
run: |
mkdir -p build
cat > build/ci-vfs-patch.cjs <<'JS'
const vfs = require('vinyl-fs'); const realSrc = vfs.src;
const { Readable } = require('stream');
function empty(){ return Readable.from([]); }
function clean(g){
if(Array.isArray(g)){ const a=g.filter(s=>typeof s==='string'&&s.trim()); return a.length?a:null; }
if(typeof g==='string') return g.trim()?g:null;
return null;
}
vfs.src = function(globs, opts){ const safe=clean(globs); return safe?realSrc.call(this,safe,opts):empty(); };

try {
const srcPath = require.resolve('vinyl-fs/lib/src/index.js');
const real = require(srcPath);
if (typeof real === 'function' && require.cache[srcPath]) {
require.cache[srcPath].exports = function(globs, opts){
const safe = clean(globs); return safe?real.call(this,safe,opts):empty();
};
}
} catch {}
JS

# ── Build outputs used by minified packagers (produces out-vscode* trees) ─
- name: Compile build (electron/browser)
run: npx --yes gulp compile-build

- name: Compile extensions (build)
run: npx --yes gulp compile-extensions-build

- name: Minify VS Code (produces out-vscode-min)
run: npx --yes gulp minify-vscode

- name: Minify VS Code REH/Web (optional but recommended)
run: |
npx --yes gulp minify-vscode-reh
npx --yes gulp minify-vscode-reh-web

- name: Assert out-vscode-min exists
run: test -f out-vscode-min/vs/base/parts/sandbox/electron-sandbox/preload.js

# ── Package (minified CI variant) ──────────────────────────────────────────
- name: Package Linux x64 (CI variant)
env:
NODE_OPTIONS: "--require ./build/ci-vfs-patch.cjs"
run: npx --yes gulp vscode-linux-x64-min-ci

# ── Debug listing ─────────────────────────────────────────────────────────
- name: List built artifacts (debug)
run: |
echo "Tree under .build/"
(ls -alh .build || true)
(find .build -maxdepth 4 -type f | sort || true)

# ── Collect release assets ────────────────────────────────────────────────
- name: Prepare release assets
run: |
set -euxo pipefail
mkdir -p release
shopt -s globstar nullglob
for pattern in \
".build/**/*linux-x64*.zip" \
".build/**/*linux-x64*.tar.gz" \
".build/**/linux-x64/**/archive/*.zip" \
".build/**/linux-x64/**/archive/*.tar.gz"; do
for f in $pattern; do
echo "Collecting $f"
cp -v "$f" release/ || true
done
done
if [ -z "$(ls -A release 2>/dev/null)" ]; then
LINUX_DIR="$(find .build -type d -name '*linux-x64*' | head -n1 || true)"
if [ -n "$LINUX_DIR" ]; then
ARCHIVE="void-linux-x64-$(date +%Y%m%d%H%M).tar.gz"
tar -czf "release/$ARCHIVE" -C "$LINUX_DIR/.." "$(basename "$LINUX_DIR")"
fi
fi
echo "Release assets:"; ls -alh release || true

- name: Upload build artifacts (debug)
if: always()
uses: actions/upload-artifact@v4
with:
name: linux-x64-release
path: release/*
if-no-files-found: ignore

# ── Publish GitHub Release via semantic-release ───────────────────────────
- name: Semantic Release
env:
GIT_AUTHOR_NAME: "Nathan Jobrayan"
GIT_AUTHOR_EMAIL: "[email protected]"
GIT_COMMITTER_NAME: "Nathan Jobrayan"
GIT_COMMITTER_EMAIL: "[email protected]"
run: npm run release
60 changes: 0 additions & 60 deletions .github/workflows/triage.yml

This file was deleted.

16 changes: 16 additions & 0 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"branches": [ "main" ],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/changelog", { "changelogFile": "CHANGELOG.md" }],
["@semantic-release/github", {
"assets": [
{ "path": "release/*", "label": "Linux x64 build" }
]
}],
["@semantic-release/git", {
"assets": ["CHANGELOG.md"]
}]
]
}
57 changes: 57 additions & 0 deletions build/ci-vfs-patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* CI shim for gulp/vinyl-fs to tolerate empty/invalid globs.
* Preload with: NODE_OPTIONS="--require ./build/ci-vfs-patch.cjs"
*/
'use strict';

/** @returns {import('stream').Readable} */
function emptyStream() {
const { Readable } = require('stream');
return Readable.from([]);
}

/**
* Sanitize vinyl-fs glob argument.
* @param {any} globs
* @returns {string|string[]|null}
*/
function sanitize(globs) {
if (Array.isArray(globs)) {
const cleaned = globs.filter(g => typeof g === 'string' && g.trim().length > 0);
return cleaned.length ? cleaned : null;
}
if (typeof globs === 'string') {
const s = globs.trim();
return s.length ? s : null;
}
return null;
}

/**
* Patch a src function (vinyl-fs/lib/src).
* @param {(globs:any, options?:any)=>any} realSrc
*/
function makePatchedSrc(realSrc) {
return function patchedSrc(globs, options) {
const safe = sanitize(globs);
if (!safe) return emptyStream();
return realSrc.call(this, safe, options);
};
}

try {
// Patch top-level vinyl-fs.src
const vfs = require('vinyl-fs');
if (vfs && typeof vfs.src === 'function') {
vfs.src = makePatchedSrc(vfs.src);
}
} catch { /* ignore */ }

try {
// Patch direct import path used by gulp
const srcPath = require.resolve('vinyl-fs/lib/src/index.js');
const realSrc = require(srcPath);
if (typeof realSrc === 'function' && require.cache[srcPath]) {
require.cache[srcPath].exports = makePatchedSrc(realSrc);
}
} catch { /* ignore */ }
11 changes: 5 additions & 6 deletions extensions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions extensions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
"version": "0.0.1",
"license": "MIT",
"description": "Dependencies shared by all extensions",
"dependencies": {
"typescript": "^5.8.2"
},
"scripts": {
"postinstall": "node ./postinstall.mjs"
},
"devDependencies": {
"@parcel/watcher": "2.5.1",
"esbuild": "0.25.0",
"typescript": "^5.9.2",
"vscode-grammar-updater": "^1.1.0"
},
"overrides": {
Expand Down
Loading