Skip to content
Open
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
17 changes: 17 additions & 0 deletions .github/runs-on.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
runners:
linux-x64-builder:
family: ["c8i.2xlarge"]
spot: false
image: ubuntu24-full-x64
extras: s3-cache
linux-arm64-builder:
family: ["c8g.2xlarge"]
spot: false
image: ubuntu24-full-arm64
extras: s3-cache
linux-x64-tester:
family: ["m8i.2xlarge"]
spot: false
image: ubuntu24-full-x64
extras: s3-cache
volume: 60gb
109 changes: 80 additions & 29 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,29 +39,31 @@
name: Release ${{ matrix.arch }}
needs: changes
if: always() && !cancelled() && (needs.changes.outputs.should_build == 'true' || needs.changes.result == 'skipped')
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.runs_on }}
timeout-minutes: 120

strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04-arm, ubuntu-22.04]
include:
- os: ubuntu-24.04-arm
- arch: linux_gcc_arm64
runs_on: runs-on=${{ github.run_id }}/family=c8g.2xlarge/spot=false/image=ubuntu24-full-arm64/extras=s3-cache
package: QGroundControl-aarch64
host: linux_arm64
arch: linux_gcc_arm64

- os: ubuntu-22.04
- arch: linux_gcc_64
runs_on: runs-on=${{ github.run_id }}/family=c8i.2xlarge/spot=false/image=ubuntu24-full-x64/extras=s3-cache
package: QGroundControl-x86_64
host: linux
arch: linux_gcc_64

defaults:
run:
shell: bash

steps:
- name: Enable RunsOn magic cache
uses: runs-on/action@v2

- name: Harden Runner
if: runner.arch != 'ARM64'
uses: step-security/harden-runner@v2
Expand Down Expand Up @@ -123,14 +125,14 @@
binary-path: ${{ runner.temp }}/build/Release/QGroundControl

- name: Analyze binary size
if: matrix.os == 'ubuntu-22.04'
if: matrix.arch == 'linux_gcc_64'
uses: ./.github/actions/size-analysis
with:
binary-path: ${{ runner.temp }}/build/Release/QGroundControl
output-file: ${{ runner.temp }}/build/metrics.json

- name: Upload metrics artifact
if: matrix.os == 'ubuntu-22.04'
if: matrix.arch == 'linux_gcc_64'
uses: actions/upload-artifact@v7
with:
name: size-metrics
Expand Down Expand Up @@ -174,7 +176,7 @@
# gate them on push/merge_group so PR feedback stays fast.
run: |
coverage='{"job_name":"Test + Coverage linux_gcc_64 Debug","mode":"coverage","timeout_minutes":60,"configure_extra":"","exclude_labels":"Flaky|Network"}'
sanitizers='{"job_name":"Sanitizers linux_gcc_64 Debug (ASan+UBSan)","mode":"sanitizers","timeout_minutes":120,"configure_extra":"-DQGC_ENABLE_ASAN=ON -DQGC_ENABLE_UBSAN=ON","exclude_labels":"Flaky|Network|NoSanitizer"}'

Check failure on line 179 in .github/workflows/linux.yml

View workflow job for this annotation

GitHub Actions / pre-commit

179:201 [line-length] line too long (230 > 200 characters)
if [ "$IS_PR" = "1" ]; then
echo "include=[${coverage}]" >> "$GITHUB_OUTPUT"
else
Expand All @@ -185,7 +187,11 @@
name: ${{ matrix.job_name }}
needs: [changes, debug-matrix]
if: ${{ !cancelled() && (needs.changes.outputs.should_build == 'true' || needs.changes.result == 'skipped') }}
runs-on: ubuntu-22.04
# 60GB volume so the Debug build + Qt SDK + caches + Integration test
# artifacts fit comfortably; the 40GB default left only ~1-2GB headroom
# at peak and killed the runner agent before any diagnostic step could
# run.
runs-on: runs-on=${{ github.run_id }}/family=m8i.2xlarge/spot=false/image=ubuntu24-full-x64/extras=s3-cache/volume=60gb
timeout-minutes: ${{ matrix.timeout_minutes }}

strategy:
Expand All @@ -198,6 +204,9 @@
shell: bash

