ci: run the fork's pipelines on the self-hosted runners

Squash of ten CI commits from 2026-08-04 (all part of HealthChat release 26.08.0):

- ci(release): move releases to the persistent self-hosted runners
- ci: grant contents:read so checkout works on this private fork
- ci: disable the Label Community PRs job on this fork
- ci: run the heavy Gradle jobs on the self-hosted runners
- ci: rely on the runner image for the Android SDK
- ci: size the Gradle heap for the self-hosted containers
- ci: report runner capacity on the self-hosted jobs
- ci: record why the Gradle heap is reduced
- ci: cap Gradle workers so test JVMs fit the container
- ci: skip the codecov upload when no token is configured

The danger.yml hunk from the contents:read commit is omitted: upstream
replaced Danger with a github-script workflow in v26.09.
This commit is contained in:
Sedat Qaja
2026-10-16 16:45:41 +02:00
parent 3db17825de
commit 708535469e
10 changed files with 224 additions and 73 deletions
+20 -3
View File
@@ -7,18 +7,32 @@ on:
push:
branches: [ develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
build:
name: Build APKs
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
permissions:
# A job-level block replaces the workflow-level one, so contents:read has to be
# repeated here or checkout fails on this private fork.
contents: read
# For NejcZdovc/comment-pr
pull-requests: write
strategy:
@@ -31,6 +45,9 @@ jobs:
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
+17 -3
View File
@@ -7,17 +7,28 @@ on:
push:
branches: [ develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
build:
name: Build Enterprise APKs
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Skip in forks
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
strategy:
@@ -30,6 +41,9 @@ jobs:
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -46,17 +46,24 @@ concurrency:
env:
# Upstream's workflows hardcode -Xmx8g / Kotlin daemon -Xmx4g, sized for the
# 4-core/16GB ubuntu-latest that public repos get. On this container the
# daemon keeps getting SIGKILLed mid-build (DaemonDisappearedException, no
# OutOfMemoryError, no heap dump), so stay well under upstream's ceiling and
# leave room for the Kotlin daemon and the parallel lint workers.
# 4-core/16GB ubuntu-latest that public repos get. On the previous incus
# container the daemon kept getting SIGKILLed mid-build
# (DaemonDisappearedException, no OutOfMemoryError, no heap dump), so these
# values stay well under upstream's ceiling and leave room for the Kotlin
# daemon and the parallel lint workers. Re-check them against the "Report
# runner capacity" output now that jobs land on the persistent org runners,
# whose specs may differ.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
release:
name: Build and upload AAB to Play Store
runs-on: novaloop-incus-large
# Persistent org runners (github-runner-01, gha-runner-1..3). The previous
# novaloop-incus-large label pointed at the GARM ephemeral pool, whose freshly
# spawned runners were reaped mid-job — jobs died ~10 minutes in with
# "The operation was canceled" and no other diagnostic.
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@v4
with:
@@ -176,18 +183,14 @@ jobs:
distribution: 'temurin'
java-version: '21'
# The self-hosted runner image has no Android SDK (unlike GitHub-hosted
# ubuntu-latest), so install it here.
# - platform: follows COMPILE_SDK in plugins/src/main/kotlin/Versions.kt.
# Platform packages are minor-versioned, so compileSdk 37 is android-37.0.
# - build-tools: nothing sets buildToolsVersion, so AGP picks its own
# default (SdkConstants.CURRENT_BUILD_TOOLS_VERSION). Bump it when AGP is
# upgraded, not when Versions.kt changes.
- name: Set up Android SDK
uses: android-actions/setup-android@v3
with:
packages: 'platforms;android-37.0 build-tools;36.0.0'
# The Android SDK now comes from the self-hosted runner image, so there is no
# setup-android step here. The image must provide it AND export ANDROID_HOME to
# the job environment — Gradle fails with "SDK location not found" otherwise.
# The image also has to carry the platform matching COMPILE_SDK in
# plugins/src/main/kotlin/Versions.kt (platform packages are minor-versioned, so
# compileSdk 37 means android-37.0) and the build-tools version AGP defaults to
# (SdkConstants.CURRENT_BUILD_TOOLS_VERSION), since nothing sets buildToolsVersion
# here. Both need refreshing in the image when AGP or COMPILE_SDK moves.
- name: Configure gradle
uses: gradle/actions/setup-gradle@v4
+17 -3
View File
@@ -5,11 +5,22 @@ on:
workflow_dispatch:
pull_request:
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
ARCH: x86_64
DEVICE: pixel_7_pro
@@ -19,7 +30,7 @@ env:
jobs:
build-apk:
name: Build APK
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Secrets are not available to pull_request runs from forks, so the Maestro job below
# cannot run for them; skip the build too, as its only consumer is that job.
if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository
@@ -28,6 +39,9 @@ jobs:
cancel-in-progress: true
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
+32 -27
View File
@@ -23,33 +23,38 @@ jobs:
script: |
core.setFailed("PR has been labeled with X-Blocked; it cannot be merged.");
community-prs:
name: Label Community PRs
runs-on: ubuntu-latest
if: github.event.action == 'opened'
permissions:
pull-requests: write
steps:
- name: Check membership
if: github.event.pull_request.user.login != 'renovate[bot]'
uses: tspascoal/get-user-teams-membership@b2546c5affc730fd8e3d8483ae9ad3621938c2f9 # v4.0.2
id: teams
with:
username: ${{ github.event.pull_request.user.login }}
organization: element-hq
team: Vector Core
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }}
- name: Add label
if: steps.teams.outputs.isTeamMember == 'false'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['Z-Community-PR']
});
# Disabled on the HealthChat fork. This job asks whether the PR author belongs to the
# element-hq "Vector Core" team, using secrets.ELEMENT_BOT_TOKEN_READ_ORG — a secret
# that only exists upstream. Here it fails on every PR that is opened, and there is no
# community to label anyway. Kept commented rather than deleted so upstream changes to
# it still conflict visibly on merge.
# community-prs:
# name: Label Community PRs
# runs-on: ubuntu-latest
# if: github.event.action == 'opened'
# permissions:
# pull-requests: write
# steps:
# - name: Check membership
# if: github.event.pull_request.user.login != 'renovate[bot]'
# uses: tspascoal/get-user-teams-membership@b2546c5affc730fd8e3d8483ae9ad3621938c2f9 # v4.0.2
# id: teams
# with:
# username: ${{ github.event.pull_request.user.login }}
# organization: element-hq
# team: Vector Core
# GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }}
# - name: Add label
# if: steps.teams.outputs.isTeamMember == 'false'
# uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# with:
# script: |
# github.rest.issues.addLabels({
# issue_number: context.issue.number,
# owner: context.repo.owner,
# repo: context.repo.repo,
# labels: ['Z-Community-PR']
# });
close-if-fork-develop:
name: Forbid develop branch fork contributions
+35 -8
View File
@@ -7,11 +7,22 @@ on:
push:
branches: [ main, develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs:
@@ -20,6 +31,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -63,7 +77,7 @@ jobs:
checkDependencies:
name: Search for invalid dependencies
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
@@ -87,7 +101,7 @@ jobs:
# Code checks
konsist:
name: Konsist tests
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-konsist-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-konsist-develop-{0}', github.sha) || format('check-konsist-{0}', github.ref) }}
@@ -128,12 +142,21 @@ jobs:
compose:
name: Compose tests
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-compose-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-compose-develop-{0}', github.sha) || format('check-compose-{0}', github.ref) }}
cancel-in-progress: true
steps:
# Records the container's real limits so heap settings can be read against them
# instead of guessed at. Mirrors healthchat-release-playstore.yml.
- name: Report runner capacity
if: runner.environment == 'self-hosted'
run: |
free -h; nproc; df -h .
cat /sys/fs/cgroup/memory.max 2>/dev/null \
|| cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null \
|| echo "no cgroup memory limit visible"
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Ensure we are building the branch and not the branch after being merged on develop
@@ -162,7 +185,7 @@ jobs:
lint:
name: Android lint check
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-lint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-lint-develop-{0}', github.sha) || format('check-lint-{0}', github.ref) }}
@@ -207,7 +230,7 @@ jobs:
detekt:
name: Detekt checks
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-detekt-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-detekt-develop-{0}', github.sha) || format('check-detekt-{0}', github.ref) }}
@@ -248,7 +271,7 @@ jobs:
ktlint:
name: Ktlint checks
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('check-ktlint-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('check-ktlint-develop-{0}', github.sha) || format('check-ktlint-{0}', github.ref) }}
@@ -331,6 +354,10 @@ jobs:
name: Run zizmor
runs-on: ubuntu-latest
permissions:
# A job-level block replaces the workflow-level one, so contents:read has to be
# repeated here or checkout fails on this private fork.
contents: read
actions: read # Required for zizmor to read workflow files.
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+17 -3
View File
@@ -5,11 +5,22 @@ on:
pull_request:
types: [ labeled ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g -Dsonar.gradle.skipCompile=true
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g -Dsonar.gradle.skipCompile=true
CI_GRADLE_ARG_PROPERTIES: --no-configuration-cache
jobs:
@@ -19,11 +30,14 @@ jobs:
pull-requests: write
contents: write
name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }}
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots'
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
+17 -3
View File
@@ -7,24 +7,38 @@ on:
push:
branches: [ main, develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx4g
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -Dkotlin.daemon.jvm.options=-Xmx2g
CI_GRADLE_ARG_PROPERTIES: --stacktrace --warn -Dsonar.gradle.skipCompile=true --no-configuration-cache
GROUP: ${{ format('sonar-{0}', github.ref) }}
jobs:
sonar:
name: Sonar Quality Checks
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ format('sonar-{0}', github.ref) }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}
steps:
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
+44 -5
View File
@@ -7,24 +7,53 @@ on:
push:
branches: [ main, develop ]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
# Enrich gradle.properties for CI/CD
env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseG1GC
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
# Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
# Each incus container reports 48 GiB and 8 cores, so a single build has ample room —
# but the containers are oversubscribed against their host, and two concurrent heavy
# Gradle jobs get their daemons SIGKILLed ("the daemon has disappeared", no
# OutOfMemoryError, no heap dump). Running the same job alone succeeds. Lower heaps
# make two builds likelier to coexist; the real fix is sizing the containers to the
# host. See the "Report runner capacity" step.
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx6g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseG1GC
# --max-workers caps how many test tasks run at once. build.gradle.kts sets
# maxParallelForks = cores/2 and screenshot tests fork at maxHeapSize=2g, so on these
# 8-core containers the default (workers = cores = 8) allows 8 x 4 x 2g = 64 GB of test
# JVMs against a 48 GiB cgroup limit, with no swap — the daemon gets OOM-killed and
# Gradle reports "the daemon has disappeared". GitHub's 4-core hosted runners reach only
# 2 x 2 x 2g = 16 GB and additionally have the 8 GB swapfile added above.
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache --max-workers=4
jobs:
tests:
name: Runs unit tests
runs-on: ubuntu-latest
runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR.
concurrency:
group: ${{ github.ref == 'refs/heads/main' && format('unit-tests-main-{0}', github.sha) || github.ref == 'refs/heads/develop' && format('unit-tests-develop-{0}', github.sha) || format('unit-tests-{0}', github.ref) }}
cancel-in-progress: true
steps:
# Records the container's real limits so heap settings can be read against them
# instead of guessed at. Mirrors healthchat-release-playstore.yml.
- name: Report runner capacity
if: runner.environment == 'self-hosted'
run: |
free -h; nproc; df -h .
cat /sys/fs/cgroup/memory.max 2>/dev/null \
|| cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null \
|| echo "no cgroup memory limit visible"
- name: Free Disk Space (Ubuntu)
# Only meaningful on GitHub's throwaway VMs. On a persistent self-hosted runner this
# would delete dotnet/haskell/docker images from the host on every run.
if: runner.environment == 'github-hosted'
uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -41,6 +70,9 @@ jobs:
# Increase swapfile size to prevent screenshot tests getting terminated
# https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749
- name: 💽 Increase swapfile size
# /mnt/swapfile is a GitHub runner-image path and this needs passwordless sudo,
# so it only applies to the hosted VMs.
if: runner.environment == 'github-hosted'
run: |
sudo swapoff -a
sudo fallocate -l 8G /mnt/swapfile
@@ -112,8 +144,15 @@ jobs:
echo "---" >> $GITHUB_STEP_SUMMARY
# https://github.com/codecov/codecov-action
# This fork has no CODECOV_TOKEN, and Codecov rejects tokenless uploads from private
# repositories ("Token required - not valid tokenless upload"). With
# fail_ci_if_error: true that failed the whole job *after* the tests had passed, so
# skip the upload unless a token is configured. Adding the secret re-enables it.
- name: ☂️ Upload coverage reports to codecov
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
if: env.CODECOV_TOKEN != ''
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
+5 -1
View File
@@ -2,7 +2,11 @@ name: Validate Git LFS
on: [pull_request, merge_group]
permissions: {}
permissions:
# Private fork: actions/checkout needs contents:read to clone. Upstream is public,
# where an empty token can still read the repo, so an empty permissions block works there and
# fails here with `fatal: repository not found`.
contents: read
jobs:
build: