Compare commits

..
Author SHA1 Message Date
druppy 60996de947 scope probe: instance-wide hook vs repo hook
Build RPi image / build (push) Successful in 9s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Sandbox - cmake + ccache on the pool / build (push) Successful in 11s
2026-10-24 23:08:01 +02:00
druppy 1ebfa86dc6 post-migration health check
Sandbox - cmake + ccache on the pool / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 10s
2026-10-24 23:01:20 +02:00
druppy a410495daa second cmake run: expect ccache hits
Sandbox - cmake + ccache on the pool / build (push) Successful in 11s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 10s
2026-10-24 22:59:36 +02:00
druppy ba41deb9ec Add sandbox_c cmake project + ccache workflow for pool testing
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 1m27s
Sandbox - cmake + ccache on the pool / build (push) Successful in 10s
2026-10-24 22:54:55 +02:00
druppy fb253f9b27 verify repo-based install end to end
Build RPi image / build (push) Successful in 9s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 1m28s
2026-10-24 22:53:25 +02:00
druppy 2ba284f326 backstop probe: no webhook available
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 10s
2026-10-24 22:34:19 +02:00
druppy 064818c973 latency probe: webhook-woken poll
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 9s
2026-10-24 22:32:41 +02:00
druppy c182c8d4fc latency probe: webhook-woken poll
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 9s
Build RPi image / build (push) Successful in 10s
2026-10-24 22:32:35 +02:00
druppy a2c8f59b5c hybrid test: webhook wakes, poll decides
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 9s
2026-10-24 22:31:57 +02:00
druppy 9880ee36cd default-branch run: do workflow events fire?
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 9s
2026-10-24 22:23:02 +02:00
druppy 7d6c6783e9 webhook verify against allowed host
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 10s
2026-10-24 22:21:22 +02:00
druppy acc061e752 webhook verify after egress fix
Build RPi image / build (push) Successful in 9s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 9s
2026-10-24 22:20:21 +02:00
druppy c1293bda4c webhook test: fresh hook, correct URL
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 10s
2026-10-24 22:12:09 +02:00
druppy f4db1e7013 scale B
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 10s
2026-10-24 21:46:49 +02:00
druppy 79ea99e939 scale A
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
2026-10-24 21:46:49 +02:00
druppy 45d3d3c91f Show full cargo test output
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 10s
Build RPi image / build (push) Successful in 10s
2026-10-24 21:43:55 +02:00
druppy 92f6412539 trigger: prove cache survives the runner that wrote it
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 9s
2026-10-24 21:40:48 +02:00
druppy 07b072e5b7 Use absolute cargo path in toolchain step too
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 32s
2026-10-24 21:37:28 +02:00
druppy 1acdc44f88 Call cargo by absolute path (PATH did not carry over between steps)
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Failing after 10s
2026-10-24 21:33:57 +02:00
druppy 393bac993d Export cargo bin PATH directly; GITHUB_PATH did not propagate
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Failing after 10s
2026-10-24 21:30:48 +02:00
druppy ff2270a64d Cache rustup+registry+target, reach cache server via LAN IP, fail on pipe errors
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Failing after 10s
2026-10-24 21:27:35 +02:00
druppy f45ca8ff95 trigger: exercise ephemeral podman runner
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Successful in 12s
2026-10-24 21:25:46 +02:00
druppy 37d70c40c5 Add sandbox crate + workflow to test ephemeral podman runners
Build RPi image / build (push) Successful in 10s
Sandbox - ephemeral runner + shared cache / cargo (push) Failing after 2s
2026-10-24 21:23:30 +02:00
13 changed files with 484 additions and 0 deletions

No files matched your search

