Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/scripts/apt-retry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -euo pipefail

usage() {
echo "usage: apt-retry.sh [--sudo] <apt-get arguments...>" >&2
exit 2
}

use_sudo=false
if [[ "${1:-}" == "--sudo" ]]; then
use_sudo=true
shift
fi
(( $# > 0 )) || usage

attempts="${RIVET_APT_RETRY_ATTEMPTS:-2}"
timeout_seconds="${RIVET_APT_TIMEOUT_SECONDS:-180}"
delay_seconds="${RIVET_APT_RETRY_DELAY_SECONDS:-10}"
apt_get="${RIVET_APT_GET:-apt-get}"
timeout_command="${RIVET_TIMEOUT_COMMAND:-timeout}"
if [[ -x /usr/bin/timeout && -z "${RIVET_TIMEOUT_COMMAND:-}" ]]; then
timeout_command=/usr/bin/timeout
fi

for value_name in attempts timeout_seconds delay_seconds; do
value="${!value_name}"
if [[ ! "$value" =~ ^[0-9]+$ ]] || (( value < 1 )); then
echo "apt-retry: $value_name must be a positive integer" >&2
exit 2
fi
done

apt_command=(
env DEBIAN_FRONTEND=noninteractive
"$apt_get"
-o Acquire::Retries=3
-o DPkg::Lock::Timeout=60
"$@"
)
if [[ "$use_sudo" == true ]]; then
apt_command=(sudo -n "${apt_command[@]}")
fi

operation="$1"
for (( attempt = 1; attempt <= attempts; attempt++ )); do
echo "apt-retry: $operation attempt $attempt/$attempts (timeout ${timeout_seconds}s)"
if "$timeout_command" --foreground --signal=TERM --kill-after=30s \
"${timeout_seconds}s" "${apt_command[@]}"; then
exit 0
else
status=$?
fi
if (( attempt == attempts )); then
echo "apt-retry: $operation failed after $attempts attempts" >&2
exit "$status"
fi
sleep "$(( delay_seconds * attempt ))"
done
53 changes: 53 additions & 0 deletions .github/scripts/test-apt-retry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT

fake_apt="$work/fake-apt-get"
cat >"$fake_apt" <<'SH'
#!/usr/bin/env bash
set -euo pipefail
count=0
if [[ -f "$FAKE_APT_STATE" ]]; then
count="$(cat "$FAKE_APT_STATE")"
fi
count="$(( count + 1 ))"
printf '%s' "$count" >"$FAKE_APT_STATE"
printf '%s\n' "$*" >"$FAKE_APT_ARGUMENTS"
case "$FAKE_APT_MODE" in
retry)
(( count >= 3 ))
;;
timeout)
sleep 5
;;
*)
exit 64
;;
esac
SH
chmod +x "$fake_apt"

export RIVET_APT_GET="$fake_apt"
export RIVET_APT_RETRY_DELAY_SECONDS=1
export FAKE_APT_STATE="$work/count"
export FAKE_APT_ARGUMENTS="$work/arguments"

export FAKE_APT_MODE=retry
export RIVET_APT_RETRY_ATTEMPTS=3
export RIVET_APT_TIMEOUT_SECONDS=5
bash "$script_dir/apt-retry.sh" install --yes example-package
[[ "$(cat "$FAKE_APT_STATE")" == 3 ]]
grep -Fq -- 'install --yes example-package' "$FAKE_APT_ARGUMENTS"
grep -Fq -- 'Acquire::Retries=3' "$FAKE_APT_ARGUMENTS"
grep -Fq -- 'DPkg::Lock::Timeout=60' "$FAKE_APT_ARGUMENTS"

