CI #211
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: CI | |
on: | |
pull_request: | |
push: | |
branches: | |
- main | |
- master | |
workflow_dispatch: | |
inputs: | |
source: | |
description: 'Branch (user[/repo]:branch)' | |
type: string | |
required: false | |
schedule: | |
- cron: '15 3 * * *' | |
permissions: {} | |
defaults: | |
run: | |
# Use `bash` by default. Note that, according to the documentation, the | |
# real command is `bash --noprofile --norc -eo pipefail {0}`, which makes | |
# the shell catch any errors in pipelines. | |
shell: bash | |
concurrency: | |
# If the CI run is for a PR, allow a single concurrent run per PR and cancel | |
# all other runs for the same PR (e.g., if the PR was rebased) even when | |
# those runs are for different commits. If the CI run is for anything else | |
# (push, workflow_dispatch, schedule), allow a single concurrent run per | |
# commit and cancel other runs for the same commit. | |
# | |
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} | |
cancel-in-progress: true | |
env: | |
guix_cached_paths: | | |
~/.cache/guix | |
~/.cache/guix-install-action | |
~/.cache/guix-root | |
/gnu | |
/var/guix/db/db.sqlite* | |
/var/guix/gcroots | |
/var/guix/profiles | |
jobs: | |
build: | |
runs-on: ubuntu-latest | |
outputs: | |
strategy: ${{ steps.strategy.outputs.result }} | |
cache_name: ${{ steps.saved_cache_name.outputs.name }} | |
steps: | |
- id: strategy | |
name: Determine strategy for the test job | |
uses: actions/github-script@v7.0.1 | |
with: | |
script: | | |
// Default settings. | |
const defaultSource = [ | |
{ repo: "qmk/qmk_firmware", branch: "master" }, | |
// Not compatible with gcc-arm-none-eabi-7-2018-q2-update since | |
// https://github.com/qmk/qmk_firmware/pull/21656 had been merged | |
// (`_Alignas` in compound literals requires GCC >= 8.x). | |
// { repo: "qmk/qmk_firmware", branch: "develop" }, | |
]; | |
// Read workflow inputs. | |
let inputSource = ""; | |
if (context.eventName == "workflow_dispatch") { | |
const payload = context.payload; | |
const inputs = payload && payload.inputs; | |
inputSource = inputs && inputs.source && inputs.source.trim() || ""; | |
} | |
// Parse the `source` input. | |
let matrixSource = defaultSource; | |
if (inputSource != "") { | |
const sourceParts = inputSource.split(":", 2); | |
if (sourceParts.length == 2 ) { | |
let repoParts = sourceParts[0].split("/", 2); | |
if (repoParts.length == 1) { | |
repoParts.push("qmk_firmware"); | |
} | |
matrixSource = [ | |
{ repo: repoParts.join("/"), branch: sourceParts[1] }, | |
]; | |
} | |
} | |
// Determine build strategy. | |
const strategy = { | |
"fail-fast": false, | |
"matrix": { | |
"source": matrixSource, | |
}, | |
}; | |
// Print the resulting strategy to the log. | |
core.startGroup("Strategy for the test job:"); | |
core.info(JSON.stringify(strategy, null, 2)); | |
core.endGroup(); | |
// Return the strategy as the step output in the JSON format. | |
return strategy; | |
- name: Determine possible cache names | |
id: cache_name | |
run: | | |
# Format the cache name | |
base="guix" | |
date="$(date --utc +%Y%m%d)" | |
sha="${{github.sha}}" | |
echo "ok=${base}-ok-${date}-${sha}" >> $GITHUB_OUTPUT | |
echo "ok_date_only_prefix=${base}-ok-${date}-" >> $GITHUB_OUTPUT | |
echo "ok_any_prefix=${base}-ok-" >> $GITHUB_OUTPUT | |
echo "fail=${base}-fail-${date}-${sha}-${{github.run_id}}-${{github.run_attempt}}" >> $GITHUB_OUTPUT | |
echo "fail_same_run_id_prefix=${base}-fail-${date}-${sha}-${{github.run_id}}-" >> $GITHUB_OUTPUT | |
echo "fail_date_only_prefix=${base}-fail-${date}-" >> $GITHUB_OUTPUT | |
echo "any_prefix=${base}-" >> $GITHUB_OUTPUT | |
- name: Check whether the Guix cache needs updating | |
id: check_cache | |
uses: actions/cache/restore@v4.0.1 | |
with: | |
path: ${{ env.guix_cached_paths }} | |
key: ${{ steps.cache_name.outputs.ok }} | |
lookup-only: true | |
- name: Prepare the Guix store before restoring from cache | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
run: | | |
# Make the Guix store writable for the current user | |
sudo mkdir -p /gnu/store /var/guix/db /var/guix/gcroots /var/guix/profiles | |
sudo chown -R ${USER}: /gnu /var/guix | |
sudo chmod -R u+w /gnu /var/guix | |
- name: Restore the Guix store from cache | |
id: restore_cache | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
uses: actions/cache/restore@v4.0.1 | |
with: | |
path: ${{ env.guix_cached_paths }} | |
key: ${{ steps.cache_name.outputs.ok }} | |
restore-keys: | | |
${{ steps.cache_name.outputs.fail_same_run_id_prefix }} | |
${{ steps.cache_name.outputs.ok_date_only_prefix }} | |
${{ steps.cache_name.outputs.ok_any_prefix }} | |
${{ steps.cache_name.outputs.any_prefix }} | |
- name: Fix up the Guix store after restoring it from cache | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
run: | | |
# Fix up permissions for the Guix store when restoring from cache. | |
sudo chown -R root:root /gnu /var/guix | |
sudo chmod 755 /gnu | |
sudo chmod -R u-w /gnu/store | |
sudo chmod 1775 /gnu/store | |
# Move the Guix cache for root to the proper location (that cache is | |
# used to speed up `guix pull`). | |
if [ -d ~/.cache/guix-root ]; then | |
sudo mkdir -p ~root/.cache | |
sudo rm -rf ~root/.cache/guix | |
sudo mv ~/.cache/guix-root ~root/.cache/guix | |
sudo chown -R root:root ~root/.cache/guix | |
fi | |
# Make sure that all expected directories exist. | |
mkdir -p ~/.cache/guix | |
- name: Install Guix | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
uses: sigprof/guix-install-action@b677b02f4c9cced3fb63cfc7d00f8565f6b075ae | |
with: | |
channels: '%default-channels' | |
useExistingGuix: ${{ steps.restore_cache.outputs.cache-matched-key != '' }} | |
pullAfterInstall: >- | |
${{ | |
!startsWith(steps.restore_cache.outputs.cache-matched-key, steps.cache_name.outputs.ok_date_only_prefix) | |
&& !startsWith(steps.restore_cache.outputs.cache-matched-key, steps.cache_name.outputs.fail_date_only_prefix) | |
}} | |
- name: Verify that Guix works without warnings | |
id: guix_setup_check | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
run: test -z "$(guix --version 2>&1 >/dev/null)" | |
- name: Checkout the project source | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
uses: actions/checkout@v4.1.1 | |
- name: Build the Guix shell environment | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
run: guix shell -r ~/.cache/manifest-gcroot -m manifest.scm -- true | |
- name: Collect garbage | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
run: | | |
# Collect garbage | |
echo '::group::GC roots before the garbage collection' | |
sudo /var/guix/profiles/per-user/root/current-guix/bin/guix gc --list-roots | |
echo '::endgroup::' | |
echo '::group::Running GC' | |
sudo /var/guix/profiles/per-user/root/current-guix/bin/guix gc --delete-generations | |
echo '::endgroup::' | |
echo '::group::GC roots after the garbage collection' | |
sudo /var/guix/profiles/per-user/root/current-guix/bin/guix gc --list-roots | |
echo '::endgroup::' | |
- name: Build the Guix shell environment again | |
if: ${{ !steps.check_cache.outputs.cache-hit }} | |
run: | | |
# GC removes too much; this rebuild fetches those packages again. | |
guix shell -r ~/.cache/manifest-gcroot -m manifest.scm -- true | |
- name: Set cache name for a successful update | |
if: always() && !cancelled() && success() | |
run: echo 'final_cache_name=${{ steps.cache_name.outputs.ok}}' >> $GITHUB_ENV | |
- name: Set cache name for a failed update | |
if: always() && !cancelled() && !success() | |
run: echo 'final_cache_name=${{ steps.cache_name.outputs.fail}}' >> $GITHUB_ENV | |
- name: Determine the resulting cache name | |
id: saved_cache_name | |
if: always() && !cancelled() | |
run: echo "name=$final_cache_name" >> $GITHUB_OUTPUT | |
- name: Prepare to save the Guix store | |
id: guix_shutdown | |
if: >- | |
always() && !cancelled() | |
&& !steps.check_cache.outputs.cache-hit | |
&& (steps.guix_setup_check.outcome == 'success') | |
run: | | |
# Stop the Guix services. | |
sudo systemctl stop guix-daemon.service | |
sudo systemctl stop gnu-store.mount | |
# Move ~root/.cache/guix to an accessible place and adjust the | |
# permissions to make it readable for the cache action. | |
sudo mkdir -p ~root/.cache/guix | |
sudo mv ~root/.cache/guix ~/.cache/guix-root | |
sudo chown -R ${USER}: ~/.cache/guix-root | |
# Report uncompressed sizes of all cached paths. | |
set -f | |
set -- ${guix_cached_paths} | |
set +f | |
du -sh --total ${*/#\~/$HOME} | |
- name: Save the Guix store in the cache | |
if: >- | |
always() && !cancelled() | |
&& !steps.check_cache.outputs.cache-hit | |
&& (steps.guix_shutdown.outcome == 'success') | |
uses: actions/cache/save@v4.0.1 | |
with: | |
path: ${{ env.guix_cached_paths }} | |
key: ${{ steps.saved_cache_name.outputs.name }} | |
test: | |
needs: build | |
strategy: ${{ fromJSON(needs.build.outputs.strategy) }} | |
defaults: | |
run: | |
working-directory: qmk_firmware | |
runs-on: ubuntu-latest | |
steps: | |
- name: Prepare the Guix store before restoring from cache | |
working-directory: . | |
run: | | |
# Make the Guix store writable for the current user | |
sudo mkdir -p /gnu/store /var/guix/db /var/guix/gcroots /var/guix/profiles | |
sudo chown -R ${USER}: /gnu /var/guix | |
sudo chmod -R u+w /gnu /var/guix | |
- name: Restore the Guix store from cache | |
id: restore_cache | |
uses: actions/cache/restore@v4.0.1 | |
with: | |
path: ${{ env.guix_cached_paths }} | |
key: ${{ needs.build.outputs.cache_name }} | |
fail-on-cache-miss: true | |
- name: Fix up the Guix store after restoring it from cache | |
working-directory: . | |
run: | | |
# Fix up permissions for the Guix store when restoring from cache. | |
sudo chown -R root:root /gnu /var/guix | |
sudo chmod 755 /gnu | |
sudo chmod -R u-w /gnu/store | |
sudo chmod 1775 /gnu/store | |
# Move the Guix cache for root to the proper location (that cache is | |
# used to speed up `guix pull`). | |
if [ -d ~/.cache/guix-root ]; then | |
sudo mkdir -p ~root/.cache | |
sudo rm -rf ~root/.cache/guix | |
sudo mv ~/.cache/guix-root ~root/.cache/guix | |
sudo chown -R root:root ~root/.cache/guix | |
fi | |
- name: Install Guix | |
uses: sigprof/guix-install-action@b677b02f4c9cced3fb63cfc7d00f8565f6b075ae | |
with: | |
channels: '%default-channels' | |
useExistingGuix: true | |
pullAfterInstall: false | |
- name: Verify that Guix works without warnings | |
working-directory: . | |
run: test -z "$(guix --version 2>&1 >/dev/null)" | |
- name: Checkout the project source | |
uses: actions/checkout@v4.1.1 | |
with: | |
path: guix-qmk | |
- name: Build the Guix shell environment | |
working-directory: guix-qmk | |
run: guix shell -r ~/.cache/manifest-gcroot -m manifest.scm -- true | |
- name: Checkout the QMK firmware source code | |
uses: actions/checkout@v4.1.1 | |
with: | |
path: qmk_firmware | |
repository: ${{ matrix.source.repo }} | |
ref: ${{ matrix.source.branch }} | |
submodules: recursive | |
- name: Configure the 'upstream' remote | |
run: | | |
git remote add -t master -t develop upstream https://github.com/qmk/qmk_firmware | |
git fetch --depth=1 --no-tags --no-recurse-submodules upstream | |
- name: Configure the udev rules | |
if: ${{ runner.os == 'Linux' }} | |
run: sudo install -o root -g root -m 0644 util/udev/50-qmk.rules /etc/udev/rules.d/ | |
- name: Update submodules | |
run: guix shell -m ../guix-qmk/manifest.scm -- make git-submodule | |
- name: Test 'qmk doctor' | |
run: guix shell -m ../guix-qmk/manifest.scm -- qmk doctor | |
- name: Test 'qmk setup' | |
run: | | |
# Test 'qmk setup' | |
# 'qmk setup' does not return the exit code of 'qmk doctor', | |
# therefore grepping the text output is needed. | |
guix shell -m ../guix-qmk/manifest.scm -- qmk setup 2>&1 | tee qmk-setup.log | |
grep -q "QMK is ready to go" qmk-setup.log | |
- name: Test AVR build using 'make' | |
run: guix shell -m ../guix-qmk/manifest.scm -- make planck/rev5:default | |
- name: Test Arm build using 'make' | |
run: guix shell -m ../guix-qmk/manifest.scm -- make planck/rev6:default | |
- name: Test 'make clean' | |
run: guix shell -m ../guix-qmk/manifest.scm -- make clean | |
- name: Force clean before testing 'qmk compile' | |
run: git clean -fdx | |
- name: Test AVR build using 'qmk compile' | |
run: guix shell -m ../guix-qmk/manifest.scm -- qmk compile -kb planck/rev5 -km default | |
- name: Test Arm build using 'qmk compile' | |
run: guix shell -m ../guix-qmk/manifest.scm -- qmk compile -kb planck/rev6 -km default | |
- name: Test 'qmk clean' | |
run: guix shell -m ../guix-qmk/manifest.scm -- qmk clean | |
finish: | |
needs: | |
- build | |
- test | |
runs-on: ubuntu-latest | |
if: always() | |
env: | |
ci_success: >- | |
${{ | |
(needs.build.result == 'success') | |
&& (needs.test.result == 'success') | |
}} | |
steps: | |
- name: Report CI status | |
run: $ci_success |