steps:
- name: Enable RunsOn magic cache
uses: runs-on/action@v2

- name: Harden Runner
uses: step-security/harden-runner@v2
with:
Expand Down Expand Up @@ -240,53 +249,95 @@
build-dir: ${{ runner.temp }}/build
build-type: Debug

- name: Run Unit Tests
id: tests
# Split test execution by label: Unit tests parallelize cleanly (no
# MockLink/Vehicle shared state), Integration tests share LinkManager
# singletons and per-test RESOURCE_LOCK so they serialize naturally on
# one CTest invocation. Run them in two passes so the Unit pass can use
# all cores while the Integration pass stays correct.
- name: Run Unit Tests (parallel)
id: unit_tests
uses: ./.github/actions/run-unit-tests
env:
ASAN_OPTIONS: ${{ matrix.mode == 'sanitizers' && 'detect_leaks=1:halt_on_error=1:check_initialization_order=1' || '' }}
LSAN_OPTIONS: ${{ matrix.mode == 'sanitizers' && format('suppressions={0}/build/asan_suppressions.txt', runner.temp) || '' }}
UBSAN_OPTIONS: ${{ matrix.mode == 'sanitizers' && format('print_stacktrace=1:halt_on_error=1:suppressions={0}/build/ubsan_suppressions.txt', runner.temp) || '' }}
with:
build-dir: ${{ runner.temp }}/build
junit-output: junit-results-linux-${{ matrix.mode }}-unit.xml
ctest-output: test-output-linux-${{ matrix.mode }}-unit.txt
include-labels: 'Unit'
exclude-labels: ${{ matrix.exclude_labels }}
parallel: auto
Comment thread
mrpollo marked this conversation as resolved.

- name: Run Integration Tests (serial)
id: integration_tests
if: always() && !cancelled() && steps.unit_tests.conclusion != 'cancelled'
uses: ./.github/actions/run-unit-tests
env:
ASAN_OPTIONS: ${{ matrix.mode == 'sanitizers' && 'detect_leaks=1:halt_on_error=1:check_initialization_order=1' || '' }}
LSAN_OPTIONS: ${{ matrix.mode == 'sanitizers' && format('suppressions={0}/build/asan_suppressions.txt', runner.temp) || '' }}
UBSAN_OPTIONS: ${{ matrix.mode == 'sanitizers' && format('print_stacktrace=1:halt_on_error=1:suppressions={0}/build/ubsan_suppressions.txt', runner.temp) || '' }}
with:
build-dir: ${{ runner.temp }}/build
junit-output: junit-results-linux-${{ matrix.mode }}.xml
ctest-output: test-output-linux-${{ matrix.mode }}.txt
include-labels: 'Unit|Integration'
junit-output: junit-results-linux-${{ matrix.mode }}-integration.xml
ctest-output: test-output-linux-${{ matrix.mode }}-integration.txt
include-labels: 'Integration'
exclude-labels: ${{ matrix.exclude_labels }}
# Coverage requires serial execution (gcov writes race); sanitizers don't.
parallel: ${{ matrix.mode == 'coverage' && '1' || 'auto' }}
parallel: '1'
Comment thread
mrpollo marked this conversation as resolved.

- name: Analyze Unit Test Durations
if: always() && !cancelled() && steps.tests.conclusion != 'skipped'
- name: Analyze Unit Test Durations (unit)
if: always() && !cancelled() && steps.unit_tests.conclusion != 'skipped'
uses: ./.github/actions/test-duration-report
with:
junit-path: ${{ runner.temp }}/build/junit-results-linux-${{ matrix.mode }}.xml
report-json-path: ${{ runner.temp }}/build/test-duration-linux-${{ matrix.mode }}.json
junit-path: ${{ runner.temp }}/build/junit-results-linux-${{ matrix.mode }}-unit.xml
report-json-path: ${{ runner.temp }}/build/test-duration-linux-${{ matrix.mode }}-unit.json
top-n: '20'
slow-threshold-seconds: '60'

