Skip to content

thermal: it's PARTY TIME!!! #221

thermal: it's PARTY TIME!!!

thermal: it's PARTY TIME!!! #221

Workflow file for this run

name: CI
on:
pull_request: {}
push:
branches: [master]
# For pull requests only, cancel the previous build when a new commit is pushed. Since unfortunately
# it's not possible to only apply this to pull requests, for pull request events we use the ref
# (`refs/pulls/NUMBER/merge`, which gets reused across builds for the same PR), and for pushes we
# use the commit sha (which should never have two builds in the default branch running at a time).
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: true
# Define permissions at the job level.
permissions: {}
jobs:
dist:
name: ${{ matrix.name }}
permissions:
contents: read
id-token: write
attestations: write
strategy:
matrix:
include:
- os: ubuntu-latest
name: Linux
- os: windows-latest
name: Windows
uses: ./.github/workflows/build-boards.yml
with:
os: ${{ matrix.os }}
upload-artifacts: ${{ matrix.os == 'ubuntu-latest' }}
license:
name: Check licensing
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout the source code
uses: actions/checkout@v6
- name: Check License Header
uses: apache/skywalking-eyes/header@501a28d2fb4a9b962661987e50cf0219631b32ff
tests:
name: Run tests
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout the source code
uses: actions/checkout@v6
- name: Run tests
run: cargo test --verbose --workspace
env:
CARGO_TERM_COLOR: always
format:
name: Check formatting
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout the source code
uses: actions/checkout@v6
- name: cargo fmt
run: cargo fmt --all --check
docs-build:
name: Build documentation
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout the source code
uses: actions/checkout@v6
- name: Create output directories
run: |
mkdir -p output/reference
mkdir -p output/bugs
- name: Copy static website files
run: |
cp website/index.html output/index.html
cp website/style.css output/style.css
cp website/bugs/index.html output/bugs/index.html
- name: Generate reference
uses: tonynv/asciidoctor-action@master
with:
program: asciidoctor doc/index.adoc -o output/reference/index.html
- name: Upload content as an artifact
uses: actions/upload-pages-artifact@v4
with:
path: output/
retention-days: 90 # We might want to inspect this in a PR.
docs-deploy:
name: Deploy documentation
runs-on: ubuntu-slim
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
needs:
- docs-build
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
id: deployment
# Hubris builds have to be reproducible, and we want to test that in CI. This job does a build of
# a few boards with the standard Ubuntu image and no interference, as the baseline to compire to.
reproducible-a:
name: Reproducibility (A, ${{ matrix.name }})
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix: &reproducibility-matrix
include:
- name: cosmo-b
image: default
app_toml: app/cosmo/rev-b.toml
- name: oxide-rot-1
image: a
app_toml: app/oxide-rot-1/app.toml
steps:
- name: Checkout the source code
uses: actions/checkout@v6
# We check explicitly to ensure the other job has a different one.
- name: Check that GCC is the system C toolchain
run: cc --version | grep -q "Free Software Foundation"
- name: Build a Hubris board
run: |
umask 0007 # We set the umask explicitly here to ensure the other job has a different one.
cargo xtask dist "$app_toml"
env:
app_toml: ${{ matrix.app_toml }}
- name: Upload the artifact to be later checked
uses: actions/upload-artifact@v6
with:
name: reproducible-${{ matrix.name }}-a
path: target/${{ matrix.name }}/dist/${{ matrix.image }}/build-${{ matrix.name }}-image-${{ matrix.image }}.zip
if-no-files-found: error
# Hubris builds have to be reproducible, and we want to test that in CI. This job does a build of
# a few boards trying to change the build environment as further as possible from reproducible-a.
# Each variability we introduce and its reasoning is documented in comments below.
#
# While this is not a guarantee that things are reproducible, this should catch most of the usual
# sources of nondeterminism within build systems and toolchains.
#
# More information on common variations are available on the reproducible-builds website:
# https://reproducible-builds.org/docs/env-variations/
reproducible-b:
name: Reproducibility (B, ${{ matrix.name }})
runs-on: ubuntu-latest
permissions:
contents: read
strategy:
matrix: *reproducibility-matrix
env:
CUSTOM_ROOT: /very/long/path/we/are/doing/the/build/in/to/check/for/issues/with/long/paths/or/different/paths
steps:
- name: Install Ubuntu dependencies
run: |
sudo apt-get update
sudo apt-get install -y disorderfs clang
sudo apt-get remove -y gcc
sudo apt-get autoremove -y
# In the Ubuntu dependencies installation step above we switched from GCC to Clang as the
# system C toolchain and linker. We are not using the system linker in the build process
# (Hubris uses the LLD copy bundled with Rust), so switching the system toolchain will catch
# us accidentally relying on it (and breaking reproducibility depending on which toolchain is
# installed on the system attempting to reproduce).
- name: Check that clang is the system C toolchain
run: |
! command -v gcc >/dev/null
cc --version | grep -q clang
- name: Checkout the source code in the standard GitHub Actions directory
uses: actions/checkout@v6
# We run the Hubris build in a different directory, to ensure that paths are not hardcoded. We
# also use a very long path, as a reproducibility issue Emily found in the wild in the past
# was a rust-lang/rust test failing when built in a path that was too long.
#
# We also use disorderfs to randomize the ordering of listing directories, to catch code
# assuming directory entries are always returned in the same order.
- name: Prepare a custom build root directory with disorderfs
run: |
sudo mkdir -p $CUSTOM_ROOT
sudo disorderfs --multi-user=yes --shuffle-dirents=yes $(pwd) $CUSTOM_ROOT
# The current time might be included in the built artifacts. To ensure reproducibility, move
# the time forward by a day and a few hours. This should be enough to expose differences in
# the build without messing with TLS certificate expiration.
#
# Note that this causes very funny behavior in GitHub Action's workflow UI, as apparently step
# duration estimates are based on time reported by the runner???
- name: Move forward in time to ensure a different build date
run: |
sudo timedatectl set-ntp false
sudo timedatectl set-time "$(date -d '1 day ago 11 hours ago' "+%Y-%m-%d %H:%M:%S")"
date
- name: Build a Hubris board
run: |
# Permissions of files created during the build process might leak into the artifacts.
# Changing the umask will let us test with different permissions than archives created in
# the reproducible-a job.
umask 0077
cd $CUSTOM_ROOT
cargo xtask dist "$app_toml"
env:
app_toml: ${{ matrix.app_toml }}
- name: Move back to the right time
run: sudo timedatectl set-ntp true
- name: Upload the artifact to be later checked
uses: actions/upload-artifact@v6
with:
name: reproducible-${{ matrix.name }}-b
path: ${{ env.CUSTOM_ROOT }}/target/${{ matrix.name }}/dist/${{ matrix.image }}/build-${{ matrix.name }}-image-${{ matrix.image }}.zip
if-no-files-found: error
reproducible-check:
name: Reproducibility check (${{ matrix.name }})
runs-on: ubuntu-slim
needs:
- reproducible-a
- reproducible-b
strategy:
fail-fast: false # We want all reports even if one fails.
matrix: *reproducibility-matrix
permissions: {}
steps:
- name: Install uv (Python package manager)
uses: astral-sh/setup-uv@v7
with:
enable-cache: false
ignore-empty-workdir: true
- name: Download reproducible artifacts
uses: actions/download-artifact@v7
with:
pattern: reproducible-${{ matrix.name }}-*
# Diffoscope is a tool built by the reproducible-builds people to do a rich format-aware diff
# of two files. For example, it understands both zip archives and ELF objects, so it can point
# out the member of archive or the ELF section containing the difference.
#
# We are pulling diffoscope from PyPI transparently through `uvx`, instead of installing it
# from the Ubuntu archives with `apt-get`. We're doing this because due to (mostly sensible)
# packaging choice installing the Ubuntu package takes 6+ minutes, compared to the sub-second
# installation time `uvx` provides. The fact we get a newer version doesn't hurt either.
#
# If you are curious, the reason why the Ubuntu package takes so long to install is because
# diffoscope can produce better diffs the more CLI tools are installed, and the Ubuntu package
# depends on all of those tools. We don't really care about all of them, and the barebones
# version installed through PyPI is enough for us.
- name: Compare the two reproducible artifacts
run: uvx diffoscope --html report.html "reproducible-$name-a/build-$name-image-$image.zip" "reproducible-$name-b/build-$name-image-$image.zip"
env:
name: ${{ matrix.name }}
image: ${{ matrix.image }}
- name: Upload the diffoscope report
if: failure() # Only upload the report if the previous step failed.
id: diffoscope-report
uses: actions/upload-artifact@v6
with:
name: reproducible-diffoscope-report-${{ matrix.name }}
path: report.html
if-no-files-found: error
- name: Add a job summary to point folks to diffoscope
if: failure()
run: echo "Non-reproducibility was detected by CI in $name. [Download the diffoscope report]($report_url) to learn more" >> $GITHUB_STEP_SUMMARY
env:
name: ${{ matrix.name }}
report_url: ${{ steps.diffoscope-report.outputs.artifact-url }}
finish:
name: CI finished
runs-on: ubuntu-slim
permissions: {}
needs:
- dist
- license
- tests
- format
- docs-build
- docs-deploy
- reproducible-check
if: "${{ !cancelled() }}"
steps:
- name: Calculate the correct exit status
run: echo $needs | jq --exit-status 'all(.result == "success" or .result == "skipped")'
env:
needs: ${{ toJson(needs) }}