Skip to content

Introduce reusable composite GitHub Actions and refactor workflows #44

Introduce reusable composite GitHub Actions and refactor workflows

Introduce reusable composite GitHub Actions and refactor workflows #44

name: Test composite actions
# Self-contained smoke tests for the composite actions in .github/actions/.
# Runs every composite that can be exercised without external secrets and
# asserts on its outputs / side effects. Composites that require live Azure
# Key Vault or live SonarCloud are covered by the downstream integration
# workflows, not here.
on:
push:
paths:
- '.github/actions/**'
- '.github/workflows/Test composite actions.yml'
pull_request:
paths:
- '.github/actions/**'
- '.github/workflows/Test composite actions.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
guard-trigger:
name: guard-trigger
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Invoke composite
uses: ./.github/actions/guard-trigger
- name: Confirm pass
run: echo "guard-trigger passed on event '${{ github.event_name }}'"
resolve-oidc:
name: resolve-oidc
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Explicit inputs
id: explicit
uses: ./.github/actions/resolve-oidc
with:
client-id: 11111111-1111-1111-1111-111111111111
tenant-id: 22222222-2222-2222-2222-222222222222
subscription-id: 33333333-3333-3333-3333-333333333333
repository-owner: NotSkyline
- name: Assert explicit inputs were honored
env:
USE_OIDC: ${{ steps.explicit.outputs.use-oidc }}
CLIENT_ID: ${{ steps.explicit.outputs.client-id }}
run: |
test "$USE_OIDC" = "true"
test "$CLIENT_ID" = "11111111-1111-1111-1111-111111111111"
- name: Skyline defaults
id: skyline
uses: ./.github/actions/resolve-oidc
with:
repository-owner: SkylineCommunications
- name: Assert Skyline defaults
env:
USE_OIDC: ${{ steps.skyline.outputs.use-oidc }}
CLIENT_ID: ${{ steps.skyline.outputs.client-id }}
run: |
test "$USE_OIDC" = "true"
test "$CLIENT_ID" = "c50da9cc-ba14-4138-8595-a62d97ab0e53"
- name: External owner, no inputs
id: external
uses: ./.github/actions/resolve-oidc
with:
repository-owner: SomeOtherOrg
- name: Assert OIDC disabled
env:
USE_OIDC: ${{ steps.external.outputs.use-oidc }}
run: test "$USE_OIDC" = "false"
load-secrets-overrides:
name: load-secrets (overrides only)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Invoke composite with overrides only
uses: ./.github/actions/load-secrets
with:
use-oidc: 'false'
overrides: |
TEST_FOO=hello
TEST_BAR=
TEST_BAZ=world
- name: Assert overrides
run: |
test "$TEST_FOO" = "hello"
test "$TEST_BAZ" = "world"
# Empty override must be skipped.
test -z "${TEST_BAR:-}"
setup-nuget-sources:
name: setup-nuget-sources
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Invoke composite (no Skyline feeds, no azure token)
uses: ./.github/actions/setup-nuget-sources
with:
repository-owner: ${{ github.repository_owner }}
github-token: ${{ secrets.GITHUB_TOKEN }}
include-skyline: 'false'
- name: Assert GitHub source registered
run: dotnet nuget list source | grep -q PrivateGitHubNugets
- name: Re-invoke (idempotency check)
uses: ./.github/actions/setup-nuget-sources
with:
repository-owner: ${{ github.repository_owner }}
github-token: ${{ secrets.GITHUB_TOKEN }}
include-skyline: 'false'
- name: Assert still exactly one entry
run: |
count=$(dotnet nuget list source | grep -c PrivateGitHubNugets)
test "$count" -eq 1
validate-inputs:
name: validate-inputs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: All inputs set (should pass)
uses: ./.github/actions/validate-inputs
with:
sonarcloud-project-name: my-project
sonarcloud-token: sk_fake_token
dataminer-token: dm_fake_token
repository: ${{ github.repository }}
run-number: ${{ github.run_number }}
check-sonar: 'true'
check-dataminer: 'true'
- name: Missing sonar name (should fail) — captured
id: missing-name
continue-on-error: true
uses: ./.github/actions/validate-inputs
with:
sonarcloud-token: sk_fake_token
repository: ${{ github.repository }}
run-number: ${{ github.run_number }}
check-sonar: 'true'
- name: Assert failure was detected
run: |
if [[ "${{ steps.missing-name.outcome }}" != "failure" ]]; then
echo "Expected validate-inputs to fail when sonar name is missing"
exit 1
fi
apply-catalog-identifiers:
name: apply-catalog-identifiers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Stage fake manifest
run: |
mkdir -p Test/CatalogInformation
cat > Test/CatalogInformation/manifest.yml <<'EOF'
id: 00000000-0000-0000-0000-000000000000
name: Test
EOF
- name: Invoke composite
uses: ./.github/actions/apply-catalog-identifiers
with:
mappings: Test/CatalogInformation/manifest.yml=12345678-1234-1234-1234-123456789abc
- name: Assert id rewritten
run: |
grep -q '^id: 12345678-1234-1234-1234-123456789abc$' Test/CatalogInformation/manifest.yml
- name: Invoke with empty mappings (no-op)
uses: ./.github/actions/apply-catalog-identifiers
with:
mappings: ''
apply-source-code-url:
name: apply-source-code-url
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Stage fake manifest with empty source_code_url
run: |
mkdir -p TestSrc/CatalogInformation
cat > TestSrc/CatalogInformation/manifest.yml <<'EOF'
id: 00000000-0000-0000-0000-000000000000
source_code_url:
name: Test
EOF
- name: Invoke composite
uses: ./.github/actions/apply-source-code-url
with:
repository: ${{ github.repository }}
- name: Assert source_code_url filled in
env:
REPO: ${{ github.repository }}
run: |
grep -q "source_code_url: 'https://github.com/$REPO'" TestSrc/CatalogInformation/manifest.yml
detect-test-runner:
name: detect-test-runner
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: No global.json (default vstest)
id: default
uses: ./.github/actions/detect-test-runner
- name: Assert vstest
env:
MODE: ${{ steps.default.outputs.mode }}
run: test "$MODE" = "vstest"
- name: Stage global.json with MTP
run: |
cat > global.json <<'EOF'
{
"test": { "runner": "Microsoft.Testing.Platform" }
}
EOF
- name: Detect MTP
id: mtp
uses: ./.github/actions/detect-test-runner
- name: Assert mtp
env:
MODE: ${{ steps.mtp.outputs.mode }}
run: test "$MODE" = "mtp"
unit-tests:
name: run-unit-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Scaffold a minimal MSTest solution
shell: bash
run: |
mkdir sample && cd sample
dotnet new sln -n Sample
dotnet new mstest -n Sample.Tests --framework net8.0
dotnet sln add Sample.Tests/Sample.Tests.csproj
# Force a VSTest layout: strip the MTP package reference the template adds.
sed -i '/Microsoft\.Testing\.Platform/d' Sample.Tests/Sample.Tests.csproj
dotnet build --configuration Release
- name: Invoke run-unit-tests (vstest)
uses: ./.github/actions/run-unit-tests
with:
solution-path: ${{ github.workspace }}/sample/Sample.slnx
test-runner-mode: vstest
- name: Assert TRX produced
shell: bash
run: |
find sample -path '*/TestResults/*unitTestResults.trx' | grep -q .
- name: Invoke run-unit-tests with invalid mode (must fail)
id: bad-mode
continue-on-error: true
uses: ./.github/actions/run-unit-tests
with:
solution-path: ${{ github.workspace }}/sample/Sample.sln
test-runner-mode: bogus
- name: Assert invalid mode rejected
env:
OUTCOME: ${{ steps.bad-mode.outcome }}
run: test "$OUTCOME" = "failure"
update-global-json-sdks:
name: update-global-json-sdks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# The composite hard-codes the centrally-managed DataMiner SDK
# version. Keep this in sync with $DATAMINER_SDK_VERSION in
# .github/actions/update-global-json-sdks/action.yml.
- name: Expected DataMiner SDK version
id: expected
shell: bash
run: echo "version=2.5.2" >> "$GITHUB_OUTPUT"
- name: No global.json (skip silently)
uses: ./.github/actions/update-global-json-sdks
- name: Stage global.json without msbuild-sdks
shell: bash
run: |
cat > global.json <<'EOF'
{
"sdk": { "version": "8.0.100" }
}
EOF
- name: Invoke composite (no msbuild-sdks, skip silently)
uses: ./.github/actions/update-global-json-sdks
- name: Assert global.json untouched
shell: bash
run: |
grep -q '"version": "8.0.100"' global.json
if grep -q 'msbuild-sdks' global.json; then
echo "Expected no msbuild-sdks section"
exit 1
fi
- name: Stage global.json with managed and unmanaged SDKs
shell: bash
run: |
cat > global.json <<'EOF'
{
"sdk": { "version": "8.0.100" },
"msbuild-sdks": {
"Skyline.DataMiner.Sdk": "0.0.1",
"Skyline.DataMiner.SomethingElse": "0.0.1",
"Microsoft.Build.NoTargets": "3.7.0"
}
}
EOF
- name: Invoke composite (should bump DataMiner family)
uses: ./.github/actions/update-global-json-sdks
- name: Assert managed keys bumped, unmanaged untouched
shell: bash
env:
EXPECTED: ${{ steps.expected.outputs.version }}
run: |
jq -e --arg v "$EXPECTED" '."msbuild-sdks"."Skyline.DataMiner.Sdk" == $v' global.json > /dev/null
jq -e --arg v "$EXPECTED" '."msbuild-sdks"."Skyline.DataMiner.SomethingElse" == $v' global.json > /dev/null
jq -e '."msbuild-sdks"."Microsoft.Build.NoTargets" == "3.7.0"' global.json > /dev/null
- name: Re-invoke (idempotency check)
uses: ./.github/actions/update-global-json-sdks
- name: Assert still in expected state
shell: bash
env:
EXPECTED: ${{ steps.expected.outputs.version }}
run: |
jq -e --arg v "$EXPECTED" '."msbuild-sdks"."Skyline.DataMiner.Sdk" == $v' global.json > /dev/null
jq -e '."msbuild-sdks"."Microsoft.Build.NoTargets" == "3.7.0"' global.json > /dev/null
sonarcloud-status:
# Excluded from automatic runs: requires a live SonarCloud token. Run
# manually via workflow_dispatch when SONAR_TOKEN + SONAR_NAME are set.
name: sonarcloud-status (manual only)
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check prerequisites
id: prereq
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_NAME: ${{ vars.SONAR_NAME }}
run: |
if [[ -z "$SONAR_TOKEN" || -z "$SONAR_NAME" ]]; then
echo "Skipping: SONAR_TOKEN secret or SONAR_NAME variable is not configured."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Invoke composite
if: steps.prereq.outputs.skip != 'true'
uses: ./.github/actions/sonarcloud-status
with:
project-key: ${{ vars.SONAR_NAME }}
branch: ${{ github.ref_name }}
token: ${{ secrets.SONAR_TOKEN }}
repository: ${{ github.repository }}