- name: Report Test Results
if: always() && !cancelled() && steps.tests.conclusion != 'skipped'
- name: Analyze Unit Test Durations (integration)
if: always() && !cancelled() && steps.integration_tests.conclusion != 'skipped'
uses: ./.github/actions/test-duration-report
with:
junit-path: ${{ runner.temp }}/build/junit-results-linux-${{ matrix.mode }}-integration.xml
report-json-path: ${{ runner.temp }}/build/test-duration-linux-${{ matrix.mode }}-integration.json
top-n: '20'
slow-threshold-seconds: '60'

- name: Report Test Results (unit)
if: always() && !cancelled() && steps.unit_tests.conclusion != 'skipped'
uses: ./.github/actions/test-report
with:
name: Unit Tests (${{ matrix.mode }})
build-dir: ${{ runner.temp }}/build
junit-file: junit-results-linux-${{ matrix.mode }}.xml
output-file: test-output-linux-${{ matrix.mode }}.txt
artifact-name: test-results-linux-${{ matrix.mode }}
junit-file: junit-results-linux-${{ matrix.mode }}-unit.xml
output-file: test-output-linux-${{ matrix.mode }}-unit.txt
artifact-name: test-results-linux-${{ matrix.mode }}-unit
retention-days: 7
trunk-org-slug: ${{ vars.TRUNK_ORG_SLUG }}
trunk-token: ${{ secrets.TRUNK_TOKEN }}

- name: Report Test Results (integration)
if: always() && !cancelled() && steps.integration_tests.conclusion != 'skipped'
uses: ./.github/actions/test-report
with:
name: Integration Tests (${{ matrix.mode }})
build-dir: ${{ runner.temp }}/build
junit-file: junit-results-linux-${{ matrix.mode }}-integration.xml
output-file: test-output-linux-${{ matrix.mode }}-integration.txt
artifact-name: test-results-linux-${{ matrix.mode }}-integration
retention-days: 7
trunk-org-slug: ${{ vars.TRUNK_ORG_SLUG }}
trunk-token: ${{ secrets.TRUNK_TOKEN }}

- name: Upload Test Artifacts
if: always() && !cancelled() && steps.tests.conclusion != 'skipped'
if: always() && !cancelled() && (steps.unit_tests.conclusion != 'skipped' || steps.integration_tests.conclusion != 'skipped')
uses: actions/upload-artifact@v7
with:
name: test-artifacts-linux-${{ matrix.mode }}
path: |
${{ runner.temp }}/build/test-output-linux-${{ matrix.mode }}.txt
${{ runner.temp }}/build/junit-results-linux-${{ matrix.mode }}.xml
${{ runner.temp }}/build/test-duration-linux-${{ matrix.mode }}.json
${{ runner.temp }}/build/test-output-linux-${{ matrix.mode }}-*.txt
${{ runner.temp }}/build/junit-results-linux-${{ matrix.mode }}-*.xml
${{ runner.temp }}/build/test-duration-linux-${{ matrix.mode }}-*.json
retention-days: 7

- name: Verify coverage data files exist
Expand Down
14 changes: 11 additions & 3 deletions src/Utilities/Platform/QGCKeychain.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,22 @@ bool indicatesMissingBackend(QKeychain::Error err)
return err == QKeychain::NoBackendAvailable || err == QKeychain::AccessDenied;
}

// Linux libsecret backend reports DBus ServiceUnknown as OtherError — recognize it as "no backend".
// Linux libsecret backend reports a few "no Secret Service reachable" conditions
// as QKeychain::OtherError. Recognize the known patterns so we transparently fall
// back to QSettings instead of failing the call.
bool isMissingSecretService(QKeychain::Error err, const QString& errorString)
{
if (err != QKeychain::OtherError) {
return false;
}
return errorString.contains(QLatin1String("org.freedesktop.secrets")) ||
errorString.contains(QLatin1String("ServiceUnknown"));
// libsecret can't reach the Secret Service over D-Bus.
if (errorString.contains(QLatin1String("org.freedesktop.secrets")) ||
errorString.contains(QLatin1String("ServiceUnknown"))) {
return true;
}
// No session bus to talk to (headless CI without dbus-launch / gnome-keyring).
return errorString.contains(QLatin1String("autolaunch D-Bus")) ||
errorString.contains(QLatin1String("DBUS_SESSION_BUS_ADDRESS"));
}

} // namespace
Expand Down
Loading