Skip to content

build and release

build and release #61

Workflow file for this run

name: build and release
on:
workflow_dispatch: # for manual debugging
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
jobs:
build-bin:
strategy:
fail-fast: false # to see what builds/what fails
matrix:
include:
# each `target` is picked from `rustup target list` and each
# `vscode-target` is drawn from `vsce publish --help`
# x86 windows
- os: windows-latest
native:
target: x86_64-pc-windows-msvc
vscode-target: win32-x64
- os: ubuntu-latest
native:
target: x86_64-unknown-linux-gnu
vscode-target: linux-x64 # todo: fully-static MUSL?
zig: false
cross:
target: aarch64-unknown-linux-gnu
vscode-target: linux-arm64
zig: true
- os: macos-latest
native:
target: x86_64-apple-darwin
vscode-target: darwin-x64
zig: true
cross:
target: aarch64-apple-darwin
vscode-target: darwin-arm64
zig: true
name: Build (${{ matrix.native.target }})
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # shallow clone for speed
- name: "cache native base binary build: ${{ matrix.native.target }}"
uses: actions/cache@v4
id: native-base-cache
with:
path: |
./target/${{ matrix.native.target }}/release/base_language_server*
./target/${{ matrix.native.target }}/release/base_language_server.exe
./target/${{ matrix.native.target }}/release/base_language_server.pdb
# hardcode paths in an attempt to appease windows
key: ${{ matrix.native.target }}--${{ hashFiles('./Cargo.lock', 'pkg/base/Cargo.toml', 'pkg/base/**/*.rs', 'pkg/base/**/*.scm', './scripts/build_bin.sh') }}
- name: "cache cross base binary build: ${{ matrix.cross.target }}"
uses: actions/cache@v4
if: matrix.cross.target
id: cross-base-cache
with:
path: |
./target/${{ matrix.cross.target }}/release/base_language_server*
./target/${{ matrix.cross.target }}/release/base_language_server.exe
./target/${{ matrix.cross.target }}/release/base_language_server.pdb
key: ${{ matrix.cross.target }}--${{ hashFiles('./Cargo.lock', 'pkg/pro/Cargo.toml', 'pkg/base/**/*.rs', 'pkg/base/**/*.scm') }}
- name: "cache native pro binary build: ${{ matrix.native.target }}"
uses: actions/cache@v4
id: native-pro-cache
with:
path: |
./target/${{ matrix.native.target }}/release/pro_language_server*
./target/${{ matrix.native.target }}/release/pro_language_server.exe
./target/${{ matrix.native.target }}/release/pro_language_server.pdb
# hardcode paths in an attempt to appease windows
key: ${{ matrix.native.target }}--${{ hashFiles('./Cargo.lock', '**/Cargo.toml', 'pkg/base/**/*.rs', 'pkg/base/**/*.scm', './scripts/build_bin.sh') }}
- name: "cache cross pro binary build: ${{ matrix.cross.target }}"
uses: actions/cache@v4
if: matrix.cross.target
id: cross-pro-cache
with:
path: |
./target/${{ matrix.cross.target }}/release/pro_language_server*
./target/${{ matrix.cross.target }}/release/pro_language_server.exe
./target/${{ matrix.cross.target }}/release/pro_language_server.pdb
key: ${{ matrix.cross.target }}--${{ hashFiles('./Cargo.lock', '**/Cargo.toml', 'pkg/base/**/*.rs', 'pkg/base/**/*.scm') }}
- id: boot
shell: bash
run: |
ref_name="${{ github.ref_name }}"
native_base="${{ steps.native-base-cache.outputs.cache-hit != 'true'|| false }}"
native_pro="${{ steps.native-pro-cache.outputs.cache-hit != 'true' || false }}"
cross_base="${{ matrix.cross.target && steps.cross-base-cache.outputs.cache-hit != 'true' || false }}"
cross_pro="${{ matrix.cross.target && steps.native-pro-cache.outputs.cache-hit != 'true' || false }}"
{
case true in
$native_base|$native_pro|$cross_base|$cross_pro) echo "init=true" ;;
*) echo "init=false" ;;
esac
echo "native_base=$native_base"
echo "cross_base=$cross_base"
echo "native_pro=$native_pro"
echo "cross_pro=$cross_pro"
} | tee -a "$GITHUB_OUTPUT"
- name: set MacOS SDKROOT and MACOSX_DEPLOYMENT_TARGET
# see https://stackoverflow.com/questions/66849112/how-do-i-cross-compile-a-rust-application-from-macos-x86-to-macos-silicon
if: matrix.os == 'macos-latest' && steps.boot.outputs.init == 'true'
run: |
{
echo "SDKROOT=$(xcrun -sdk macosx --show-sdk-path)"
echo "MACOSX_DEPLOYMENT_TARGET=$(xcrun -sdk macosx --show-sdk-platform-version)"
} | tee -a $GITHUB_ENV
- name: "Install rust toolchain"
if: steps.boot.outputs.init == 'true'
shell: bash
run: |
rustup toolchain install stable --profile minimal
rustup component add rust-src
rustup component add llvm-tools-preview # 32mb
# pulling 3x32MB of llvm tools is still better than pushing+pulling
# 6x60MB of the built binary
- name: "install native rust target: ${{ matrix.native.target }}"
shell: bash
if: steps.boot.outputs.native_base == 'true' || steps.boot.outputs.native_pro == 'true'
run: rustup target add ${{ matrix.native.target }}
- uses: Swatinem/rust-cache@v2 # see https://github.com/Swatinem/rust-cache
if: steps.boot.outputs.init == 'true'
- name: setup zig
if: steps.boot.outputs.init == 'true'
shell: bash
run: python3 -m pip install ziglang cargo-zigbuild
- name: modify $PATH
shell: bash
if: steps.boot.outputs.init == 'true'
run: |
{
site_packages="$(
python3 -c 'import sys, pathlib as p; [
print(i) for i in sys.path
if p.Path(i).name == "site-packages"
]'
)"
_sysroot="$(rustup run stable rustc --print sysroot)"
bins_dir="$_sysroot/lib/rustlib/${{ matrix.native.target }}/bin"
export PATH="$bins_dir:$site_packages/ziglang:$PATH"
echo "PATH=$PATH"
} | tee -a $GITHUB_ENV | tr ':' '\n' | sed 's/^/ - /g'
- name: "build native base bin: ${{ matrix.native.target }}"
shell: bash
if: steps.native-base-cache.outputs.cache-hit != 'true'
run: |
target="${{ matrix.native.target }}"
use_zig="${{ matrix.native.zig || 'false' }}"
export RUSTFLAGS="-Clink-args=-Wl,--build-id=sha1"
./scripts/build_bin.sh \
--version=base \
--profile=release \
--target="$target" \
--zig="$use_zig"
- name: "upload native base bin: ${{ matrix.native.target }}"
uses: actions/upload-artifact@v4
with:
name: cconvention-base-${{matrix.native.target}}
path: ./target/${{matrix.native.target}}/release/base_language_server*
if-no-files-found: error
- name: "build native pro bin: ${{ matrix.native.target }}"
shell: bash
if: steps.native-pro-cache.outputs.cache-hit != 'true'
run: |
target="${{ matrix.native.target }}"
use_zig="${{ matrix.native.zig || 'false' }}"
export RUSTFLAGS="-Clink-args=-Wl,--build-id=sha1"
./scripts/build_bin.sh \
--version=pro \
--profile=release \
--target="$target" \
--zig="$use_zig"
- name: "upload native pro bin: ${{ matrix.native.target }}"
uses: actions/upload-artifact@v4
with:
name: cconvention-pro-${{matrix.native.target}}
path: ./target/${{matrix.native.target}}/release/pro_language_server*
if-no-files-found: error
- name: "add rustup cross-compilation target: ${{ matrix.cross.target || 'none' }}"
if: steps.boot.outputs.cross_base == 'true' || steps.boot.outputs.cross_pro == 'true'
shell: bash
run: rustup target add ${{ matrix.cross.target }}
- name: "build cross base bin: ${{ matrix.cross.target || 'none' }}"
if: matrix.cross.target && steps.cross-base-cache.outputs.cache-hit != 'true'
shell: bash
run: |
target="${{ matrix.cross.target }}"
use_zig="${{ matrix.cross.zig || 'false' }}"
export RUSTFLAGS="-Clink-args=-Wl,--build-id=sha1"
./scripts/build_bin.sh \
--version=base \
--profile=release \
--target="$target" \
--zig="$use_zig"
- name: "upload cross base bin: ${{ matrix.cross.target }}"
if: matrix.cross.target
uses: actions/upload-artifact@v4
with:
name: cconvention-base-${{matrix.cross.target}} # TODO: include version in name?
path: ./target/${{matrix.cross.target}}/release/base_language_server*
if-no-files-found: error
- name: "build cross pro bin: ${{ matrix.cross.target || 'none' }}"
if: matrix.cross.target && steps.cross-pro-cache.outputs.cache-hit != 'true'
shell: bash
run: |
target="${{ matrix.cross.target }}"
use_zig="${{ matrix.cross.zig || 'false' }}"
export RUSTFLAGS="-Clink-args=-Wl,--build-id=sha1"
./scripts/build_bin.sh \
--version=pro \
--profile=release \
--target="$target" \
--zig="$use_zig"
- name: "upload cross pro bin: ${{ matrix.cross.target }}"
if: matrix.cross.target
uses: actions/upload-artifact@v4
with:
name: cconvention-pro-${{matrix.cross.target}}
path: ./target/${{matrix.cross.target}}/release/pro_language_server*
if-no-files-found: error
- name: debug
shell: bash
run: |
git diff
build-js:
name: Bundle VSCode extension JS
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # shallow clone for speed
- name: cache the built JS
uses: actions/cache@v4
id: built-base-js
with:
path: ./editors/code/base/dist/main.min.common.js
key: "built-js-for-${{ hashfiles('./editors/code/base/src/*.ts', './editors/code/base/pnpm-lock.yaml') }}"
- name: cache the built textmate grammar
uses: actions/cache@v4
id: textmate
with:
path: ./editors/code/base/dist/tmLanguage.json
key: "textmate-${{ hashfiles('./editors/code/base/src/tmLanguage.yaml') }}"
- uses: pnpm/action-setup@v2 # see https://github.com/pnpm/action-setup
if: steps.built-base-js.outputs.cache-hit != 'true' || steps.textmate.outputs.cache-hit != 'true'
with:
version: 8
- uses: actions/setup-node@v3 # see https://github.com/actions/setup-node/
if: steps.built-base-js.outputs.cache-hit != 'true' || steps.textmate.outputs.cache-hit != 'true'
with:
node-version: 20
cache: "pnpm"
cache-dependency-path: |
./editors/code/base/pnpm-lock.yaml
./editors/code/pro/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
working-directory: editors/code/base
if: steps.built-base-js.outputs.cache-hit != 'true' || steps.textmate.outputs.cache-hit != 'true'
- name: modify $PATH
shell: bash
working-directory: ./editors/code/base
run: |
export PATH=${PWD}/node_modules/.bin:$PATH
echo PATH=$PATH | tee -a $GITHUB_ENV
- name: build the textmate grammar if changed
if: steps.textmate.outputs.cache-hit != 'true'
shell: bash
working-directory: ./editors/code/base
run: ./scripts/build_textmate.sh
- name: upload the textmate grammar
uses: actions/upload-artifact@v4
with:
name: tmLanguage
path: ./editors/code/base/dist/tmLanguage.json
if-no-files-found: error
- name: build the JS if changed
shell: bash
if: steps.built-base-js.outputs.cache-hit != 'true'
working-directory: ./editors/code/base
run: ./scripts/build_js.sh
- name: upload bundled JS
uses: actions/upload-artifact@v4
with:
name: main-js
path: ./editors/code/base/dist/main.min.common.js
if-no-files-found: error
build-vsix:
name: Build and publish vsix
needs: [build-bin, build-js]
runs-on: ubuntu-latest
strategy:
fail-fast: false # to see what builds/what fails
matrix:
include:
- os: windows-latest
native:
target: x86_64-pc-windows-msvc
vscode-target: win32-x64
- os: ubuntu-latest
native:
target: x86_64-unknown-linux-gnu
vscode-target: linux-x64 # todo: fully-static MUSL?
cross:
target: aarch64-unknown-linux-gnu
vscode-target: linux-arm64
- os: macos-latest
native:
target: x86_64-apple-darwin
vscode-target: darwin-x64
cross:
target: aarch64-apple-darwin
vscode-target: darwin-arm64
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # shallow clone for speed
- name: "download native base binary: ${{ matrix.native.target }}"
uses: actions/download-artifact@v4
# https://github.com/actions/download-artifact
with:
name: cconvention-base-${{ matrix.native.target }}
path: ./target/${{ matrix.native.target }}/release/
- name: "download cross base binary: ${{ matrix.cross.target || '<skip>' }}"
uses: actions/download-artifact@v4
if: matrix.cross.target
with:
name: cconvention-base-${{ matrix.cross.target }}
path: ./target/${{ matrix.cross.target }}/release/
- name: "download native pro binary: ${{ matrix.native.target }}"
uses: actions/download-artifact@v4
# https://github.com/actions/download-artifact
with:
name: cconvention-pro-${{ matrix.native.target }}
path: ./target/${{ matrix.native.target }}/release/
- name: "download cross pro binary: ${{ matrix.cross.target || '<skip>' }}"
uses: actions/download-artifact@v4
if: matrix.cross.target
with:
name: cconvention-pro-${{ matrix.cross.target }}
path: ./target/${{ matrix.cross.target }}/release/
- name: "download bundled JS"
uses: actions/download-artifact@v4
with:
name: main-js
path: ./editors/code/base/dist/
- name: "download textmate grammar"
uses: actions/download-artifact@v4
with:
name: tmLanguage
path: ./editors/code/base/dist/
- name: "ensure dist directories exist"
shell: bash
run: |
set -eu
cd ./editors/code/
mkdir -p ./pro/dist
cp ./base/dist/main.min.common.js ./base/dist/tmLanguage.json ./pro/dist/
- name: derive paths
id: paths
run: |
case "${{ matrix.os }}" in
ubuntu-latest) bin_suffix="" ; debug_suffix=".debug" ;;
windows-latest) bin_suffix=".exe"; debug_suffix=".pdb" ;;
macos-latest) bin_suffix="" ; debug_suffix=".dSYM" ;;
esac
native_base_bin="${PWD}/target/${{ matrix.native.target }}/release/base_language_server$bin_suffix"
native_base_debug="${PWD}/target/${{ matrix.native.target }}/release/base_language_server$debug_suffix"
cross_base_bin="${PWD}/target/${{ matrix.cross.target }}/release/base_language_server$bin_suffix"
cross_base_debug="${PWD}/target/${{ matrix.cross.target }}/release/base_language_server$debug_suffix"
native_pro_bin="${PWD}/target/${{ matrix.native.target }}/release/pro_language_server$bin_suffix"
native_pro_debug="${PWD}/target/${{ matrix.native.target }}/release/pro_language_server$debug_suffix"
cross_pro_bin="${PWD}/target/${{ matrix.cross.target }}/release/pro_language_server$bin_suffix"
cross_pro_debug="${PWD}/target/${{ matrix.cross.target }}/release/pro_language_server$debug_suffix"
{
echo "native_base_bin=$native_base_bin"
echo "native_base_debug=$native_base_debug"
echo "cross_base_bin=$cross_base_bin"
echo "cross_base_debug=$cross_base_debug"
echo "native_pro_bin=$native_pro_bin"
echo "native_pro_debug=$native_pro_debug"
echo "cross_pro_bin=$cross_pro_bin"
echo "cross_pro_debug=$cross_pro_debug"
} | tee -a $GITHUB_OUTPUT
stat $native_base_bin || {
echo "$native_bin not found"
ls -al ./target/${{ matrix.native.target }}/release/
find ./target/${{ matrix.native.target }}/release/ -name 'base_language_server*'
}
stat $native_base_debug || echo "$native_debug not found"
stat $cross_base_bin || echo "$cross_bin not found"
stat $cross_base_debug || echo "$cross_debug not found"
- uses: pnpm/action-setup@v2 # see https://github.com/pnpm/action-setup
with:
version: 8
- uses: actions/setup-node@v3 # see https://github.com/actions/setup-node/
with:
node-version: 20
cache: "pnpm"
cache-dependency-path: |
./editors/code/base/pnpm-lock.yaml
./editors/code/pro/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
working-directory: editors/code/base
- name: modify $PATH
shell: bash
working-directory: ./editors/code/base
run: |
export PATH=${PWD}/node_modules/.bin:$PATH
echo PATH=$PATH | tee -a $GITHUB_ENV
- name: build the base ${{ matrix.native.vscode-target }} vsix
shell: bash
run: |
./scripts/build_vsix.sh \
--version=base \
--profile=release \
--target=${{ matrix.native.target }}
- name: upload the base ${{ matrix.native.vscode-target }} vsix
uses: actions/upload-artifact@v4
with:
name: cconvention-base-${{matrix.native.vscode-target}}.vsix
path: ./editors/code/base/dist/cconvention.${{ matrix.native.vscode-target }}.vsix
if-no-files-found: error
- name: build the base ${{ matrix.cross.vscode-target || '<missing cross>' }} vsix
if: matrix.cross.target
shell: bash
run: |
./scripts/build_vsix.sh \
--version=base \
--profile=release \
--target=${{ matrix.cross.target }}
- name: upload the base ${{ matrix.cross.vscode-target || '<missing cross>' }} vsix
if: matrix.cross.target
uses: actions/upload-artifact@v4
with:
name: cconvention-base-${{matrix.cross.vscode-target}}.vsix
path: ./editors/code/base/dist/cconvention.${{ matrix.cross.vscode-target }}.vsix
if-no-files-found: error
- name: publish the base ${{ matrix.cross.vscode-target || '<missing cross>' }} vsix
if: matrix.cross.target && startsWith(github.ref_name, 'v')
shell: bash
run: |
set -eu
cd ./editors/code/base
VSCE_PAT=${{ secrets.BASE_VSCE_PAT }} vsce publish \
--no-dependencies \
--no-git-tag-version \
--skip-duplicate \
--packagePath \
./dist/cconvention.${{matrix.cross.vscode-target}}.vsix
- name: build the pro ${{ matrix.native.vscode-target }} vsix
shell: bash
run: |
./scripts/build_vsix.sh \
--version=pro \
--profile=release \
--target=${{ matrix.native.target }}
- name: upload the pro ${{ matrix.native.vscode-target }} vsix
uses: actions/upload-artifact@v4
with:
name: cconvention-pro-${{matrix.native.vscode-target}}.vsix
path: ./editors/code/pro/dist/cconvention.${{ matrix.native.vscode-target }}.vsix
if-no-files-found: error
- name: build the pro ${{ matrix.cross.vscode-target || '<missing cross>' }} vsix
if: matrix.cross.target
shell: bash
run: |
./scripts/build_vsix.sh \
--version=pro \
--profile=release \
--target=${{ matrix.cross.target }}
- name: upload the pro ${{ matrix.cross.vscode-target || '<missing cross>' }} vsix
if: matrix.cross.target
uses: actions/upload-artifact@v4
with:
name: cconvention-pro-${{matrix.cross.vscode-target}}.vsix
path: ./editors/code/pro/dist/cconvention.${{ matrix.cross.vscode-target }}.vsix
if-no-files-found: error
- name: "check cross base debuginfo: ${{ matrix.cross.target || '<skip>'}}"
if: matrix.cross.target
run: |
if [ "${{ matrix.os }}" = "windows-latest" ]; then suffix=".exe";
else suffix=""
fi
bin="./target/${{ matrix.native.target }}/release/base_language_server$suffix"
sentry-cli debug-files check $bin
- name: "check native base debuginfo: ${{ matrix.native.target }}"
run: |
command -v sentry-cli || echo "missing sentry-cli"
bin=${{ steps.paths.outputs.native_base_bin }}
debug=${{ steps.paths.outputs.native_base_debug }}
echo bin=$bin
stat $bin || echo "$bin not found"
sentry-cli debug-files check $bin
if [ -e "$debug" ]; then
sentry-cli debug-files check $debug
else
echo "$debug not found"
fi
release:
name: Create GitHub release
runs-on: ubuntu-latest
needs: [build-vsix]
steps:
- name: Get the release version from the tag
id: "release_id"
if: env.VERSION == ''
shell: bash
run: |
semver_regex='^v[0-9]+\.[0-9]+\.[0-9]+$'
github_ref="${{ github.ref_name }}"
echo "ref=${github_ref}"
if (echo "$github_ref" | grep -qE "$semver_regex"); then
echo "VERSION=${github_ref/v/}" | tee -a "$GITHUB_ENV"
echo "ok=true" | tee -a "$GITHUB_OUTPUT"
else
echo "ok=false" | tee -a "$GITHUB_OUTPUT"
fi
- uses: actions/checkout@v4
if: steps.release_id.outputs.ok == 'true'
with:
fetch-depth: 1 # shallow clone for speed
- name: Check that tag version and Cargo.toml version are the same
shell: bash
if: steps.release_id.outputs.ok == 'true'
run: |
if ! grep -q "version = \"$VERSION\"" ./pkg/*/Cargo.toml; then
echo "version does not match Cargo.toml" >&2
exit 1
fi
- name: Create GitHub release
if: steps.release_id.outputs.ok == 'true'
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
run: gh release create "v$VERSION" --draft --verify-tag --title "v$VERSION"
- uses: actions/download-artifact@v4
if: steps.release_id.outputs.ok == 'true'
with:
pattern: "cconvention-*"
path: artifacts
- name: flatten directory
shell: bash
run: |
sudo apt install -y tree
tree ./artifacts
mkdir flattened
# since all arch-specific builds of the cli are named cconvention,
# rename them when moving them into the directory to upload
variants=(base pro)
triples=(
x86_64-unknown-linux-gnu
aarch64-unknown-linux-gnu
x86_64-apple-darwin
aarch64-apple-darwin
)
for variant in "${variants[@]}"; do
for triple in "${triples[@]}"; do
name="cconvention-${variant}-${triple}"
cp \
"./artifacts/${name}/${variant}_language_server" \
"./flattened/cconvention-${variant}-${triple}"
done
done
for variant in "${variants[@]}"; do
triple=x86_64-pc-windows-msvc
name="cconvention-${variant}-${triple}"
cp \
"./artifacts/${name}/${variant}_language_server.exe" \
"./flattened/cconvention-${variant}-${triple}.exe"
done
# all vsix variants have unique names
cp ./artifacts/**/*.vsix ./flattened/
- name: "checksums"
if: steps.release_id.outputs.ok == 'true'
shell: bash
run: |
cd flattened && shasum -a 256 * > checksums.txt
- name: "upload artifacts"
if: steps.release_id.outputs.ok == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
shell: bash
run: |
cd flattened &&
find . -type f -exec gh release upload "v$VERSION" '{}' ';'