Skip to content

Build

Build #390

Workflow file for this run

name: Build
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# ZETTLR MAIN BUILD GITHUB ACTIONS FILE #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# This file contains the logic necessary to build a full release from source. #
# #
# WHEN IT RUNS: #
# #
# * Whenever someone successfully pushes develop --> master. #
# * Every Monday at noon (UTC) #
# * Manually #
# #
# The last two events are being used to create nightly releases. The first #
# event type indicates that the pipeline should build a regular release. #
# #
# HOW IT RUNS: #
# #
# 1. Three virtual machines are spun up in parallel: A Windows VM, a Ubuntu #
# VM, and a macOS VM. All three are tasked with building all releases that #
# we offer for the given platform. The Ubuntu VM builds the most since we #
# have many more Linux releases. Windows and macOS only build one x64 and #
# one ARM release. However, they also perform code signing (and #
# notarization in case of the macOS VM), which the Ubuntu VM doesn't do. #
# 2. Each VM uploads all resulting artifacts into their respective artifact #
# space, named "linux", "darwin", and "win32" respectively (analogous to #
# node.js's process.platform-variable). #
# 3. Iff (if and only if) all three runners have shut down successfully, #
# indicating successful builds, another Ubuntu VM will be started. That #
# one is tasked with downloading all releases from the three artifact #
# spaces, calculate the SHA-256 checksums for every file and afterwards #
# upload all files: In case of a nightly to the Zettlr server, where they #
# can be downloaded from nightly.zettlr.com, or, in case of a regular #
# release, to a newly created release draft here on GitHub. #
# #
# THINGS TO NOTE: #
# #
# * Every runner will retrieve the package.json's "version" field by running #
# a somewhat weird-looking command. This is necessary to make finding the #
# releases easier which are being produced by electron-forge and electron- #
# builder. #
# * Especially the macOS runner can often produce problems, because there #
# have been rumors that apparently even the macOS server operating systems #
# tend to open modal windows -- even though they're running headless. This #
# means that in case the macOS runners seem to hang it's likely we again #
# need to update the add-osx-cert.sh script. #
# * Everything is being run on Bash. Although PowerShell can do a lot, this #
# way all steps are within one language, making it easier to maintain. #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
on:
push:
branches:
- master
workflow_dispatch: {} # Used to manually trigger a nightly build.
schedule: # Every Monday at noon (UTC) we run a scheduled nightly build
- cron: '0 12 * * 1'
# Defaults for every job and step in this workflow
defaults:
run:
shell: bash # Run everything using bash
# Global environment variables
env:
NODE_VERSION: '20'
# Ensure only a single build workflow runs at any one time
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
# This workflow file contains four jobs, three to build the corresponding
# releases on all three supported platforms, and a last one, which will
# create the release draft.
jobs:
# First, ensure that nothing breaks before attempting to build
preflight:
name: 'Preflight: Lint/Unit tests'
uses: ./.github/workflows/check.yml
# If we're building a full release, we have to make sure that nobody (that is:
# Hendrik) forgot to increment the tag version. If we already have a tag with
# this version, we should not proceed, because that will then lead to an
# overwriting of the files in the previous release. That doesn't wreak any
# havoc because we don't push unstable code to master, BUT it looks bad.
verify_version:
name: 'Preflight: Verify version tag'
runs-on: ubuntu-22.04
steps:
- name: Clone repository (master branch)
uses: actions/checkout@v4
with:
ref: 'master'
fetch-depth: 0 # Required for the tag verification step.
- name: Setup NodeJS ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: Retrieve tag version
id: ref
run: echo "version=$(cat package.json | jq -r '.version')" >> $GITHUB_ENV
- name: Verify the corresponding tag does not yet exist
if: github.ref_name == 'master'
# NOTE: the ! will return 0 if the command fails, and 1 otherwise
run: '! git rev-parse "v${{env.version}}"'
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# WINDOWS BUILDS #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
build_win_x64:
name: Windows (x86_64)
env:
npm_config_arch: x64
needs: [preflight, verify_version]
runs-on: windows-latest
steps:
- name: Checkout branch ${{ github.ref_name }}
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: Setup NodeJS ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: Set up build environment
run: yarn install --immutable
- name: Set tag version to nightly
if: github.ref_name == 'develop'
# run: echo "$(cat package.json | jq --arg commit "$(git rev-parse --short HEAD)" '.version = .version + "-nightly+" + $commit')" > package.json
run: echo "$(cat package.json | jq '.version = .version + "-nightly"')" > package.json
- name: Retrieve tag version
id: ref
run: echo "version=$(cat package.json | jq -r '.version')" >> $GITHUB_ENV
- name: Package & Release
run: yarn package:win-x64 && yarn release:win-x64
env:
CSC_LINK: ${{ secrets.WIN_CERT_2025_03_15 }}
CSC_KEY_PASSWORD: ${{ secrets.WIN_CERT_PASS_2025_03_15 }}
- name: Cache installer
uses: actions/upload-artifact@v4
with:
name: win32_x64
path: |
./release/Zettlr-${{env.version}}-x64.exe
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# MACOS BUILDS #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
build_macos_x64:
name: macOS (x86_64)
env:
npm_config_arch: x64
needs: [preflight, verify_version]
runs-on: macos-latest
steps:
# Check out master for a regular release, or develop branch for a nightly
- name: Checkout branch ${{ github.ref_name }}
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: Setup NodeJS ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: Set up build environment
run: yarn install --immutable
- name: Set tag version to nightly
if: github.ref_name == 'develop'
# run: echo "$(cat package.json | jq --arg commit "$(git rev-parse --short HEAD)" '.version = .version + "-nightly+" + $commit')" > package.json
run: echo "$(cat package.json | jq '.version = .version + "-nightly"')" > package.json
- name: Retrieve tag version
id: ref
run: echo "version=$(cat package.json | jq -r '.version')" >> $GITHUB_ENV
# Save the macOS certificate on this runner for forge to access it in the
# next step below.
- name: Retrieve code signing certificate
run: ./scripts/add-osx-cert.sh
env:
MACOS_CERT: ${{ secrets.MACOS_CERT }}
MACOS_CERT_PASS: ${{ secrets.MACOS_CERT_PASS }}
- name: Package
run: yarn package:mac-x64
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASS: ${{ secrets.APPLE_ID_PASS }}
- name: Release
run: yarn release:mac-x64
- name: Cache image file
uses: actions/upload-artifact@v4
with:
name: darwin_x64
path: |
./release/Zettlr-${{env.version}}-x64.dmg
build_macos_arm64:
name: macOS (arm64)
env:
npm_config_arch: arm64
needs: [preflight, verify_version]
runs-on: macos-latest
steps:
- name: Checkout branch ${{ github.ref_name }}
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: Setup NodeJS ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: Set up build environment
run: yarn install --immutable
- name: Set tag version to nightly
if: github.ref_name == 'develop'
# run: echo "$(cat package.json | jq --arg commit "$(git rev-parse --short HEAD)" '.version = .version + "-nightly+" + $commit')" > package.json
run: echo "$(cat package.json | jq '.version = .version + "-nightly"')" > package.json
- name: Retrieve tag version
id: ref
run: echo "version=$(cat package.json | jq -r '.version')" >> $GITHUB_ENV
# Save the macOS certificate on this runner for forge to access it in the
# next step below.
- name: Retrieve code signing certificate
run: ./scripts/add-osx-cert.sh
env:
MACOS_CERT: ${{ secrets.MACOS_CERT }}
MACOS_CERT_PASS: ${{ secrets.MACOS_CERT_PASS }}
- name: Package
run: yarn package:mac-arm
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASS: ${{ secrets.APPLE_ID_PASS }}
- name: Release
run: yarn release:mac-arm
- name: Cache image file
uses: actions/upload-artifact@v4
with:
name: darwin_arm64
path: |
./release/Zettlr-${{env.version}}-arm64.dmg
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# LINUX BUILDS #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
build_linux_x64:
name: Linux (x86_64)
env:
npm_config_arch: x64
needs: [preflight, verify_version]
runs-on: ubuntu-22.04
steps:
# Check out master for a regular release, or develop branch for a nightly
- name: Checkout branch ${{ github.ref_name }}
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: Setup NodeJS ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: Set up build environment
run: yarn install --immutable
- name: Set tag version to nightly
# If this a scheduled workflow, we know that we should build a nightly
if: github.ref_name == 'develop'
# run: echo "$(cat package.json | jq --arg commit "$(git rev-parse --short HEAD)" '.version = .version + "-nightly+" + $commit')" > package.json
run: echo "$(cat package.json | jq '.version = .version + "-nightly"')" > package.json
- name: Retrieve tag version
id: ref
run: echo "version=$(cat package.json | jq -r '.version')" >> $GITHUB_ENV
- name: Package & Release
run: |
yarn package:linux-x64
yarn release:linux-x64
- name: Cache installers
uses: actions/upload-artifact@v4
with:
name: linux_x64
path: |
./release/Zettlr-${{env.version}}-amd64.deb
./release/Zettlr-${{env.version}}-x86_64.rpm
./release/Zettlr-${{env.version}}-x86_64.AppImage
build_linux_arm64:
name: Linux (arm64)
env:
npm_config_arch: arm64
CC: zig cc -target aarch64-linux-gnu
CXX: zig c++ -target aarch64-linux-gnu
needs: [preflight, verify_version]
runs-on: ubuntu-22.04
steps:
- name: Checkout branch ${{ github.ref_name }}
uses: actions/checkout@v4
with:
ref: ${{ github.ref_name }}
- name: Setup NodeJS ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
- name: Set up build environment
run: yarn install --immutable
- name: Set tag version to nightly
if: github.ref_name == 'develop'
# run: echo "$(cat package.json | jq --arg commit "$(git rev-parse --short HEAD)" '.version = .version + "-nightly+" + $commit')" > package.json
run: echo "$(cat package.json | jq '.version = .version + "-nightly"')" > package.json
- name: Retrieve tag version
id: ref
run: echo "version=$(cat package.json | jq -r '.version')" >> $GITHUB_ENV
- name: Package & Release
run: |
yarn package:linux-arm
yarn release:linux-arm
- name: Cache installers
uses: actions/upload-artifact@v4
with:
name: linux_arm64
path: |
./release/Zettlr-${{env.version}}-arm64.deb
./release/Zettlr-${{env.version}}-aarch64.rpm
./release/Zettlr-${{env.version}}-arm64.AppImage
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# PREPARE RELEASE DRAFT #
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# After the three builds, this job downloads all assets, creates and verifies
# SHA256 checksums, and finally creates a release draft and uploads all
# assets to it. NOTE: If the workflow detects a nightly is being built, this
# step rather uploads the binaries to the Zettlr server instead of creating
# a release draft.
prepare_release:
name: Prepare release draft
# Make sure (and wait until) the builds have succeeded
needs:
- build_win_x64
# - build_win_arm64 # DEBUG: Disable win arm for now
- build_macos_x64
- build_macos_arm64
- build_linux_x64
- build_linux_arm64
runs-on: ubuntu-22.04
steps:
- name: Checkout branch ${{ github.ref_name }}
uses: actions/checkout@v4
- name: Setup NodeJS ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: Set up build environment
run: yarn install --immutable
- name: Set tag version to nightly
if: github.ref_name == 'develop'
# run: echo "$(cat package.json | jq --arg commit "$(git rev-parse --short HEAD)" '.version = .version + "-nightly+" + $commit')" > package.json
run: echo "$(cat package.json | jq '.version = .version + "-nightly"')" > package.json
- name: Retrieve tag version
id: ref
run: echo "version=$(cat package.json | jq -r '.version')" >> $GITHUB_ENV
- name: Make release directory
run: mkdir ./release
# Download all resulting assets from the previous steps.
- name: Retrieve installers (Windows x86_64)
uses: actions/download-artifact@v4
with:
name: win32_x64
path: ./release
- name: Retrieve installers (macOS x86_64)
uses: actions/download-artifact@v4
with:
name: darwin_x64
path: ./release
- name: Retrieve installers (macOS arm64)
uses: actions/download-artifact@v4
with:
name: darwin_arm64
path: ./release
- name: Retrieve installers (Linux x86_64)
uses: actions/download-artifact@v4
with:
name: linux_x64
path: ./release
- name: Retrieve installers (Linux arm64)
uses: actions/download-artifact@v4
with:
name: linux_arm64
path: ./release
# Generate the checksums
- name: Generate SHA256 checksums
# sha256sum "Zettlr-${{env.version}}-arm64.exe" >> "SHA256SUMS.txt" # DEBUG: Disable win arm for now
run: |
cd ./release
sha256sum "Zettlr-${{env.version}}-x64.exe" > "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-x64.dmg" >> "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-arm64.dmg" >> "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-x86_64.AppImage" >> "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-arm64.AppImage" >> "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-amd64.deb" >> "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-arm64.deb" >> "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-x86_64.rpm" >> "SHA256SUMS.txt"
sha256sum "Zettlr-${{env.version}}-aarch64.rpm" >> "SHA256SUMS.txt"
cd ..
- name: Verify checksums
run: |
cd ./release
sha256sum -c SHA256SUMS.txt
cd ..
# IF WE BUILD A NIGHTLY, AT THIS POINT JUST UPLOAD TO THE SERVER.
# We must make sure to copy the three additional files
# to the release folder because of the --delete flag in rsync below.
- name: Copy Nightly Static Files
if: github.ref_name == 'develop'
run: |
cp ./scripts/assets/nightly-index.php ./release/index.php
cp ./scripts/assets/nightly-sm_preview.png ./release/sm_preview.png
cp ./resources/icons/png/512x512.png ./release/logo.png
- name: Upload nightlies to the server
if: github.ref_name == 'develop'
uses: easingthemes/ssh-deploy@v3.0.1
env:
SSH_PRIVATE_KEY: ${{ secrets.NIGHTLY_SSH_PRIVATE_KEY }}
# --delete so that no old releases remain on the server. Each iteration is approx. 1GB in size.
ARGS: "-vzhr --delete" # verbose, compress, human-readble, recursive, delete
SOURCE: "release/"
REMOTE_HOST: ${{ secrets.NIGHTLY_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.NIGHTLY_REMOTE_USER }}
TARGET: ${{ secrets.NIGHTLY_TARGET }}
# OTHERWISE: Create a new release draft
- name: Create release draft
if: github.ref_name == 'master'
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Populate the inputs of the release we already know
tag_name: v${{env.version}}
name: Release v${{env.version}}
body: If you can read this, we have forgotten to fill in the changelog. Sorry!
draft: true # Always create as draft, so that we can populate the remaining values easily
# Gosh, is that convenient as opposed to earlier!
files: |
./release/Zettlr-${{env.version}}-x64.exe
./release/Zettlr-${{env.version}}-arm64.exe
./release/Zettlr-${{env.version}}-x64.dmg
./release/Zettlr-${{env.version}}-arm64.dmg
./release/Zettlr-${{env.version}}-amd64.deb
./release/Zettlr-${{env.version}}-arm64.deb
./release/Zettlr-${{env.version}}-x86_64.rpm
./release/Zettlr-${{env.version}}-aarch64.rpm
./release/Zettlr-${{env.version}}-x86_64.AppImage
./release/Zettlr-${{env.version}}-arm64.AppImage
./release/SHA256SUMS.txt