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: push:
branches: [ develop ] 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 # Enrich gradle.properties for CI/CD
env: 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 CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs: jobs:
build: build:
name: Build APKs name: Build APKs
runs-on: ubuntu-latest runs-on: [self-hosted, Linux, X64]
permissions: 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 # For NejcZdovc/comment-pr
pull-requests: write pull-requests: write
strategy: strategy:
@@ -31,6 +45,9 @@ jobs:
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu) - 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 uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with: with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB # 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: push:
branches: [ develop ] 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 # Enrich gradle.properties for CI/CD
env: 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 CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs: jobs:
build: build:
name: Build Enterprise APKs name: Build Enterprise APKs
runs-on: ubuntu-latest runs-on: [self-hosted, Linux, X64]
# Skip in forks # Skip in forks
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }} if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'element-hq/element-x-android' }}
strategy: strategy:
@@ -30,6 +41,9 @@ jobs:
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu) - 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 uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with: with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB # This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -46,17 +46,24 @@ concurrency:
env: env:
# Upstream's workflows hardcode -Xmx8g / Kotlin daemon -Xmx4g, sized for the # Upstream's workflows hardcode -Xmx8g / Kotlin daemon -Xmx4g, sized for the
# 4-core/16GB ubuntu-latest that public repos get. On this container the # 4-core/16GB ubuntu-latest that public repos get. On the previous incus
# daemon keeps getting SIGKILLed mid-build (DaemonDisappearedException, no # container the daemon kept getting SIGKILLed mid-build
# OutOfMemoryError, no heap dump), so stay well under upstream's ceiling and # (DaemonDisappearedException, no OutOfMemoryError, no heap dump), so these
# leave room for the Kotlin daemon and the parallel lint workers. # 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 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 CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs: jobs:
release: release:
name: Build and upload AAB to Play Store 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: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -176,18 +183,14 @@ jobs:
distribution: 'temurin' distribution: 'temurin'
java-version: '21' java-version: '21'
# The self-hosted runner image has no Android SDK (unlike GitHub-hosted # The Android SDK now comes from the self-hosted runner image, so there is no
# ubuntu-latest), so install it here. # setup-android step here. The image must provide it AND export ANDROID_HOME to
# - platform: follows COMPILE_SDK in plugins/src/main/kotlin/Versions.kt. # the job environment — Gradle fails with "SDK location not found" otherwise.
# Platform packages are minor-versioned, so compileSdk 37 is android-37.0. # The image also has to carry the platform matching COMPILE_SDK in
# - build-tools: nothing sets buildToolsVersion, so AGP picks its own # plugins/src/main/kotlin/Versions.kt (platform packages are minor-versioned, so
# default (SdkConstants.CURRENT_BUILD_TOOLS_VERSION). Bump it when AGP is # compileSdk 37 means android-37.0) and the build-tools version AGP defaults to
# upgraded, not when Versions.kt changes. # (SdkConstants.CURRENT_BUILD_TOOLS_VERSION), since nothing sets buildToolsVersion
- name: Set up Android SDK # here. Both need refreshing in the image when AGP or COMPILE_SDK moves.
uses: android-actions/setup-android@v3
with:
packages: 'platforms;android-37.0 build-tools;36.0.0'
- name: Configure gradle - name: Configure gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
+17 -3
View File
@@ -5,11 +5,22 @@ on:
workflow_dispatch: workflow_dispatch:
pull_request: 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 # Enrich gradle.properties for CI/CD
env: 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 CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
ARCH: x86_64 ARCH: x86_64
DEVICE: pixel_7_pro DEVICE: pixel_7_pro
@@ -19,7 +30,7 @@ env:
jobs: jobs:
build-apk: build-apk:
name: 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 # 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. # 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 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 cancel-in-progress: true
steps: steps:
- name: Free Disk Space (Ubuntu) - 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 uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with: with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB # 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: | script: |
core.setFailed("PR has been labeled with X-Blocked; it cannot be merged."); core.setFailed("PR has been labeled with X-Blocked; it cannot be merged.");
community-prs: # Disabled on the HealthChat fork. This job asks whether the PR author belongs to the
name: Label Community PRs # element-hq "Vector Core" team, using secrets.ELEMENT_BOT_TOKEN_READ_ORG — a secret
runs-on: ubuntu-latest # that only exists upstream. Here it fails on every PR that is opened, and there is no
if: github.event.action == 'opened' # community to label anyway. Kept commented rather than deleted so upstream changes to
permissions: # it still conflict visibly on merge.
pull-requests: write # community-prs:
steps: # name: Label Community PRs
- name: Check membership # runs-on: ubuntu-latest
if: github.event.pull_request.user.login != 'renovate[bot]' # if: github.event.action == 'opened'
uses: tspascoal/get-user-teams-membership@b2546c5affc730fd8e3d8483ae9ad3621938c2f9 # v4.0.2 # permissions:
id: teams # pull-requests: write
with: # steps:
username: ${{ github.event.pull_request.user.login }} # - name: Check membership
organization: element-hq # if: github.event.pull_request.user.login != 'renovate[bot]'
team: Vector Core # uses: tspascoal/get-user-teams-membership@b2546c5affc730fd8e3d8483ae9ad3621938c2f9 # v4.0.2
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }} # id: teams
- name: Add label # with:
if: steps.teams.outputs.isTeamMember == 'false' # username: ${{ github.event.pull_request.user.login }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 # organization: element-hq
with: # team: Vector Core
script: | # GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN_READ_ORG }}
github.rest.issues.addLabels({ # - name: Add label
issue_number: context.issue.number, # if: steps.teams.outputs.isTeamMember == 'false'
owner: context.repo.owner, # uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
repo: context.repo.repo, # with:
labels: ['Z-Community-PR'] # 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: close-if-fork-develop:
name: Forbid develop branch fork contributions name: Forbid develop branch fork contributions
+35 -8
View File
@@ -7,11 +7,22 @@ on:
push: push:
branches: [ main, develop ] 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 # Enrich gradle.properties for CI/CD
env: 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 CI_GRADLE_ARG_PROPERTIES: --stacktrace --no-daemon -Dsonar.gradle.skipCompile=true --no-configuration-cache
jobs: jobs:
@@ -20,6 +31,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Free Disk Space (Ubuntu) - 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 uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with: with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB # This might remove tools that are actually needed, if set to "true" but frees about 6 GB
@@ -63,7 +77,7 @@ jobs:
checkDependencies: checkDependencies:
name: Search for invalid dependencies name: Search for invalid dependencies
runs-on: ubuntu-latest runs-on: [self-hosted, Linux, X64]
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: with:
@@ -87,7 +101,7 @@ jobs:
# Code checks # Code checks
konsist: konsist:
name: Konsist tests name: Konsist tests
runs-on: ubuntu-latest runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR. # Allow all jobs on main and develop. Just one per PR.
concurrency: 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) }} 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: compose:
name: Compose tests name: Compose tests
runs-on: ubuntu-latest runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR. # Allow all jobs on main and develop. Just one per PR.
concurrency: 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) }} 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 cancel-in-progress: true
steps: 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 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: with:
# Ensure we are building the branch and not the branch after being merged on develop # Ensure we are building the branch and not the branch after being merged on develop
@@ -162,7 +185,7 @@ jobs:
lint: lint:
name: Android lint check 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. # Allow all jobs on main and develop. Just one per PR.
concurrency: 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) }} 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: detekt:
name: Detekt checks name: Detekt checks
runs-on: ubuntu-latest runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR. # Allow all jobs on main and develop. Just one per PR.
concurrency: 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) }} 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: ktlint:
name: Ktlint checks name: Ktlint checks
runs-on: ubuntu-latest runs-on: [self-hosted, Linux, X64]
# Allow all jobs on main and develop. Just one per PR. # Allow all jobs on main and develop. Just one per PR.
concurrency: 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) }} 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 name: Run zizmor
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: 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. security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
steps: steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+17 -3
View File
@@ -5,11 +5,22 @@ on:
pull_request: pull_request:
types: [ labeled ] 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 # Enrich gradle.properties for CI/CD
env: 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 CI_GRADLE_ARG_PROPERTIES: --no-configuration-cache
jobs: jobs:
@@ -19,11 +30,14 @@ jobs:
pull-requests: write pull-requests: write
contents: write contents: write
name: Record screenshots on branch ${{ github.event.pull_request.head.ref || github.ref_name }} 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' if: github.event_name == 'workflow_dispatch' || github.event.label.name == 'Record-Screenshots'
steps: steps:
- name: Free Disk Space (Ubuntu) - 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 uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with: with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB # 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: push:
branches: [ main, develop ] 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 # Enrich gradle.properties for CI/CD
env: 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 CI_GRADLE_ARG_PROPERTIES: --stacktrace --warn -Dsonar.gradle.skipCompile=true --no-configuration-cache
GROUP: ${{ format('sonar-{0}', github.ref) }} GROUP: ${{ format('sonar-{0}', github.ref) }}
jobs: jobs:
sonar: sonar:
name: Sonar Quality Checks 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. # Allow all jobs on main and develop. Just one per PR.
concurrency: concurrency:
group: ${{ format('sonar-{0}', github.ref) }} group: ${{ format('sonar-{0}', github.ref) }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' }}
steps: steps:
- name: Free Disk Space (Ubuntu) - 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 uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with: with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB # 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: push:
branches: [ main, develop ] 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 # Enrich gradle.properties for CI/CD
env: env:
GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx7g -XX:MaxMetaspaceSize=512m -Dfile.encoding=UTF-8 -XX:+HeapDumpOnOutOfMemoryError" -Dkotlin.daemon.jvm.options=-Xmx2g -XX:+UseG1GC # Reduced from upstream's -Xmx8g / 4 GB Kotlin daemon for the self-hosted runners.
CI_GRADLE_ARG_PROPERTIES: --stacktrace -Dsonar.gradle.skipCompile=true --no-configuration-cache # 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: jobs:
tests: tests:
name: Runs unit 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. # Allow all jobs on main and develop. Just one per PR.
concurrency: 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) }} 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 cancel-in-progress: true
steps: 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) - 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 uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be
with: with:
# This might remove tools that are actually needed, if set to "true" but frees about 6 GB # 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 # Increase swapfile size to prevent screenshot tests getting terminated
# https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749 # https://github.com/actions/runner-images/discussions/7188#discussioncomment-6750749
- name: 💽 Increase swapfile size - 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: | run: |
sudo swapoff -a sudo swapoff -a
sudo fallocate -l 8G /mnt/swapfile sudo fallocate -l 8G /mnt/swapfile
@@ -112,8 +144,15 @@ jobs:
echo "---" >> $GITHUB_STEP_SUMMARY echo "---" >> $GITHUB_STEP_SUMMARY
# https://github.com/codecov/codecov-action # 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 - 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: with:
fail_ci_if_error: true fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
+5 -1
View File
@@ -2,7 +2,11 @@ name: Validate Git LFS
on: [pull_request, merge_group] 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: jobs:
build: build: