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
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Global owner — all PRs require review from the repo owner
* @AutoCookies
78 changes: 78 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# SyntaxVoid CI/CD Workflows

## Workflows

### `ci.yml` — Continuous Integration
Runs on every push/PR to `main`, `master`, or `dev`.

- **Smoke check** on Linux, Windows, macOS: installs deps + builds native modules
- **ESLint** lint pass

### `build.yml` — Build & Release
Runs when you push a version tag (`v1.2.3`) or manually dispatch.

Produces downloadable artifacts for every platform:

| Platform | Artifacts |
|---|---|
| Linux x64 | `.AppImage`, `.deb`, `.rpm`, `.tar.gz` |
| Linux ARM64 | `.AppImage`, `.deb`, `.rpm`, `.tar.gz` |
| Windows x64 | `.exe` (NSIS installer), `.zip` |
| macOS Universal | `.dmg`, `.zip` |

---

## How to create a release

```bash
# 1. Bump the version in package.json
npm version 1.2.3 # or manually edit

# 2. Commit and tag
git add package.json
git commit -m "chore: release v1.2.3"
git tag v1.2.3

# 3. Push — this triggers the build workflow
git push origin main --tags
```

GitHub Actions will:
1. Build SyntaxVoid on all 4 platform runners in parallel
2. Collect all `.AppImage`, `.deb`, `.rpm`, `.tar.gz`, `.exe`, `.zip`, `.dmg` files
3. Create a GitHub Release at `https://github.com/AutoCookies/syntaxvoid/releases/tag/v1.2.3`
4. Attach all files as downloadable assets

---

## Optional: macOS Code Signing

Without signing, macOS users see a Gatekeeper warning. To enable signing,
add these **repository secrets** (Settings → Secrets → Actions):

| Secret | Value |
|---|---|
| `MACOS_CERT_P12` | Base64-encoded `.p12` Developer ID certificate |
| `MACOS_CERT_PASSWORD` | Password for the `.p12` |
| `APPLE_ID` | Your Apple ID email |
| `APPLE_APP_PASSWORD` | App-specific password from appleid.apple.com |
| `APPLE_TEAM_ID` | Your Apple Developer Team ID |

Without these secrets, the workflow produces **unsigned builds** that still
work — users just need to right-click → Open on first launch.

---

## Pre-release / canary builds

Tags with a pre-release suffix are automatically marked as pre-releases:

```bash
git tag v1.2.3-beta.1
git push origin v1.2.3-beta.1
```

To build a **SyntaxVoidNext** (canary) package that runs alongside stable:

1. Go to **Actions → Build & Release → Run workflow**
2. Set `channel` to `next`
301 changes: 301 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
name: Build & Release

# Triggers:
# 1. Push a version tag → full build + public GitHub Release
# 2. Manual trigger → build without publishing (for testing the pipeline)
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+' # stable: v1.2.3
- 'v[0-9]+.[0-9]+.[0-9]+-*' # pre-rel: v1.2.3-beta.1
Comment on lines +9 to +10

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use glob patterns that match real version tags

The on.push.tags filters are written in regex style ([0-9]+) but GitHub Actions tag filters use glob matching, where + is treated as a literal character. As written, tags like v1.2.3 or v1.2.3-beta.1 (the release flow documented in .github/workflows/README.md) will not match these patterns, so the build/release workflow will not start on normal release tags.

Useful? React with 👍 / 👎.

workflow_dispatch:
inputs:
channel:
description: 'Release channel'
required: true
default: 'regular'
type: choice
options:
- regular # standard SyntaxVoid build
- next # SyntaxVoidNext (canary) — runs alongside stable

# Only one release job runs at a time per tag.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
# ────────────────────────────────────────────────────────────────────────
# Build Matrix — one runner per platform
# ────────────────────────────────────────────────────────────────────────
build:
name: Build · ${{ matrix.name }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:

# ── Linux (x64) ──────────────────────────────────────────────────
- name: Linux x64
runner: ubuntu-latest
platform: linux
arch: x64
artifact_name: syntaxvoid-linux
artifact_glob: |
dist/*.AppImage
dist/*.deb
dist/*.rpm
dist/*.tar.gz

# ── Linux (ARM64) — cross-compiled on x64 runner ─────────────────
- name: Linux ARM64
runner: ubuntu-latest
platform: linux
arch: arm64
artifact_name: syntaxvoid-linux-arm64
artifact_glob: |
dist/*.AppImage
dist/*.deb
dist/*.rpm
dist/*.tar.gz

# ── Windows (x64) ────────────────────────────────────────────────
- name: Windows x64
runner: windows-latest
platform: win
arch: x64
artifact_name: syntaxvoid-windows
artifact_glob: |
dist/*.exe
dist/*.zip

# ── macOS (Universal: Intel + Apple Silicon) ──────────────────────
- name: macOS Universal
runner: macos-latest
platform: mac
arch: universal
artifact_name: syntaxvoid-macos
artifact_glob: |
dist/*.dmg
dist/*.zip

steps:
# ── Checkout ──────────────────────────────────────────────────────────
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history so electron-builder can read git tags

# ── Node.js ───────────────────────────────────────────────────────────
- name: Set up Node.js 20
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'yarn'

- name: Enable Corepack (Yarn)
run: corepack enable

# ── Linux: install system dependencies ────────────────────────────────
# NOTE: Ubuntu 24.04 (Noble) renamed libasound2 → libasound2t64
- name: Install Linux build deps
if: matrix.platform == 'linux'
run: |
sudo apt-get update -y
sudo apt-get install -y \
libx11-dev libxkbfile-dev libsecret-1-dev \
rpm fakeroot dpkg libnss3 libatk1.0-0 \
libatk-bridge2.0-0 libgdk-pixbuf2.0-0 \
libgtk-3-0 libgbm1 libasound2t64

# ── macOS: import code-signing certificate (optional) ─────────────────
# Set the following repository secrets to enable code-signing:
# MACOS_CERT_P12 base64-encoded .p12 certificate
# MACOS_CERT_PASSWORD password for the .p12
# APPLE_ID Apple ID email (for notarization)
# APPLE_APP_PASSWORD App-specific password
# APPLE_TEAM_ID Apple Developer Team ID
- name: Import macOS signing certificate
if: matrix.platform == 'mac' && env.MACOS_CERT_P12 != ''
env:
MACOS_CERT_P12: ${{ secrets.MACOS_CERT_P12 }}
MACOS_CERT_PASSWORD: ${{ secrets.MACOS_CERT_PASSWORD }}
run: |
echo "$MACOS_CERT_P12" | base64 --decode > certificate.p12
security create-keychain -p ci_keychain build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p ci_keychain build.keychain
security import certificate.p12 -k build.keychain \
-P "$MACOS_CERT_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: \
-s -k ci_keychain build.keychain
rm certificate.p12

# ── Set up crabpm (SyntaxVoid package manager) ────────────────────────
# Cloned into `ppm/` — the path the build script expects.
#
# Linux / Windows: normal install works fine.
#
# macOS: git-utils (a crabpm dependency) bundles an old libgit2/zlib that
# redefines the `fdopen` macro, which conflicts with Xcode 16+ SDK headers.
# We skip all native-module compilation with --ignore-scripts and then run
# the postinstall script manually so the bundled Node binary is still
# downloaded. crabpm's git integration is not needed for CI builds.
- name: Set up crabpm (Linux / Windows)
if: matrix.platform != 'mac'
shell: bash
run: |
git clone --depth=1 https://github.com/AutoCookies/crabpm.git ppm
cd ppm
yarn install --ignore-engines

- name: Set up crabpm (macOS — skip native compilation)
if: matrix.platform == 'mac'
shell: bash
run: |
git clone --depth=1 https://github.com/AutoCookies/crabpm.git ppm
cd ppm
yarn install --ignore-engines --ignore-scripts
node script/postinstall.js

# ── Install project dependencies ──────────────────────────────────────
- name: Install dependencies
run: yarn install --ignore-engines

# ── Windows: restore distutils (removed in Python 3.12, needed by node-gyp)
- name: Restore distutils (Windows)
if: matrix.platform == 'win'
run: pip install setuptools

# ── Rebuild native Node modules for Electron ──────────────────────────
- name: Build native modules
run: yarn build

# ── Determine flags ───────────────────────────────────────────────────
- name: Determine build flags
id: flags
shell: bash
run: |
FLAGS="--platform ${{ matrix.platform }}"
# Canary channel
if [[ "${{ github.event.inputs.channel }}" == "next" ]]; then
FLAGS="$FLAGS --next"
fi
# Architecture override (ARM64 cross-compile, universal macOS)
if [[ "${{ matrix.arch }}" == "arm64" ]]; then
FLAGS="$FLAGS --target arm64-appimage,deb,rpm,tar.gz"
fi
if [[ "${{ matrix.arch }}" == "universal" ]]; then
FLAGS="$FLAGS --target universal"
fi
echo "flags=$FLAGS" >> "$GITHUB_OUTPUT"

# ── Build Linux distributables ─────────────────────────────────────────
- name: Build (Linux)
if: matrix.platform == 'linux'
run: yarn dist ${{ steps.flags.outputs.flags }}

# ── Build Windows distributables ───────────────────────────────────────
- name: Build (Windows)
if: matrix.platform == 'win'
run: yarn dist ${{ steps.flags.outputs.flags }}

# ── Build macOS distributables ─────────────────────────────────────────
- name: Build (macOS)
if: matrix.platform == 'mac'
env:
# Disable auto-discovery when no cert is present (produces unsigned build)
CSC_IDENTITY_AUTO_DISCOVERY: ${{ secrets.MACOS_CERT_P12 != '' && 'true' || 'false' }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: yarn dist ${{ steps.flags.outputs.flags }}

# ── List produced files ────────────────────────────────────────────────
- name: List dist output
shell: bash
run: ls -lh dist/ || true

# ── Upload artifacts (kept 90 days, always; used by release job) ───────
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: |
dist/*.AppImage
dist/*.deb
dist/*.rpm
dist/*.tar.gz
dist/*.exe
dist/*.zip
dist/*.dmg
if-no-files-found: warn
retention-days: 90

# ────────────────────────────────────────────────────────────────────────
# Release — collect all artifacts and publish a GitHub Release
# Only runs when triggered by a version tag push.
# ────────────────────────────────────────────────────────────────────────
release:
name: Publish Release
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write

steps:
- name: Checkout (for release notes)
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Download all platform artifacts
uses: actions/download-artifact@v4
with:
path: release-files/
merge-multiple: true

- name: List release files
run: ls -lhR release-files/

- name: Determine release type
id: rel
run: |
TAG="${GITHUB_REF_NAME}"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
if echo "$TAG" | grep -qE '-(alpha|beta|rc)'; then
echo "prerelease=true" >> "$GITHUB_OUTPUT"
else
echo "prerelease=false" >> "$GITHUB_OUTPUT"
fi

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.rel.outputs.tag }}
name: SyntaxVoid ${{ steps.rel.outputs.tag }}
prerelease: ${{ steps.rel.outputs.prerelease }}
draft: false
generate_release_notes: true
body: |
## SyntaxVoid ${{ steps.rel.outputs.tag }}

### Downloads

| Platform | Format | Notes |
|---|---|---|
| Linux | `.AppImage` | Universal, no install needed |
| Linux | `.deb` | Debian / Ubuntu |
| Linux | `.rpm` | Fedora / RHEL |
| Linux | `.tar.gz` | Manual install |
| Windows | `.exe` | NSIS installer |
| Windows | `.zip` | Portable |
| macOS | `.dmg` | Drag-and-drop install |
| macOS | `.zip` | Portable |

> **Note**: macOS builds may be unsigned. Right-click → Open on first launch.

See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for full details.
files: release-files/**/*
Loading