+61
View File
@@ -0,0 +1,61 @@
name: Sandbox - cmake + ccache on the pool
on:
push:
branches: [ci/sandbox]
workflow_dispatch:
env:
# Inside the workspace so actions/cache can store it. The path is identical on
# every run, which matters because ccache hashes the compiler command line.
CCACHE_DIR: ${{ github.workspace }}/.ccache
jobs:
build:
runs-on: cmake
defaults:
run:
shell: bash
working-directory: sandbox_c
steps:
- uses: actions/checkout@v4
- name: Toolchain
run: |
set -euo pipefail
command -v cmake >/dev/null && command -v ccache >/dev/null && exit 0
apt-get update -qq
apt-get install -y -qq --no-install-recommends \
cmake ninja-build gcc libc6-dev ccache ca-certificates >/dev/null
cmake --version | head -1
ccache --version | head -1
# Cache the compiler cache, never build/: CMake bakes absolute paths into
# CMakeCache.txt, so a restored build tree breaks if the workspace moves.
- name: Cache ccache
uses: actions/cache@v4
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ hashFiles('sandbox_c/**') }}
restore-keys: ccache-
- name: ccache before build
run: ccache --show-stats | grep -E "cache (hits|misses|read)" || ccache --show-stats
- name: Configure
run: |
cmake -S . -B build -G Ninja \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --parallel
- name: Test
run: ctest --test-dir build --output-on-failure
- name: ccache after build
if: always()
run: |
ccache --show-stats
set -o pipefail
ctest --test-dir build 2>&1 | grep -E "tests passed"
+49
View File
@@ -0,0 +1,49 @@
name: Sandbox - ephemeral runner + shared cache
on:
push:
branches: [ci/sandbox]
workflow_dispatch:
jobs:
cargo:
runs-on: sandbox
defaults:
run:
working-directory: sandbox
shell: bash
steps:
- uses: actions/checkout@v4
- name: Where am I?
run: |
echo "runner : ${RUNNER_NAME}"
echo "uid : $(id -u) cgroup: $(cut -d/ -f3 /proc/self/cgroup 2>/dev/null || echo n/a)"
echo "cache : ${ACTIONS_CACHE_URL:-unset}"
# Restore first, so a warm cache also skips the toolchain download.
- name: Cache rust toolchain, registry and target
uses: actions/cache@v4
with:
path: |
~/.cargo
~/.rustup
sandbox/target
key: cargo-${{ hashFiles('sandbox/Cargo.lock') }}
restore-keys: cargo-
- name: Toolchain
run: |
set -euo pipefail
if ! command -v cargo >/dev/null && [ ! -x "$HOME/.cargo/bin/cargo" ]; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable
fi
# runner v5.2.2 does not apply GITHUB_PATH to later steps, so use absolute paths
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/cargo" --version
- name: Test
run: |
set -o pipefail # without this a failing build looks green
CARGO="$HOME/.cargo/bin/cargo"
echo "HOME=$HOME cargo there: $([ -x "$CARGO" ] && echo yes || echo no)"
"$CARGO" test 2>&1 | tail -22
+214
View File
@@ -0,0 +1,214 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anyhow"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
[[package]]
name = "cfg-if"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
[[package]]
name = "ci-sandbox"
version = "0.1.0"
dependencies = [
"anyhow",
"rand",
"serde",
"serde_json",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "libc"
version = "0.2.191"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.9",
]
[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "zerocopy"
version = "0.8.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.119",
]
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "ci-sandbox"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
anyhow = "1"
rand = "0.8"
[dev-dependencies]
+49
View File
@@ -0,0 +1,49 @@
//! Tiny crate used to exercise CI caching: enough deps to make a warm
//! build obviously faster than a cold one.
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Reading {
pub sensor: String,
pub celsius: f64,
}
pub fn to_fahrenheit(celsius: f64) -> f64 {
celsius * 9.0 / 5.0 + 32.0
}
pub fn parse(json: &str) -> Result<Reading> {
let r: Reading = serde_json::from_str(json)?;
if r.celsius < -273.15 {
bail!("{} C is below absolute zero", r.celsius);
}
Ok(r)
}
pub fn jitter(amount: f64) -> f64 {
use rand::Rng;
rand::thread_rng().gen_range(-amount..=amount)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn converts_units() {
assert!((to_fahrenheit(0.0) - 32.0).abs() < 1e-9);
assert!((to_fahrenheit(100.0) - 212.0).abs() < 1e-9);
}
#[test]
fn parses_reading() {
let r = parse(r#"{"sensor":"attic","celsius":21.5}"#).unwrap();
assert_eq!(r.sensor, "attic");
}
#[test]
fn rejects_impossible_temperature() {
assert!(parse(r#"{"sensor":"x","celsius":-300.0}"#).is_err());
}
}
+4
View File
@@ -0,0 +1,4 @@
/build
/.ccache
CMakeCache.txt
CMakeFiles/
+11
View File
@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.22)
project(sandbox_c C)
set(CMAKE_C_STANDARD 11)
add_library(tempsense STATIC src/temperature.c src/convert.c src/parse.c src/format.c)
target_include_directories(tempsense PUBLIC include)
enable_testing()
add_executable(test_tempsense tests/test_temperature.c)
target_link_libraries(test_tempsense PRIVATE tempsense)
add_test(NAME tempsense COMMAND test_tempsense)
+12
View File
@@ -0,0 +1,12 @@
#ifndef TEMPSENSE_H
#define TEMPSENSE_H
#include <stddef.h>
typedef struct { char sensor[32]; double celsius; } Reading;
double to_fahrenheit(double celsius);
int parse_reading(const char *json, Reading *out); /* 0 on success */
int format_reading(const Reading *in, char *buf, size_t len);
double clamp_to_absolute_zero(double celsius);
#endif
+2
View File
@@ -0,0 +1,2 @@
#include "temperature.h"
double to_fahrenheit(double celsius) { return celsius * 9.0 / 5.0 + 32.0; }
+7
View File
@@ -0,0 +1,7 @@
#include "temperature.h"
#include <stdio.h>
int format_reading(const Reading *in, char *buf, size_t len) {
if (!in || !buf || len < 16) return 1;
int n = snprintf(buf, len, "{\"sensor\":\"%s\",\"celsius\":%.2f}", in->sensor, in->celsius);
return (n < 0 || (size_t)n >= len) ? 2 : 0;
}
+34
View File
@@ -0,0 +1,34 @@
#include "temperature.h"
#include <stdlib.h>
#include <string.h>
/* Deliberately tiny parser: finds "celsius":<num> and an optional
"sensor":"..." so the test can assert on both fields. */
int parse_reading(const char *json, Reading *out) {
if (!json || !out) return 1;
const char *c = strstr(json, "\"celsius\"");
const char *s = strstr(json, "\"sensor\"");
if (!c) return 2;
c = strchr(c, ':');
if (!c) return 3;
char *end = NULL;
out->celsius = strtod(c + 1, &end);
if (end == c + 1) return 4;
out->sensor[0] = '\0';
if (s) {
s = strchr(s, ':');
if (s) {
const char *open = strchr(s, '"');
if (open) {
const char *close = strchr(open + 1, '"');
if (close) {
size_t n = (size_t)(close - open - 1);
if (n >= sizeof out->sensor) n = sizeof out->sensor - 1;
memcpy(out->sensor, open + 1, n);
out->sensor[n] = '\0';
}
}
}
}
return 0;
}
+3
View File
@@ -0,0 +1,3 @@
#include "temperature.h"
#define ABSOLUTE_ZERO_C (-273.15)
double clamp_to_absolute_zero(double celsius) { return celsius < ABSOLUTE_ZERO_C ? ABSOLUTE_ZERO_C : celsius; }
+26
View File
@@ -0,0 +1,26 @@
#include "temperature.h"
#include <assert.h>
#include <math.h>
#include <string.h>
#include <stdio.h>
int main(void) {
assert(fabs(to_fahrenheit(0.0) - 32.0) < 1e-9);
assert(fabs(to_fahrenheit(100.0) - 212.0) < 1e-9);
Reading r;
assert(parse_reading("{\"sensor\":\"attic\",\"celsius\":21.50}", &r) == 0);
assert(strcmp(r.sensor, "attic") == 0);
assert(fabs(r.celsius - 21.5) < 1e-9);
assert(parse_reading("{\"celsius\":-300.0}", &r) == 0);
assert(fabs(clamp_to_absolute_zero(r.celsius) + 273.15) < 1e-9);
assert(parse_reading("nonsense", &r) != 0);
char buf[128];
Reading in = { "boiler", 65.25 };
assert(format_reading(&in, buf, sizeof buf) == 0);
assert(strstr(buf, "\"sensor\":\"boiler\"") != NULL);
puts("all tempsense assertions passed");
return 0;
}