export FAKE_APT_MODE=timeout
export RIVET_APT_RETRY_ATTEMPTS=1
export RIVET_APT_TIMEOUT_SECONDS=1
if bash "$script_dir/apt-retry.sh" update; then
echo "apt-retry test expected the bounded command to fail" >&2
exit 1
fi
5 changes: 3 additions & 2 deletions .github/workflows/architectures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ jobs:
raco pkg install --auto --no-docs --name rivet --link "$PWD"
- name: Install GTK4 development files and rpm tooling
run: |
sudo apt-get update
sudo apt-get install --yes libgtk-4-dev rpm desktop-file-utils
bash .github/scripts/apt-retry.sh --sudo update
bash .github/scripts/apt-retry.sh --sudo install --yes \
libgtk-4-dev rpm desktop-file-utils
- name: Compile full schema matrix, package, and verify a GTK4 app
shell: bash
run: |
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ jobs:
- name: Test release tag provenance validator
shell: bash
run: bash .github/scripts/test-release-tag.sh
- name: Test bounded apt retry wrapper
shell: bash
run: bash .github/scripts/test-apt-retry.sh
- name: Set up Racket CS
uses: Bogdanp/setup-racket@2466913449df77df2bad149d1f2fc4e1ea4795dd # v1.15
with:
Expand Down
10 changes: 6 additions & 4 deletions .github/workflows/linux-installers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ jobs:
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install --yes desktop-file-utils libgtk-4-dev libsecret-1-dev rpm
bash .github/scripts/apt-retry.sh --sudo update
bash .github/scripts/apt-retry.sh --sudo install --yes \
desktop-file-utils libgtk-4-dev libsecret-1-dev rpm
raco pkg install --auto --no-docs --name rivet --link "$PWD"
- name: Build release artifacts
shell: bash
Expand Down Expand Up @@ -119,14 +120,15 @@ jobs:
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: linux-native-installers
path: ${{ runner.temp }}/installers
- name: Install headless Wayland compositor
run: |
sudo apt-get update
sudo apt-get install --yes weston
bash .github/scripts/apt-retry.sh --sudo update
bash .github/scripts/apt-retry.sh --sudo install --yes weston
- name: Launch AppImage outside the build tree
shell: bash
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/native-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ jobs:
shell: bash
run: |
set -euo pipefail
sudo apt-get update
sudo apt-get install --yes \
bash .github/scripts/apt-retry.sh --sudo update
bash .github/scripts/apt-retry.sh --sudo install --yes \
at-spi2-core dbus-x11 libgtk-4-dev libsecret-1-dev \
python3-pyatspi weston
raco pkg install --auto --no-docs --name rivet --link "$PWD"
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/roundtrip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ jobs:
run: raco pkg install --auto --no-docs --name rivet --link "$PWD"
- name: Install Linux development files
run: |
sudo apt-get update
sudo apt-get install --yes desktop-file-utils libglib2.0-dev libsecret-1-dev libgtk-4-dev weston
bash .github/scripts/apt-retry.sh --sudo update
bash .github/scripts/apt-retry.sh --sudo install --yes \
desktop-file-utils libglib2.0-dev libsecret-1-dev libgtk-4-dev weston
- name: Build backend and integration runner
shell: bash
run: |
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Bound and retry Ubuntu runner package-manager operations so a stalled mirror
cannot consume an entire desktop validation job without a useful failure.
- Removed the misleading `rivet-type` runtime descriptors from the pre-1.0
public surface. Schema type identifiers are declaration grammar, while Keel
is the canonical runtime-inspection model.
Expand Down
8 changes: 8 additions & 0 deletions docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ Wayland gate: `DISPLAY` is unset, so the application cannot silently pass by
using X11 or XWayland. Products supporting Linux should keep an equivalent
Wayland launch in their release matrix; see [Linux display policy](linux-display.md).

Rivet's own Ubuntu jobs run provisioning `apt-get` commands through
`.github/scripts/apt-retry.sh`. Each attempt is non-interactive, applies APT's
network and lock retries, and has a three-minute wall-clock limit; one fresh
attempt is allowed before the job fails. Product CI should use the same bounded
principle so a stalled package mirror cannot consume the whole native-build
budget. Package installation lifecycle tests themselves stay unwrapped because
their raw transaction outcome is the behavior under test.

The separate `Native UI End-to-End` workflow packages the maintained Taskboard
and drives its real WinUI 3, SwiftUI/AppKit, and GTK4 accessibility surfaces.
It covers activation, an RPC mutation, an Event, State rendering, and orderly
Expand Down
6 changes: 6 additions & 0 deletions docs/ci.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ AT-SPI 检查真实原生界面。门禁覆盖窗口激活、一次 RPC 变更
State 渲染与有序关闭;失败时保留有界的无障碍快照、交互轨迹、截图和日志。
应用如何复用这套模式,见[原生 UI 端到端测试](native-ui-testing.md)。

Rivet 自己的 Ubuntu 任务会通过 `.github/scripts/apt-retry.sh` 执行环境准备阶段的
`apt-get`。每次尝试都使用非交互模式、APT 网络与锁重试,并设有三分钟墙钟时间
上限;失败后只允许再换一次新尝试。产品 CI 也应采用同样的有界原则,避免软件源
卡住后耗尽整个原生构建预算。安装器生命周期测试仍直接执行原始包管理器事务,
因为事务结果本身就是这些测试要验证的行为。

## 发布门禁

推送标签前,应要求上述所有原生宿主任务和产品自己的 Racket 测试通过。
Expand Down
Loading