From 3263b229af68d737af9da69c69beeead6f98d12e Mon Sep 17 00:00:00 2001 From: JP Dillingham Date: Sun, 30 Aug 2026 18:41:45 -0500 Subject: [PATCH] add retries to CI --- .github/workflows/build.yml | 83 +++++++++++++++++++++++++++++++++++-- bin/ci | 60 --------------------------- bin/test | 22 ++++++++++ 3 files changed, 101 insertions(+), 64 deletions(-) delete mode 100755 bin/ci diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2adfb410d..5983b02c4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,19 +5,44 @@ on: branches: [ master ] pull_request: +env: + TEST_ATTEMPTS: 3 + TEST_RETRY_DELAY_SECONDS: 15 + jobs: build: runs-on: ubuntu-latest steps: + - name: Check required secrets + env: + TOKEN_SONARCLOUD: ${{ secrets.TOKEN_SONARCLOUD }} + TOKEN_CODECOV_SOULSEEKNET: ${{ secrets.TOKEN_CODECOV_SOULSEEKNET }} + SLSK_INTEGRATION_USERNAME: ${{ secrets.SLSK_INTEGRATION_USERNAME }} + SLSK_INTEGRATION_PASSWORD: ${{ secrets.SLSK_INTEGRATION_PASSWORD }} + run: | + missing_vars=() + for var in TOKEN_SONARCLOUD TOKEN_CODECOV_SOULSEEKNET SLSK_INTEGRATION_USERNAME SLSK_INTEGRATION_PASSWORD; do + if [ -z "${!var}" ]; then + missing_vars+=("${var}") + fi + done + + if [ ${#missing_vars[@]} -ne 0 ]; then + echo "::error::missing required secret(s): ${missing_vars[*]}" + exit 1 + fi + + echo 'all required secrets are present' + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-dotnet@v4 with: - dotnet-version: '8.0.x' + dotnet-version: '10.0.x' - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v4 # required by sonarscanner with: distribution: temurin java-version: '17' @@ -25,14 +50,64 @@ jobs: - name: Install dotnet-sonarscanner run: dotnet tool install --global dotnet-sonarscanner --version 11.2.1 - - name: Run CI + - name: Begin Sonar analysis env: TOKEN_SONARCLOUD: ${{ secrets.TOKEN_SONARCLOUD }} + run: | + export PATH="$HOME/.dotnet/tools:$PATH" + + # set options for a branch analysis, or for a PR analysis if building a PR + options="/d:sonar.branch.name=${GITHUB_REF_NAME}" + + if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then + pr_number="${GITHUB_REF#refs/pull/}" + pr_number="${pr_number%/merge}" + options="/d:sonar.pullrequest.base=${GITHUB_BASE_REF} /d:sonar.pullrequest.branch=${GITHUB_HEAD_REF} /d:sonar.pullrequest.key=${pr_number}" + fi + + echo "Launching dotnet-sonarscanner with options: ${options}" + + dotnet-sonarscanner begin \ + /key:"jpdillingham_Soulseek.NET" \ + /o:jpdillingham-github \ + ${options} \ + /d:sonar.host.url="https://sonarcloud.io" \ + /d:sonar.github.repository="jpdillingham/Soulseek.NET" \ + /d:sonar.exclusions="**/*examples*/**" \ + /d:sonar.cs.opencover.reportsPaths="tests/opencover.xml" \ + /d:sonar.token="${TOKEN_SONARCLOUD}" + + - name: Build + run: bash ./bin/build + + - name: Test + env: SLSK_INTEGRATION_USERNAME: ${{ secrets.SLSK_INTEGRATION_USERNAME }} SLSK_INTEGRATION_PASSWORD: ${{ secrets.SLSK_INTEGRATION_PASSWORD }} + run: | + for attempt in $(seq 1 "${TEST_ATTEMPTS}"); do + echo "::group::test attempt ${attempt} of ${TEST_ATTEMPTS}" + if bash ./bin/test --all --no-build; then + echo "::endgroup::" + exit 0 + fi + echo "::endgroup::" + + if [ "${attempt}" -lt "${TEST_ATTEMPTS}" ]; then + echo "::warning::tests failed on attempt ${attempt}; retrying in ${TEST_RETRY_DELAY_SECONDS}s" + sleep "${TEST_RETRY_DELAY_SECONDS}" + fi + done + + echo "::error::tests failed after ${TEST_ATTEMPTS} attempts" + exit 1 + + - name: End Sonar analysis + env: + TOKEN_SONARCLOUD: ${{ secrets.TOKEN_SONARCLOUD }} run: | export PATH="$HOME/.dotnet/tools:$PATH" - bash ./bin/ci + dotnet-sonarscanner end /d:sonar.token="${TOKEN_SONARCLOUD}" - name: Upload coverage to Codecov uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # 7.0.0 diff --git a/bin/ci b/bin/ci deleted file mode 100755 index 7e71ddb20..000000000 --- a/bin/ci +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -set -e -dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -required_vars=( - TOKEN_SONARCLOUD - SLSK_INTEGRATION_USERNAME - SLSK_INTEGRATION_PASSWORD -) - -missing_vars=() -for var in "${required_vars[@]}"; do - if [ -z "${!var}" ]; then - missing_vars+=("${var}") - fi -done - -if [ ${#missing_vars[@]} -ne 0 ]; then - echo "Missing required environment variable(s): ${missing_vars[*]}" - exit 1 -fi - -# get the current git branch and set options for a non-PR analysis -branch=$(git rev-parse --abbrev-ref HEAD) -options="/d:sonar.branch.name="${branch}"" - -# if running in GitHub Actions, use the provided environment variables to set analysis options -if [ ! -z "${GITHUB_ACTIONS}" ]; then - branch="${GITHUB_REF_NAME}" - options="/d:sonar.branch.name="${branch}"" - - # if building a PR, GitHub Actions already provides the base and head branches, so set options for PR analysis - if [ "${GITHUB_EVENT_NAME}" = "pull_request" ]; then - base="${GITHUB_BASE_REF}" - branch="${GITHUB_HEAD_REF}" - pr_number="${GITHUB_REF#refs/pull/}" - pr_number="${pr_number%/merge}" - options="/d:sonar.pullrequest.base="${base}" /d:sonar.pullrequest.branch="${branch}" /d:sonar.pullrequest.key="${pr_number}"" - fi -fi - -echo "Launching dotnet-sonarscanner with options: ${options}" - -# disable git bash/mingw path mangling on Windows -export MSYS2_ARG_CONV_EXCL="*" - -dotnet-sonarscanner begin \ - /key:"jpdillingham_Soulseek.NET" \ - /o:jpdillingham-github \ - ${options} \ - /d:sonar.host.url="https://sonarcloud.io" \ - /d:sonar.github.repository="jpdillingham/Soulseek.NET" \ - /d:sonar.exclusions="**/*examples*/**" \ - /d:sonar.cs.opencover.reportsPaths="tests/opencover.xml" \ - /d:sonar.token="${TOKEN_SONARCLOUD}" - -. "${dir}/build" -. "${dir}/test" --all --no-build - -dotnet-sonarscanner end /d:sonar.token="${TOKEN_SONARCLOUD}" \ No newline at end of file diff --git a/bin/test b/bin/test index 20b28d49f..a8c963291 100755 --- a/bin/test +++ b/bin/test @@ -40,6 +40,28 @@ if [ "$run_unit" == false ] && [ "$run_integration" == false ]; then exit 1 fi +# integration tests sign in to the live network; if credentials are missing, warn and skip +# them rather than failing, so that unit tests can still run +if [ "$run_integration" == true ]; then + missing_vars=() + for var in SLSK_INTEGRATION_USERNAME SLSK_INTEGRATION_PASSWORD; do + if [ -z "${!var}" ]; then + missing_vars+=("${var}") + fi + done + + if [ ${#missing_vars[@]} -ne 0 ]; then + echo "WARNING: skipping integration tests; missing environment variable(s): ${missing_vars[*]}" + run_integration=false + + # nothing left to run, so don't exit 0 having tested nothing + if [ "$run_unit" == false ]; then + echo 'no tests left to run' + exit 1 + fi + fi +fi + # if the caller passed --no-build through, trust them and skip our own staleness check no_build=false for arg in "${extra_args[@]}"; do