Merge branch 'main' of https://git.stella-ops.org/stella-ops.org/git.stella-ops.org
This commit is contained in:
@@ -15,7 +15,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
package-feeds:
|
package-feeds:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||||
@@ -68,3 +68,4 @@ jobs:
|
|||||||
out/advisory-ai/feeds/provenance.json
|
out/advisory-ai/feeds/provenance.json
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sealed-smoke:
|
sealed-smoke:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
@@ -26,3 +26,4 @@ jobs:
|
|||||||
run: pip install dnslib
|
run: pip install dnslib
|
||||||
- name: Run sealed-mode smoke
|
- name: Run sealed-mode smoke
|
||||||
run: sudo devops/airgap/sealed-ci-smoke.sh
|
run: sudo devops/airgap/sealed-ci-smoke.sh
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
package-backfill:
|
package-backfill:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||||
@@ -81,3 +81,4 @@ jobs:
|
|||||||
out/aoc/SHA256SUMS
|
out/aoc/SHA256SUMS
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
aoc-guard:
|
aoc-guard:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_VERSION: '10.0.100'
|
DOTNET_VERSION: '10.0.100'
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
|
|
||||||
aoc-verify:
|
aoc-verify:
|
||||||
needs: aoc-guard
|
needs: aoc-guard
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
if: github.event_name != 'schedule'
|
if: github.event_name != 'schedule'
|
||||||
env:
|
env:
|
||||||
DOTNET_VERSION: '10.0.100'
|
DOTNET_VERSION: '10.0.100'
|
||||||
@@ -168,3 +168,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: aoc-verify-artifacts
|
name: aoc-verify-artifacts
|
||||||
path: ${{ env.ARTIFACT_DIR }}
|
path: ${{ env.ARTIFACT_DIR }}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
rotate:
|
rotate:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
environment: ${{ inputs.environment }}
|
environment: ${{ inputs.environment }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -164,3 +164,4 @@ jobs:
|
|||||||
echo "Key Path: ${{ inputs.key_path }}"
|
echo "Key Path: ${{ inputs.key_path }}"
|
||||||
echo "Source: ${{ inputs.source }}"
|
echo "Source: ${{ inputs.source }}"
|
||||||
echo "Algorithm: ${{ inputs.algorithm }}"
|
echo "Algorithm: ${{ inputs.algorithm }}"
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
profile-validation:
|
profile-validation:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
run: ./devops/tools/validate-profiles.sh
|
run: ./devops/tools/validate-profiles.sh
|
||||||
|
|
||||||
build-test:
|
build-test:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
environment: ${{ github.event_name == 'pull_request' && 'preview' || 'staging' }}
|
environment: ${{ github.event_name == 'pull_request' && 'preview' || 'staging' }}
|
||||||
env:
|
env:
|
||||||
PUBLISH_DIR: ${{ github.workspace }}/artifacts/publish/webservice
|
PUBLISH_DIR: ${{ github.workspace }}/artifacts/publish/webservice
|
||||||
@@ -590,7 +590,7 @@ PY
|
|||||||
# Quality Gates Foundation (Sprint 0350)
|
# Quality Gates Foundation (Sprint 0350)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
quality-gates:
|
quality-gates:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-test
|
needs: build-test
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -674,7 +674,7 @@ PY
|
|||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|
||||||
security-testing:
|
security-testing:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-test
|
needs: build-test
|
||||||
if: github.event_name == 'pull_request' || github.event_name == 'schedule'
|
if: github.event_name == 'pull_request' || github.event_name == 'schedule'
|
||||||
permissions:
|
permissions:
|
||||||
@@ -717,7 +717,7 @@ PY
|
|||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
mutation-testing:
|
mutation-testing:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-test
|
needs: build-test
|
||||||
if: github.event_name == 'schedule' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'mutation-test'))
|
if: github.event_name == 'schedule' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'mutation-test'))
|
||||||
permissions:
|
permissions:
|
||||||
@@ -790,7 +790,7 @@ PY
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
sealed-mode-ci:
|
sealed-mode-ci:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-test
|
needs: build-test
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -828,7 +828,7 @@ PY
|
|||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|
||||||
authority-container:
|
authority-container:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-test
|
needs: build-test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -843,7 +843,7 @@ PY
|
|||||||
excititor-batch-validation:
|
excititor-batch-validation:
|
||||||
needs: build-test
|
needs: build-test
|
||||||
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.excititor_batch == 'true')
|
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.excititor_batch == 'true')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
BATCH_RESULTS_DIR: ${{ github.workspace }}/artifacts/test-results/excititor-batch
|
BATCH_RESULTS_DIR: ${{ github.workspace }}/artifacts/test-results/excititor-batch
|
||||||
steps:
|
steps:
|
||||||
@@ -876,7 +876,7 @@ PY
|
|||||||
path: ${{ env.BATCH_RESULTS_DIR }}
|
path: ${{ env.BATCH_RESULTS_DIR }}
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOCS_OUTPUT_DIR: ${{ github.workspace }}/artifacts/docs-site
|
DOCS_OUTPUT_DIR: ${{ github.workspace }}/artifacts/docs-site
|
||||||
steps:
|
steps:
|
||||||
@@ -906,7 +906,7 @@ PY
|
|||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
scanner-perf:
|
scanner-perf:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-test
|
needs: build-test
|
||||||
env:
|
env:
|
||||||
BENCH_DIR: src/Bench/StellaOps.Bench/Scanner.Analyzers
|
BENCH_DIR: src/Bench/StellaOps.Bench/Scanner.Analyzers
|
||||||
@@ -987,7 +987,7 @@ PY
|
|||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [build-test, docs, scanner-perf]
|
needs: [build-test, docs, scanner-perf]
|
||||||
if: >-
|
if: >-
|
||||||
needs.build-test.result == 'success' &&
|
needs.build-test.result == 'success' &&
|
||||||
@@ -1160,7 +1160,7 @@ PY
|
|||||||
echo " Ref: ${{ github.ref }}"
|
echo " Ref: ${{ github.ref }}"
|
||||||
|
|
||||||
notify-smoke:
|
notify-smoke:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: deploy
|
needs: deploy
|
||||||
if: needs.deploy.result == 'success'
|
if: needs.deploy.result == 'success'
|
||||||
env:
|
env:
|
||||||
@@ -1202,3 +1202,4 @@ PY
|
|||||||
|
|
||||||
- name: Run Notify smoke validation
|
- name: Run Notify smoke validation
|
||||||
run: dotnet run --project tools/NotifySmokeCheck/NotifySmokeCheck.csproj --configuration Release
|
run: dotnet run --project tools/NotifySmokeCheck/NotifySmokeCheck.csproj --configuration Release
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-dataset:
|
build-dataset:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/out/linksets
|
ARTIFACT_DIR: ${{ github.workspace }}/out/linksets
|
||||||
steps:
|
steps:
|
||||||
@@ -30,3 +30,4 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst
|
${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst
|
||||||
${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst.sha256
|
${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst.sha256
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
detect-drift:
|
detect-drift:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@@ -124,7 +124,7 @@ jobs:
|
|||||||
create-pr:
|
create-pr:
|
||||||
needs: detect-drift
|
needs: detect-drift
|
||||||
if: needs.detect-drift.outputs.has_drift == 'true' && (github.event.inputs.create_pr == 'true' || github.event_name == 'schedule')
|
if: needs.detect-drift.outputs.has_drift == 'true' && (github.event.inputs.create_pr == 'true' || github.event_name == 'schedule')
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@@ -245,3 +245,4 @@ This commit was auto-generated by the connector-fixture-drift workflow.
|
|||||||
issue_number: pr.number,
|
issue_number: pr.number,
|
||||||
labels: ['automated', 'fixtures', 'schema-drift']
|
labels: ['automated', 'fixtures', 'schema-drift']
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
crypto-audit:
|
crypto-audit:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -42,3 +42,4 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
scripts/audit-crypto-usage.ps1
|
scripts/audit-crypto-usage.ps1
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
|
|
||||||
detect:
|
detect:
|
||||||
name: Detect Dead Paths
|
name: Detect Dead Paths
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
has-new-dead-paths: ${{ steps.check.outputs.has_new_dead_paths }}
|
has-new-dead-paths: ${{ steps.check.outputs.has_new_dead_paths }}
|
||||||
new-dead-path-count: ${{ steps.check.outputs.new_count }}
|
new-dead-path-count: ${{ steps.check.outputs.new_count }}
|
||||||
@@ -354,7 +354,7 @@ jobs:
|
|||||||
name: Post Report
|
name: Post Report
|
||||||
needs: detect
|
needs: detect
|
||||||
if: github.event_name == 'pull_request' && always()
|
if: github.event_name == 'pull_request' && always()
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
@@ -436,3 +436,4 @@ jobs:
|
|||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
pre-flight:
|
pre-flight:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
identity-pattern: ${{ steps.config.outputs.identity-pattern }}
|
identity-pattern: ${{ steps.config.outputs.identity-pattern }}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
|
|
||||||
verify-attestations:
|
verify-attestations:
|
||||||
needs: pre-flight
|
needs: pre-flight
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -123,7 +123,7 @@ jobs:
|
|||||||
|
|
||||||
verify-provenance:
|
verify-provenance:
|
||||||
needs: pre-flight
|
needs: pre-flight
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ jobs:
|
|||||||
|
|
||||||
create-audit-entry:
|
create-audit-entry:
|
||||||
needs: [verify-attestations, verify-provenance]
|
needs: [verify-attestations, verify-provenance]
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install StellaOps CLI
|
- name: Install StellaOps CLI
|
||||||
@@ -183,7 +183,7 @@ jobs:
|
|||||||
|
|
||||||
approve-deployment:
|
approve-deployment:
|
||||||
needs: [verify-attestations, verify-provenance, create-audit-entry]
|
needs: [verify-attestations, verify-provenance, create-audit-entry]
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
environment: ${{ github.event.inputs.environment }}
|
environment: ${{ github.event.inputs.environment }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -202,3 +202,4 @@ jobs:
|
|||||||
|
|
||||||
Deployment can now proceed.
|
Deployment can now proceed.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
schema-validation:
|
schema-validation:
|
||||||
name: Schema Validation
|
name: Schema Validation
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
if: github.event.inputs.skip_schema_validation != 'true'
|
if: github.event.inputs.skip_schema_validation != 'true'
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
|
|
||||||
@@ -128,7 +128,7 @@ jobs:
|
|||||||
needs: [schema-validation]
|
needs: [schema-validation]
|
||||||
if: always() && (needs.schema-validation.result == 'success' || needs.schema-validation.result == 'skipped')
|
if: always() && (needs.schema-validation.result == 'success' || needs.schema-validation.result == 'skipped')
|
||||||
name: Determinism Validation
|
name: Determinism Validation
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
@@ -243,7 +243,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
update-baselines:
|
update-baselines:
|
||||||
name: Update Baselines
|
name: Update Baselines
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [schema-validation, determinism-gate]
|
needs: [schema-validation, determinism-gate]
|
||||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.update_baselines == 'true'
|
if: github.event_name == 'workflow_dispatch' && github.event.inputs.update_baselines == 'true'
|
||||||
|
|
||||||
@@ -293,7 +293,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
drift-check:
|
drift-check:
|
||||||
name: Drift Detection Gate
|
name: Drift Detection Gate
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [schema-validation, determinism-gate]
|
needs: [schema-validation, determinism-gate]
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
@@ -328,3 +328,4 @@ jobs:
|
|||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Schema Validation: ${{ needs.schema-validation.result || 'skipped' }}" >> $GITHUB_STEP_SUMMARY
|
echo "Schema Validation: ${{ needs.schema-validation.result || 'skipped' }}" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Determinism Status: ${{ needs.determinism-gate.outputs.status || 'pass' }}" >> $GITHUB_STEP_SUMMARY
|
echo "Determinism Status: ${{ needs.determinism-gate.outputs.status || 'pass' }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint-and-preview:
|
lint-and-preview:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOCS_OUTPUT_DIR: ${{ github.workspace }}/artifacts/docs-preview
|
DOCS_OUTPUT_DIR: ${{ github.workspace }}/artifacts/docs-preview
|
||||||
steps:
|
steps:
|
||||||
@@ -99,3 +99,4 @@ jobs:
|
|||||||
name: feedser-docs-preview
|
name: feedser-docs-preview
|
||||||
path: ${{ env.DOCS_OUTPUT_DIR }}
|
path: ${{ env.DOCS_OUTPUT_DIR }}
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
perf:
|
perf:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -96,3 +96,4 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
bench/results/epss-ingest-perf-${{ github.sha }}.json
|
bench/results/epss-ingest-perf-${{ github.sha }}.json
|
||||||
retention-days: 90
|
retention-days: 90
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
export-ci:
|
export-ci:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_VERSION: '10.0.100'
|
DOTNET_VERSION: '10.0.100'
|
||||||
MINIO_ACCESS_KEY: exportci
|
MINIO_ACCESS_KEY: exportci
|
||||||
@@ -83,3 +83,4 @@ jobs:
|
|||||||
- name: Teardown MinIO
|
- name: Teardown MinIO
|
||||||
if: always()
|
if: always()
|
||||||
run: docker compose -f devops/export/minio-compose.yml down -v
|
run: docker compose -f devops/export/minio-compose.yml down -v
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-test:
|
build-test:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
TEST_RESULTS_DIR: ${{ github.workspace }}/artifacts/test-results
|
TEST_RESULTS_DIR: ${{ github.workspace }}/artifacts/test-results
|
||||||
steps:
|
steps:
|
||||||
@@ -68,7 +68,7 @@ jobs:
|
|||||||
path: ${{ env.TEST_RESULTS_DIR }}
|
path: ${{ env.TEST_RESULTS_DIR }}
|
||||||
|
|
||||||
migration-validation:
|
migration-validation:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
@@ -228,7 +228,7 @@ jobs:
|
|||||||
echo "✓ Migration is idempotent"
|
echo "✓ Migration is idempotent"
|
||||||
|
|
||||||
generate-manifest:
|
generate-manifest:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [build-test, migration-validation]
|
needs: [build-test, migration-validation]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -323,3 +323,4 @@ jobs:
|
|||||||
name: findings-ledger-migrations
|
name: findings-ledger-migrations
|
||||||
path: out/findings-ledger/
|
path: out/findings-ledger/
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
refresh:
|
refresh:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
env:
|
env:
|
||||||
@@ -66,3 +66,4 @@ jobs:
|
|||||||
path: out/feeds/icscisa-kisa/${{ steps.meta.outputs.run_date }}
|
path: out/feeds/icscisa-kisa/${{ steps.meta.outputs.run_date }}
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 21
|
retention-days: 21
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
interop-tests:
|
interop-tests:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -96,7 +96,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
summary:
|
summary:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: interop-tests
|
needs: interop-tests
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
@@ -126,3 +126,4 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
echo "| ${format} | ${STATUS} |" >> $GITHUB_STEP_SUMMARY
|
echo "| ${format} | ${STATUS} |" >> $GITHUB_STEP_SUMMARY
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate-oas:
|
validate-oas:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
|
|
||||||
check-wellknown:
|
check-wellknown:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: validate-oas
|
needs: validate-oas
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -64,7 +64,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
deprecation-check:
|
deprecation-check:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: validate-oas
|
needs: validate-oas
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -79,3 +79,4 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "[info] No deprecation policy yet (OK for initial setup)"
|
echo "[info] No deprecation policy yet (OK for initial setup)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-pack:
|
build-pack:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||||
steps:
|
steps:
|
||||||
@@ -71,7 +71,7 @@ jobs:
|
|||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
verify-pack:
|
verify-pack:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-pack
|
needs: build-pack
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -99,3 +99,4 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
nuget-license-audit:
|
nuget-license-audit:
|
||||||
name: NuGet License Audit
|
name: NuGet License Audit
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -94,7 +94,7 @@ jobs:
|
|||||||
|
|
||||||
npm-license-audit:
|
npm-license-audit:
|
||||||
name: npm License Audit
|
name: npm License Audit
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -142,7 +142,7 @@ jobs:
|
|||||||
|
|
||||||
vendored-license-check:
|
vendored-license-check:
|
||||||
name: Vendored Components Check
|
name: Vendored Components Check
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -207,7 +207,7 @@ jobs:
|
|||||||
|
|
||||||
license-compatibility-check:
|
license-compatibility-check:
|
||||||
name: License Compatibility Check
|
name: License Compatibility Check
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [nuget-license-audit, npm-license-audit]
|
needs: [nuget-license-audit, npm-license-audit]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -297,3 +297,4 @@ jobs:
|
|||||||
name: license-audit-summary
|
name: license-audit-summary
|
||||||
path: out/combined
|
path: out/combined
|
||||||
retention-days: 90
|
retention-days: 90
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
lighthouse:
|
lighthouse:
|
||||||
name: Lighthouse Audit
|
name: Lighthouse Audit
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: src/Web/StellaOps.Web
|
working-directory: src/Web/StellaOps.Web
|
||||||
@@ -145,7 +145,7 @@ jobs:
|
|||||||
|
|
||||||
axe-accessibility:
|
axe-accessibility:
|
||||||
name: Axe Accessibility Audit
|
name: Axe Accessibility Audit
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: src/Web/StellaOps.Web
|
working-directory: src/Web/StellaOps.Web
|
||||||
@@ -186,3 +186,4 @@ jobs:
|
|||||||
name: axe-accessibility-results
|
name: axe-accessibility-results
|
||||||
path: src/Web/StellaOps.Web/test-results/
|
path: src/Web/StellaOps.Web/test-results/
|
||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lnm-backfill:
|
lnm-backfill:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_VERSION: '10.0.100'
|
DOTNET_VERSION: '10.0.100'
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||||
@@ -62,3 +62,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: lnm-backfill-artifacts
|
name: lnm-backfill-artifacts
|
||||||
path: ${{ env.ARTIFACT_DIR }}
|
path: ${{ env.ARTIFACT_DIR }}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-runner:
|
build-runner:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
|
|
||||||
validate-metrics:
|
validate-metrics:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-runner
|
needs: build-runner
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -81,3 +81,4 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Monitoring config validation complete"
|
echo "Monitoring config validation complete"
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
vex-backfill:
|
vex-backfill:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_VERSION: '10.0.100'
|
DOTNET_VERSION: '10.0.100'
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||||
@@ -61,3 +61,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: lnm-vex-backfill-artifacts
|
name: lnm-vex-backfill-artifacts
|
||||||
path: ${{ env.ARTIFACT_DIR }}
|
path: ${{ env.ARTIFACT_DIR }}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ jobs:
|
|||||||
|
|
||||||
discover:
|
discover:
|
||||||
name: Discover Migrations
|
name: Discover Migrations
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
modules: ${{ steps.find.outputs.modules }}
|
modules: ${{ steps.find.outputs.modules }}
|
||||||
module_count: ${{ steps.find.outputs.count }}
|
module_count: ${{ steps.find.outputs.count }}
|
||||||
@@ -119,7 +119,7 @@ jobs:
|
|||||||
|
|
||||||
forward-migrations:
|
forward-migrations:
|
||||||
name: Forward Migration
|
name: Forward Migration
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
needs: discover
|
needs: discover
|
||||||
if: needs.discover.outputs.module_count != '0'
|
if: needs.discover.outputs.module_count != '0'
|
||||||
@@ -246,7 +246,7 @@ jobs:
|
|||||||
|
|
||||||
rollback-migrations:
|
rollback-migrations:
|
||||||
name: Rollback Migration
|
name: Rollback Migration
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
needs: [discover, forward-migrations]
|
needs: [discover, forward-migrations]
|
||||||
if: |
|
if: |
|
||||||
@@ -371,7 +371,7 @@ jobs:
|
|||||||
|
|
||||||
idempotency:
|
idempotency:
|
||||||
name: Idempotency Test
|
name: Idempotency Test
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
needs: [discover, forward-migrations]
|
needs: [discover, forward-migrations]
|
||||||
if: |
|
if: |
|
||||||
@@ -490,7 +490,7 @@ jobs:
|
|||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: Migration Summary
|
name: Migration Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [discover, forward-migrations, rollback-migrations, idempotency]
|
needs: [discover, forward-migrations, rollback-migrations, idempotency]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -510,3 +510,4 @@ jobs:
|
|||||||
- name: Check for failures
|
- name: Check for failures
|
||||||
if: contains(needs.*.result, 'failure')
|
if: contains(needs.*.result, 'failure')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
mirror-sign:
|
mirror-sign:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
MIRROR_SIGN_KEY_B64: ${{ secrets.MIRROR_SIGN_KEY_B64 }}
|
MIRROR_SIGN_KEY_B64: ${{ secrets.MIRROR_SIGN_KEY_B64 }}
|
||||||
REQUIRE_PROD_SIGNING: 1
|
REQUIRE_PROD_SIGNING: 1
|
||||||
@@ -72,3 +72,4 @@ jobs:
|
|||||||
out/mirror/thin/export-center/schedule-response.json
|
out/mirror/thin/export-center/schedule-response.json
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ jobs:
|
|||||||
|
|
||||||
parse-tag:
|
parse-tag:
|
||||||
name: Parse Tag
|
name: Parse Tag
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
outputs:
|
outputs:
|
||||||
module: ${{ steps.parse.outputs.module }}
|
module: ${{ steps.parse.outputs.module }}
|
||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
|
|
||||||
validate:
|
validate:
|
||||||
name: Validate Inputs
|
name: Validate Inputs
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [parse-tag]
|
needs: [parse-tag]
|
||||||
if: always() && (needs.parse-tag.result == 'success' || needs.parse-tag.result == 'skipped')
|
if: always() && (needs.parse-tag.result == 'success' || needs.parse-tag.result == 'skipped')
|
||||||
outputs:
|
outputs:
|
||||||
@@ -139,7 +139,7 @@ jobs:
|
|||||||
|
|
||||||
publish-nuget:
|
publish-nuget:
|
||||||
name: Publish NuGet
|
name: Publish NuGet
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate]
|
needs: [validate]
|
||||||
if: needs.validate.outputs.publish_nuget == 'true'
|
if: needs.validate.outputs.publish_nuget == 'true'
|
||||||
steps:
|
steps:
|
||||||
@@ -251,7 +251,7 @@ jobs:
|
|||||||
|
|
||||||
publish-container:
|
publish-container:
|
||||||
name: Publish Container
|
name: Publish Container
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate]
|
needs: [validate]
|
||||||
if: needs.validate.outputs.publish_container == 'true' && needs.validate.outputs.module != 'CLI'
|
if: needs.validate.outputs.publish_container == 'true' && needs.validate.outputs.module != 'CLI'
|
||||||
steps:
|
steps:
|
||||||
@@ -310,7 +310,7 @@ jobs:
|
|||||||
|
|
||||||
publish-cli:
|
publish-cli:
|
||||||
name: Publish CLI (${{ matrix.runtime }})
|
name: Publish CLI (${{ matrix.runtime }})
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate]
|
needs: [validate]
|
||||||
if: needs.validate.outputs.module == 'CLI'
|
if: needs.validate.outputs.module == 'CLI'
|
||||||
strategy:
|
strategy:
|
||||||
@@ -378,7 +378,7 @@ jobs:
|
|||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: Publish Summary
|
name: Publish Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, publish-nuget, publish-container, publish-cli]
|
needs: [validate, publish-nuget, publish-container, publish-cli]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -403,3 +403,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "::error::One or more publish jobs failed"
|
echo "::error::One or more publish jobs failed"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
|
|
||||||
prepare:
|
prepare:
|
||||||
name: Prepare Nightly Run
|
name: Prepare Nightly Run
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
run_id: ${{ steps.metadata.outputs.run_id }}
|
run_id: ${{ steps.metadata.outputs.run_id }}
|
||||||
run_date: ${{ steps.metadata.outputs.run_date }}
|
run_date: ${{ steps.metadata.outputs.run_date }}
|
||||||
@@ -88,7 +88,7 @@ jobs:
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
name: Full Build
|
name: Full Build
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
needs: prepare
|
needs: prepare
|
||||||
steps:
|
steps:
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
|
|
||||||
test-pr-gating:
|
test-pr-gating:
|
||||||
name: PR-Gating Tests
|
name: PR-Gating Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
needs: build
|
needs: build
|
||||||
services:
|
services:
|
||||||
@@ -184,7 +184,7 @@ jobs:
|
|||||||
|
|
||||||
test-extended:
|
test-extended:
|
||||||
name: Extended Tests
|
name: Extended Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
needs: build
|
needs: build
|
||||||
if: github.event.inputs.skip_performance != 'true'
|
if: github.event.inputs.skip_performance != 'true'
|
||||||
@@ -227,7 +227,7 @@ jobs:
|
|||||||
|
|
||||||
determinism:
|
determinism:
|
||||||
name: Determinism Verification
|
name: Determinism Verification
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
needs: build
|
needs: build
|
||||||
if: github.event.inputs.skip_determinism != 'true'
|
if: github.event.inputs.skip_determinism != 'true'
|
||||||
@@ -289,7 +289,7 @@ jobs:
|
|||||||
|
|
||||||
cross-module:
|
cross-module:
|
||||||
name: Cross-Module Validation
|
name: Cross-Module Validation
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
needs: build
|
needs: build
|
||||||
steps:
|
steps:
|
||||||
@@ -341,7 +341,7 @@ jobs:
|
|||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
name: Code Coverage
|
name: Code Coverage
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
needs: build
|
needs: build
|
||||||
services:
|
services:
|
||||||
@@ -415,7 +415,7 @@ jobs:
|
|||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: Nightly Summary
|
name: Nightly Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs:
|
needs:
|
||||||
- prepare
|
- prepare
|
||||||
- build
|
- build
|
||||||
@@ -483,3 +483,4 @@ jobs:
|
|||||||
- name: Exit with appropriate code
|
- name: Exit with appropriate code
|
||||||
if: steps.status.outputs.status == 'failure'
|
if: steps.status.outputs.status == 'failure'
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
offline-e2e:
|
offline-e2e:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
path: ./results/
|
path: ./results/
|
||||||
|
|
||||||
verify-isolation:
|
verify-isolation:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: offline-e2e
|
needs: offline-e2e
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
@@ -119,3 +119,4 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo "⚠️ No test results found" >> $GITHUB_STEP_SUMMARY
|
echo "⚠️ No test results found" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
policy-lint:
|
policy-lint:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -68,3 +68,4 @@ jobs:
|
|||||||
name: policy-lint
|
name: policy-lint
|
||||||
path: out/policy-lint
|
path: out/policy-lint
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
policy-simulate:
|
policy-simulate:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -87,3 +87,4 @@ jobs:
|
|||||||
name: policy-signing
|
name: policy-signing
|
||||||
path: out/policy-sign
|
path: out/policy-sign
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
promote:
|
promote:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
environment: production
|
environment: production
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -207,3 +207,4 @@ jobs:
|
|||||||
else
|
else
|
||||||
echo " Docs: skipped"
|
echo " Docs: skipped"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
benchmark:
|
benchmark:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -242,7 +242,7 @@ jobs:
|
|||||||
update-baseline:
|
update-baseline:
|
||||||
needs: benchmark
|
needs: benchmark
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.benchmark.outputs.regression != 'true'
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main' && needs.benchmark.outputs.regression != 'true'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -263,7 +263,7 @@ jobs:
|
|||||||
notify-pr:
|
notify-pr:
|
||||||
needs: benchmark
|
needs: benchmark
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
@@ -304,3 +304,4 @@ jobs:
|
|||||||
repo: context.repo.repo,
|
repo: context.repo.repo,
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
validate-corpus:
|
validate-corpus:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -136,7 +136,7 @@ jobs:
|
|||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|
||||||
validate-ground-truths:
|
validate-ground-truths:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
TZ: UTC
|
TZ: UTC
|
||||||
steps:
|
steps:
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
determinism-check:
|
determinism-check:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
TZ: UTC
|
TZ: UTC
|
||||||
needs: validate-corpus
|
needs: validate-corpus
|
||||||
@@ -265,3 +265,4 @@ jobs:
|
|||||||
|
|
||||||
print(f"Checked {len(json_files)} JSON files")
|
print(f"Checked {len(json_files)} JSON files")
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sign-images:
|
sign-images:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
contents: read
|
contents: read
|
||||||
@@ -170,7 +170,7 @@ jobs:
|
|||||||
--registry "stellaops/gateway"
|
--registry "stellaops/gateway"
|
||||||
|
|
||||||
sign-binaries:
|
sign-binaries:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
id-token: write
|
id-token: write
|
||||||
contents: read
|
contents: read
|
||||||
@@ -329,7 +329,7 @@ jobs:
|
|||||||
|
|
||||||
verify-signatures:
|
verify-signatures:
|
||||||
needs: [sign-images, sign-binaries]
|
needs: [sign-images, sign-binaries]
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: read
|
packages: read
|
||||||
@@ -397,3 +397,4 @@ jobs:
|
|||||||
--certificate-oidc-issuer "https://git.stella-ops.org"
|
--certificate-oidc-issuer "https://git.stella-ops.org"
|
||||||
\`\`\`
|
\`\`\`
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
|
|
||||||
parse-tag:
|
parse-tag:
|
||||||
name: Parse Tag
|
name: Parse Tag
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.parse.outputs.version }}
|
version: ${{ steps.parse.outputs.version }}
|
||||||
@@ -89,7 +89,7 @@ jobs:
|
|||||||
|
|
||||||
validate:
|
validate:
|
||||||
name: Validate Release
|
name: Validate Release
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [parse-tag]
|
needs: [parse-tag]
|
||||||
if: always() && (needs.parse-tag.result == 'success' || needs.parse-tag.result == 'skipped')
|
if: always() && (needs.parse-tag.result == 'success' || needs.parse-tag.result == 'skipped')
|
||||||
outputs:
|
outputs:
|
||||||
@@ -150,7 +150,7 @@ jobs:
|
|||||||
|
|
||||||
test-gate:
|
test-gate:
|
||||||
name: Test Gate
|
name: Test Gate
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate]
|
needs: [validate]
|
||||||
if: github.event.inputs.skip_tests != 'true'
|
if: github.event.inputs.skip_tests != 'true'
|
||||||
steps:
|
steps:
|
||||||
@@ -192,7 +192,7 @@ jobs:
|
|||||||
|
|
||||||
build-modules:
|
build-modules:
|
||||||
name: Build ${{ matrix.module }}
|
name: Build ${{ matrix.module }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, test-gate]
|
needs: [validate, test-gate]
|
||||||
if: always() && needs.validate.result == 'success' && (needs.test-gate.result == 'success' || needs.test-gate.result == 'skipped')
|
if: always() && needs.validate.result == 'success' && (needs.test-gate.result == 'success' || needs.test-gate.result == 'skipped')
|
||||||
strategy:
|
strategy:
|
||||||
@@ -293,7 +293,7 @@ jobs:
|
|||||||
|
|
||||||
build-containers:
|
build-containers:
|
||||||
name: Container ${{ matrix.module }}
|
name: Container ${{ matrix.module }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-modules]
|
needs: [validate, build-modules]
|
||||||
if: needs.validate.outputs.dry_run != 'true'
|
if: needs.validate.outputs.dry_run != 'true'
|
||||||
strategy:
|
strategy:
|
||||||
@@ -351,7 +351,7 @@ jobs:
|
|||||||
|
|
||||||
build-cli:
|
build-cli:
|
||||||
name: CLI (${{ matrix.runtime }})
|
name: CLI (${{ matrix.runtime }})
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, test-gate]
|
needs: [validate, test-gate]
|
||||||
if: always() && needs.validate.result == 'success' && (needs.test-gate.result == 'success' || needs.test-gate.result == 'skipped')
|
if: always() && needs.validate.result == 'success' && (needs.test-gate.result == 'success' || needs.test-gate.result == 'skipped')
|
||||||
strategy:
|
strategy:
|
||||||
@@ -421,7 +421,7 @@ jobs:
|
|||||||
|
|
||||||
build-helm:
|
build-helm:
|
||||||
name: Helm Chart
|
name: Helm Chart
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate]
|
needs: [validate]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -459,7 +459,7 @@ jobs:
|
|||||||
|
|
||||||
release-manifest:
|
release-manifest:
|
||||||
name: Release Manifest
|
name: Release Manifest
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-modules, build-cli, build-helm]
|
needs: [validate, build-modules, build-cli, build-helm]
|
||||||
if: always() && needs.validate.result == 'success'
|
if: always() && needs.validate.result == 'success'
|
||||||
steps:
|
steps:
|
||||||
@@ -538,7 +538,7 @@ jobs:
|
|||||||
|
|
||||||
generate-changelog:
|
generate-changelog:
|
||||||
name: Generate Changelog
|
name: Generate Changelog
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-modules]
|
needs: [validate, build-modules]
|
||||||
if: always() && needs.validate.result == 'success'
|
if: always() && needs.validate.result == 'success'
|
||||||
steps:
|
steps:
|
||||||
@@ -595,7 +595,7 @@ jobs:
|
|||||||
|
|
||||||
generate-suite-docs:
|
generate-suite-docs:
|
||||||
name: Generate Suite Docs
|
name: Generate Suite Docs
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, generate-changelog, release-manifest]
|
needs: [validate, generate-changelog, release-manifest]
|
||||||
if: always() && needs.validate.result == 'success'
|
if: always() && needs.validate.result == 'success'
|
||||||
steps:
|
steps:
|
||||||
@@ -658,7 +658,7 @@ jobs:
|
|||||||
|
|
||||||
generate-compose:
|
generate-compose:
|
||||||
name: Generate Docker Compose
|
name: Generate Docker Compose
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, release-manifest]
|
needs: [validate, release-manifest]
|
||||||
if: always() && needs.validate.result == 'success'
|
if: always() && needs.validate.result == 'success'
|
||||||
steps:
|
steps:
|
||||||
@@ -704,7 +704,7 @@ jobs:
|
|||||||
|
|
||||||
commit-docs:
|
commit-docs:
|
||||||
name: Commit Documentation
|
name: Commit Documentation
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, generate-suite-docs, generate-compose, create-release]
|
needs: [validate, generate-suite-docs, generate-compose, create-release]
|
||||||
if: needs.validate.outputs.dry_run != 'true' && needs.create-release.result == 'success'
|
if: needs.validate.outputs.dry_run != 'true' && needs.create-release.result == 'success'
|
||||||
steps:
|
steps:
|
||||||
@@ -765,7 +765,7 @@ jobs:
|
|||||||
|
|
||||||
create-release:
|
create-release:
|
||||||
name: Create Gitea Release
|
name: Create Gitea Release
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-modules, build-containers, build-cli, build-helm, release-manifest]
|
needs: [validate, build-modules, build-containers, build-cli, build-helm, release-manifest]
|
||||||
if: needs.validate.outputs.dry_run != 'true'
|
if: needs.validate.outputs.dry_run != 'true'
|
||||||
steps:
|
steps:
|
||||||
@@ -877,7 +877,7 @@ jobs:
|
|||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: Release Summary
|
name: Release Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-modules, build-containers, build-cli, build-helm, release-manifest, generate-changelog, generate-suite-docs, generate-compose, create-release, commit-docs]
|
needs: [validate, build-modules, build-containers, build-cli, build-helm, release-manifest, generate-changelog, generate-suite-docs, generate-compose, create-release, commit-docs]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -912,3 +912,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "::error::One or more release jobs failed"
|
echo "::error::One or more release jobs failed"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-release:
|
build-release:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_VERSION: '10.0.100'
|
DOTNET_VERSION: '10.0.100'
|
||||||
REGISTRY: registry.stella-ops.org
|
REGISTRY: registry.stella-ops.org
|
||||||
@@ -249,3 +249,4 @@ jobs:
|
|||||||
name: stellaops-debug-${{ steps.meta.outputs.version }}
|
name: stellaops-debug-${{ steps.meta.outputs.version }}
|
||||||
path: out/release/debug
|
path: out/release/debug
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Replay Verification
|
name: Replay Verification
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
replay-verification:
|
replay-verification:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -37,3 +37,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: replay-diff-report
|
name: replay-diff-report
|
||||||
path: results/diff-report.json
|
path: results/diff-report.json
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
risk-bundle-build:
|
risk-bundle-build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_VERSION: '10.0.100'
|
DOTNET_VERSION: '10.0.100'
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||||
@@ -101,7 +101,7 @@ jobs:
|
|||||||
path: ${{ env.ARTIFACT_DIR }}/*.trx
|
path: ${{ env.ARTIFACT_DIR }}/*.trx
|
||||||
|
|
||||||
risk-bundle-offline-kit:
|
risk-bundle-offline-kit:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: risk-bundle-build
|
needs: risk-bundle-build
|
||||||
env:
|
env:
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
path: ${{ env.OFFLINE_KIT_DIR }}
|
path: ${{ env.OFFLINE_KIT_DIR }}
|
||||||
|
|
||||||
publish-checksums:
|
publish-checksums:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: risk-bundle-build
|
needs: risk-bundle-build
|
||||||
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event.inputs.publish_checksums == 'true')
|
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event.inputs.publish_checksums == 'true')
|
||||||
env:
|
env:
|
||||||
@@ -196,3 +196,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: risk-bundle-published-checksums
|
name: risk-bundle-published-checksums
|
||||||
path: out/checksums/risk-bundle/
|
path: out/checksums/risk-bundle/
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ jobs:
|
|||||||
|
|
||||||
preflight:
|
preflight:
|
||||||
name: Pre-Flight Checks
|
name: Pre-Flight Checks
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
environment: ${{ inputs.environment || 'staging' }}
|
environment: ${{ inputs.environment || 'staging' }}
|
||||||
outputs:
|
outputs:
|
||||||
current-version: ${{ steps.current.outputs.version }}
|
current-version: ${{ steps.current.outputs.version }}
|
||||||
@@ -173,7 +173,7 @@ jobs:
|
|||||||
name: Measure Rollback Lag
|
name: Measure Rollback Lag
|
||||||
needs: preflight
|
needs: preflight
|
||||||
if: needs.preflight.outputs.can-rollback == 'true'
|
if: needs.preflight.outputs.can-rollback == 'true'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
environment: ${{ inputs.environment || 'staging' }}
|
environment: ${{ inputs.environment || 'staging' }}
|
||||||
outputs:
|
outputs:
|
||||||
rollback-time: ${{ steps.timing.outputs.rollback_time }}
|
rollback-time: ${{ steps.timing.outputs.rollback_time }}
|
||||||
@@ -328,7 +328,7 @@ jobs:
|
|||||||
name: Generate Report
|
name: Generate Report
|
||||||
needs: [preflight, measure]
|
needs: [preflight, measure]
|
||||||
if: always() && needs.preflight.result == 'success'
|
if: always() && needs.preflight.result == 'success'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Generate Report
|
- name: Generate Report
|
||||||
run: |
|
run: |
|
||||||
@@ -401,3 +401,4 @@ jobs:
|
|||||||
SLO_SECONDS="${{ inputs.rollback_slo_seconds || 300 }}"
|
SLO_SECONDS="${{ inputs.rollback_slo_seconds || 300 }}"
|
||||||
echo "::error::Rollback took ${TOTAL_LAG}s, exceeds SLO of ${SLO_SECONDS}s"
|
echo "::error::Rollback took ${TOTAL_LAG}s, exceeds SLO of ${SLO_SECONDS}s"
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
load-tests:
|
load-tests:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
|
|
||||||
services:
|
services:
|
||||||
@@ -139,7 +139,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
chaos-unit-tests:
|
chaos-unit-tests:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
needs: load-tests
|
needs: load-tests
|
||||||
if: always()
|
if: always()
|
||||||
@@ -198,7 +198,7 @@ jobs:
|
|||||||
retention-days: 30
|
retention-days: 30
|
||||||
|
|
||||||
valkey-failure-tests:
|
valkey-failure-tests:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
needs: load-tests
|
needs: load-tests
|
||||||
if: ${{ github.event.inputs.run_valkey_tests != 'false' }}
|
if: ${{ github.event.inputs.run_valkey_tests != 'false' }}
|
||||||
@@ -235,7 +235,7 @@ jobs:
|
|||||||
path: results/
|
path: results/
|
||||||
|
|
||||||
analyze-results:
|
analyze-results:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [load-tests, chaos-unit-tests]
|
needs: [load-tests, chaos-unit-tests]
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
@@ -304,3 +304,4 @@ jobs:
|
|||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "### Thresholds" >> $GITHUB_STEP_SUMMARY
|
echo "### Thresholds" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- Status: ${{ steps.analysis.outputs.thresholds_passed == 'true' && 'PASSED' || 'FAILED' }}" >> $GITHUB_STEP_SUMMARY
|
echo "- Status: ${{ steps.analysis.outputs.thresholds_passed == 'true' && 'PASSED' || 'FAILED' }}" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
|
|
||||||
sast-scan:
|
sast-scan:
|
||||||
name: SAST Analysis
|
name: SAST Analysis
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
permissions:
|
permissions:
|
||||||
security-events: write
|
security-events: write
|
||||||
@@ -197,7 +197,7 @@ jobs:
|
|||||||
|
|
||||||
dotnet-security:
|
dotnet-security:
|
||||||
name: .NET Security Analysis
|
name: .NET Security Analysis
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -257,7 +257,7 @@ jobs:
|
|||||||
|
|
||||||
dependency-check:
|
dependency-check:
|
||||||
name: Dependency Vulnerabilities
|
name: Dependency Vulnerabilities
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -308,7 +308,7 @@ jobs:
|
|||||||
|
|
||||||
dockerfile-lint:
|
dockerfile-lint:
|
||||||
name: Dockerfile Security
|
name: Dockerfile Security
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -362,7 +362,7 @@ jobs:
|
|||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: SAST Summary
|
name: SAST Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [sast-scan, dotnet-security, dependency-check, dockerfile-lint]
|
needs: [sast-scan, dotnet-security, dependency-check, dockerfile-lint]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -384,3 +384,4 @@ jobs:
|
|||||||
needs.dotnet-security.result == 'failure' ||
|
needs.dotnet-security.result == 'failure' ||
|
||||||
needs.dependency-check.result == 'failure')
|
needs.dependency-check.result == 'failure')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
|
|
||||||
discover:
|
discover:
|
||||||
name: Discover Changed Modules
|
name: Discover Changed Modules
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
modules: ${{ steps.detect.outputs.modules }}
|
modules: ${{ steps.detect.outputs.modules }}
|
||||||
has-schema-changes: ${{ steps.detect.outputs.has_changes }}
|
has-schema-changes: ${{ steps.detect.outputs.has_changes }}
|
||||||
@@ -126,7 +126,7 @@ jobs:
|
|||||||
name: Test ${{ matrix.module }} (Schema ${{ matrix.schema-version }})
|
name: Test ${{ matrix.module }} (Schema ${{ matrix.schema-version }})
|
||||||
needs: discover
|
needs: discover
|
||||||
if: needs.discover.outputs.has-schema-changes == 'true' || github.event_name == 'workflow_dispatch'
|
if: needs.discover.outputs.has-schema-changes == 'true' || github.event_name == 'workflow_dispatch'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -275,7 +275,7 @@ jobs:
|
|||||||
name: Generate Compatibility Report
|
name: Generate Compatibility Report
|
||||||
needs: [discover, test]
|
needs: [discover, test]
|
||||||
if: always() && needs.discover.outputs.has-schema-changes == 'true'
|
if: always() && needs.discover.outputs.has-schema-changes == 'true'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Download All Results
|
- name: Download All Results
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -337,7 +337,7 @@ jobs:
|
|||||||
name: Post Report to PR
|
name: Post Report to PR
|
||||||
needs: [discover, test, report]
|
needs: [discover, test, report]
|
||||||
if: github.event_name == 'pull_request' && always()
|
if: github.event_name == 'pull_request' && always()
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
@@ -416,3 +416,4 @@ jobs:
|
|||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sdk-publish:
|
sdk-publish:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -89,3 +89,4 @@ jobs:
|
|||||||
.nuget/packages/*.nupkg
|
.nuget/packages/*.nupkg
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
|
|
||||||
validate:
|
validate:
|
||||||
name: Validate Inputs
|
name: Validate Inputs
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
version: ${{ steps.resolve.outputs.version }}
|
version: ${{ steps.resolve.outputs.version }}
|
||||||
sign_bundle: ${{ steps.resolve.outputs.sign_bundle }}
|
sign_bundle: ${{ steps.resolve.outputs.sign_bundle }}
|
||||||
@@ -95,7 +95,7 @@ jobs:
|
|||||||
|
|
||||||
build-bundle:
|
build-bundle:
|
||||||
name: Build Secrets Bundle
|
name: Build Secrets Bundle
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate]
|
needs: [validate]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -201,7 +201,7 @@ jobs:
|
|||||||
|
|
||||||
sign-bundle:
|
sign-bundle:
|
||||||
name: Sign Secrets Bundle
|
name: Sign Secrets Bundle
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-bundle]
|
needs: [validate, build-bundle]
|
||||||
if: needs.validate.outputs.sign_bundle == 'true'
|
if: needs.validate.outputs.sign_bundle == 'true'
|
||||||
steps:
|
steps:
|
||||||
@@ -305,7 +305,7 @@ jobs:
|
|||||||
|
|
||||||
package-offline-kit:
|
package-offline-kit:
|
||||||
name: Package for Offline Kit
|
name: Package for Offline Kit
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-bundle, sign-bundle]
|
needs: [validate, build-bundle, sign-bundle]
|
||||||
if: always() && needs.build-bundle.result == 'success' && needs.validate.outputs.include_in_kit == 'true'
|
if: always() && needs.build-bundle.result == 'success' && needs.validate.outputs.include_in_kit == 'true'
|
||||||
steps:
|
steps:
|
||||||
@@ -357,7 +357,7 @@ jobs:
|
|||||||
|
|
||||||
publish:
|
publish:
|
||||||
name: Publish Bundle
|
name: Publish Bundle
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, sign-bundle, package-offline-kit]
|
needs: [validate, sign-bundle, package-offline-kit]
|
||||||
if: needs.validate.outputs.dry_run != 'true' && needs.sign-bundle.result == 'success'
|
if: needs.validate.outputs.dry_run != 'true' && needs.sign-bundle.result == 'success'
|
||||||
steps:
|
steps:
|
||||||
@@ -478,7 +478,7 @@ jobs:
|
|||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: Build Summary
|
name: Build Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [validate, build-bundle, sign-bundle, package-offline-kit, publish]
|
needs: [validate, build-bundle, sign-bundle, package-offline-kit, publish]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -501,3 +501,4 @@ jobs:
|
|||||||
echo "| Sign Bundle | ${{ needs.sign-bundle.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
|
echo "| Sign Bundle | ${{ needs.sign-bundle.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Package Offline Kit | ${{ needs.package-offline-kit.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
|
echo "| Package Offline Kit | ${{ needs.package-offline-kit.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Publish | ${{ needs.publish.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
|
echo "| Publish | ${{ needs.publish.result || 'skipped' }} |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
signals-ci:
|
signals-ci:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -73,3 +73,4 @@ jobs:
|
|||||||
out/signals
|
out/signals
|
||||||
out/signals/signals-image.tar
|
out/signals/signals-image.tar
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
sign-signals-artifacts:
|
sign-signals-artifacts:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||||
@@ -120,7 +120,7 @@ jobs:
|
|||||||
echo "Artifacts available as workflow artifact for manual ingestion"
|
echo "Artifacts available as workflow artifact for manual ingestion"
|
||||||
|
|
||||||
verify-signatures:
|
verify-signatures:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: sign-signals-artifacts
|
needs: sign-signals-artifacts
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -181,3 +181,4 @@ jobs:
|
|||||||
done
|
done
|
||||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||||
echo "Run ID: ${{ github.run_number }}" >> "$GITHUB_STEP_SUMMARY"
|
echo "Run ID: ${{ github.run_number }}" >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
reachability-smoke:
|
reachability-smoke:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
DOTNET_NOLOGO: 1
|
DOTNET_NOLOGO: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
scripts/signals/reachability-smoke.sh
|
scripts/signals/reachability-smoke.sh
|
||||||
|
|
||||||
sign-and-upload:
|
sign-and-upload:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: reachability-smoke
|
needs: reachability-smoke
|
||||||
env:
|
env:
|
||||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||||
@@ -125,3 +125,4 @@ jobs:
|
|||||||
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN == '' || env.EVIDENCE_LOCKER_URL == '' }}
|
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN == '' || env.EVIDENCE_LOCKER_URL == '' }}
|
||||||
run: |
|
run: |
|
||||||
echo "::notice::Evidence Locker upload skipped (CI_EVIDENCE_LOCKER_TOKEN or EVIDENCE_LOCKER_URL not set)"
|
echo "::notice::Evidence Locker upload skipped (CI_EVIDENCE_LOCKER_TOKEN or EVIDENCE_LOCKER_URL not set)"
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
symbols-smoke:
|
symbols-smoke:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/artifacts/symbols-ci
|
ARTIFACT_DIR: ${{ github.workspace }}/artifacts/symbols-ci
|
||||||
steps:
|
steps:
|
||||||
@@ -45,3 +45,4 @@ jobs:
|
|||||||
name: symbols-ci
|
name: symbols-ci
|
||||||
path: ${{ env.ARTIFACT_DIR }}
|
path: ${{ env.ARTIFACT_DIR }}
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
symbols-release-smoke:
|
symbols-release-smoke:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
env:
|
env:
|
||||||
ARTIFACT_DIR: ${{ github.workspace }}/artifacts/symbols-release
|
ARTIFACT_DIR: ${{ github.workspace }}/artifacts/symbols-release
|
||||||
steps:
|
steps:
|
||||||
@@ -39,3 +39,4 @@ jobs:
|
|||||||
name: symbols-release
|
name: symbols-release
|
||||||
path: ${{ env.ARTIFACT_DIR }}
|
path: ${{ env.ARTIFACT_DIR }}
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ jobs:
|
|||||||
|
|
||||||
validate:
|
validate:
|
||||||
name: Validate Annotations
|
name: Validate Annotations
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
has-violations: ${{ steps.validate.outputs.has_violations }}
|
has-violations: ${{ steps.validate.outputs.has_violations }}
|
||||||
violation-count: ${{ steps.validate.outputs.violation_count }}
|
violation-count: ${{ steps.validate.outputs.violation_count }}
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
name: Post Report
|
name: Post Report
|
||||||
needs: validate
|
needs: validate
|
||||||
if: github.event_name == 'pull_request' && needs.validate.outputs.has-violations == 'true'
|
if: github.event_name == 'pull_request' && needs.validate.outputs.has-violations == 'true'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
@@ -253,3 +253,4 @@ jobs:
|
|||||||
issue_number: context.issue.number,
|
issue_number: context.issue.number,
|
||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
|
|
||||||
detect-changes:
|
detect-changes:
|
||||||
name: Detect Changes
|
name: Detect Changes
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
has-test-changes: ${{ steps.changes.outputs.tests }}
|
has-test-changes: ${{ steps.changes.outputs.tests }}
|
||||||
has-schema-changes: ${{ steps.changes.outputs.schema }}
|
has-schema-changes: ${{ steps.changes.outputs.schema }}
|
||||||
@@ -109,7 +109,7 @@ jobs:
|
|||||||
name: Blast-Radius Validation
|
name: Blast-Radius Validation
|
||||||
needs: detect-changes
|
needs: detect-changes
|
||||||
if: needs.detect-changes.outputs.has-test-changes == 'true' || inputs.run_all == true || github.event_name == 'schedule'
|
if: needs.detect-changes.outputs.has-test-changes == 'true' || inputs.run_all == true || github.event_name == 'schedule'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
status: ${{ steps.validate.outputs.status }}
|
status: ${{ steps.validate.outputs.status }}
|
||||||
violations: ${{ steps.validate.outputs.violation_count }}
|
violations: ${{ steps.validate.outputs.violation_count }}
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
name: Dead-Path Detection
|
name: Dead-Path Detection
|
||||||
needs: detect-changes
|
needs: detect-changes
|
||||||
if: needs.detect-changes.outputs.has-code-changes == 'true' || inputs.run_all == true || github.event_name == 'schedule'
|
if: needs.detect-changes.outputs.has-code-changes == 'true' || inputs.run_all == true || github.event_name == 'schedule'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
status: ${{ steps.detect.outputs.status }}
|
status: ${{ steps.detect.outputs.status }}
|
||||||
new-paths: ${{ steps.detect.outputs.new_paths }}
|
new-paths: ${{ steps.detect.outputs.new_paths }}
|
||||||
@@ -213,7 +213,7 @@ jobs:
|
|||||||
name: Schema Evolution Check
|
name: Schema Evolution Check
|
||||||
needs: detect-changes
|
needs: detect-changes
|
||||||
if: needs.detect-changes.outputs.has-schema-changes == 'true' || inputs.run_all == true
|
if: needs.detect-changes.outputs.has-schema-changes == 'true' || inputs.run_all == true
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:16-alpine
|
image: postgres:16-alpine
|
||||||
@@ -274,7 +274,7 @@ jobs:
|
|||||||
name: Config-Diff Check
|
name: Config-Diff Check
|
||||||
needs: detect-changes
|
needs: detect-changes
|
||||||
if: needs.detect-changes.outputs.has-config-changes == 'true' || inputs.run_all == true
|
if: needs.detect-changes.outputs.has-config-changes == 'true' || inputs.run_all == true
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
status: ${{ steps.test.outputs.status }}
|
status: ${{ steps.test.outputs.status }}
|
||||||
tested-configs: ${{ steps.test.outputs.tested }}
|
tested-configs: ${{ steps.test.outputs.tested }}
|
||||||
@@ -319,7 +319,7 @@ jobs:
|
|||||||
name: Generate Report
|
name: Generate Report
|
||||||
needs: [detect-changes, blast-radius, dead-paths, schema-evolution, config-diff]
|
needs: [detect-changes, blast-radius, dead-paths, schema-evolution, config-diff]
|
||||||
if: always()
|
if: always()
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Generate Infrastructure Report
|
- name: Generate Infrastructure Report
|
||||||
run: |
|
run: |
|
||||||
@@ -417,7 +417,7 @@ jobs:
|
|||||||
name: Post PR Comment
|
name: Post PR Comment
|
||||||
needs: [report, blast-radius, dead-paths, schema-evolution, config-diff]
|
needs: [report, blast-radius, dead-paths, schema-evolution, config-diff]
|
||||||
if: github.event_name == 'pull_request' && always()
|
if: github.event_name == 'pull_request' && always()
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
@@ -504,3 +504,4 @@ jobs:
|
|||||||
body: body
|
body: body
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
unit-tests:
|
unit-tests:
|
||||||
name: Unit Tests
|
name: Unit Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -79,7 +79,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
architecture-tests:
|
architecture-tests:
|
||||||
name: Architecture Tests
|
name: Architecture Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -121,7 +121,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
contract-tests:
|
contract-tests:
|
||||||
name: Contract Tests
|
name: Contract Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 10
|
timeout-minutes: 10
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -162,7 +162,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
integration-tests:
|
integration-tests:
|
||||||
name: Integration Tests
|
name: Integration Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
security-tests:
|
security-tests:
|
||||||
name: Security Tests
|
name: Security Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -246,7 +246,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
performance-tests:
|
performance-tests:
|
||||||
name: Performance Tests
|
name: Performance Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.run_performance == 'true')
|
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.run_performance == 'true')
|
||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
steps:
|
steps:
|
||||||
@@ -288,7 +288,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
live-tests:
|
live-tests:
|
||||||
name: Live Tests (External Dependencies)
|
name: Live Tests (External Dependencies)
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
if: github.event_name == 'workflow_dispatch' && github.event.inputs.run_live == 'true'
|
if: github.event_name == 'workflow_dispatch' && github.event.inputs.run_live == 'true'
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
steps:
|
steps:
|
||||||
@@ -331,7 +331,7 @@ jobs:
|
|||||||
# ===========================================================================
|
# ===========================================================================
|
||||||
test-summary:
|
test-summary:
|
||||||
name: Test Results Summary
|
name: Test Results Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [unit-tests, architecture-tests, contract-tests, integration-tests, security-tests]
|
needs: [unit-tests, architecture-tests, contract-tests, integration-tests, security-tests]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -356,3 +356,4 @@ jobs:
|
|||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "See individual job logs for detailed test output." >> $GITHUB_STEP_SUMMARY
|
echo "See individual job logs for detailed test output." >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
|
|
||||||
discover:
|
discover:
|
||||||
name: Discover Tests
|
name: Discover Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
outputs:
|
outputs:
|
||||||
test-projects: ${{ steps.find.outputs.projects }}
|
test-projects: ${{ steps.find.outputs.projects }}
|
||||||
test-count: ${{ steps.find.outputs.count }}
|
test-count: ${{ steps.find.outputs.count }}
|
||||||
@@ -120,7 +120,7 @@ jobs:
|
|||||||
|
|
||||||
pr-gating-tests:
|
pr-gating-tests:
|
||||||
name: ${{ matrix.category }} Tests
|
name: ${{ matrix.category }} Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: ${{ matrix.timeout }}
|
timeout-minutes: ${{ matrix.timeout }}
|
||||||
needs: discover
|
needs: discover
|
||||||
strategy:
|
strategy:
|
||||||
@@ -178,7 +178,7 @@ jobs:
|
|||||||
|
|
||||||
integration:
|
integration:
|
||||||
name: Integration Tests
|
name: Integration Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
needs: discover
|
needs: discover
|
||||||
services:
|
services:
|
||||||
@@ -229,7 +229,7 @@ jobs:
|
|||||||
|
|
||||||
extended-tests:
|
extended-tests:
|
||||||
name: ${{ matrix.category }} Tests
|
name: ${{ matrix.category }} Tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
timeout-minutes: ${{ matrix.timeout }}
|
timeout-minutes: ${{ matrix.timeout }}
|
||||||
needs: discover
|
needs: discover
|
||||||
if: >-
|
if: >-
|
||||||
@@ -321,7 +321,7 @@ jobs:
|
|||||||
|
|
||||||
summary:
|
summary:
|
||||||
name: Test Summary
|
name: Test Summary
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: [discover, pr-gating-tests, integration]
|
needs: [discover, pr-gating-tests, integration]
|
||||||
if: always()
|
if: always()
|
||||||
steps:
|
steps:
|
||||||
@@ -372,3 +372,4 @@ jobs:
|
|||||||
- name: Check for failures
|
- name: Check for failures
|
||||||
if: contains(needs.*.result, 'failure')
|
if: contains(needs.*.result, 'failure')
|
||||||
run: exit 1
|
run: exit 1
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ env:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
scan-and-check-budget:
|
scan-and-check-budget:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
pull-requests: write
|
pull-requests: write
|
||||||
@@ -197,3 +197,4 @@ jobs:
|
|||||||
if: steps.env.outputs.environment != 'prod' && steps.budget.outputs.exit_code == '2'
|
if: steps.env.outputs.environment != 'prod' && steps.budget.outputs.exit_code == '2'
|
||||||
run: |
|
run: |
|
||||||
echo "::warning::Unknowns budget exceeded for ${{ steps.env.outputs.environment }}"
|
echo "::warning::Unknowns budget exceeded for ${{ steps.env.outputs.environment }}"
|
||||||
|
|
||||||
|
|||||||
@@ -151,21 +151,9 @@ CREATE INDEX idx_hlc_state_updated ON scheduler.hlc_state(updated_at DESC);
|
|||||||
| 7 | HLC-007 | DONE | HLC-003 | Guild | Add `HlcTimestampTypeHandler` for Npgsql/Dapper |
|
| 7 | HLC-007 | DONE | HLC-003 | Guild | Add `HlcTimestampTypeHandler` for Npgsql/Dapper |
|
||||||
| 8 | HLC-008 | DONE | HLC-005 | Guild | Write unit tests: tick monotonicity, receive merge, clock skew handling |
|
| 8 | HLC-008 | DONE | HLC-005 | Guild | Write unit tests: tick monotonicity, receive merge, clock skew handling |
|
||||||
| 9 | HLC-009 | DONE | HLC-008 | Guild | Write integration tests: concurrent ticks, node restart recovery |
|
| 9 | HLC-009 | DONE | HLC-008 | Guild | Write integration tests: concurrent ticks, node restart recovery |
|
||||||
<<<<<<< HEAD
|
|
||||||
| 10 | HLC-010 | DONE | HLC-009 | Guild | Write benchmarks: tick throughput, memory allocation |
|
| 10 | HLC-010 | DONE | HLC-009 | Guild | Write benchmarks: tick throughput, memory allocation |
|
||||||
| 11 | HLC-011 | DONE | HLC-010 | Guild | Create `HlcServiceCollectionExtensions` for DI registration |
|
| 11 | HLC-011 | DONE | HLC-010 | Guild | Create `HlcServiceCollectionExtensions` for DI registration |
|
||||||
| 12 | HLC-012 | DONE | HLC-011 | Guild | Documentation: README.md, API docs, usage examples |
|
| 12 | HLC-012 | DONE | HLC-011 | Guild | Documentation: README.md, API docs, usage examples |
|
||||||
=======
|
|
||||||
<<<<<<<< HEAD:docs/implplan/SPRINT_20260105_002_001_LB_hlc_core_library.md
|
|
||||||
| 10 | HLC-010 | TODO | HLC-009 | Guild | Write benchmarks: tick throughput, memory allocation |
|
|
||||||
| 11 | HLC-011 | DONE | HLC-010 | Guild | Create `HlcServiceCollectionExtensions` for DI registration |
|
|
||||||
| 12 | HLC-012 | TODO | HLC-011 | Guild | Documentation: README.md, API docs, usage examples |
|
|
||||||
========
|
|
||||||
| 10 | HLC-010 | DONE | HLC-009 | Guild | Write benchmarks: tick throughput, memory allocation |
|
|
||||||
| 11 | HLC-011 | DONE | HLC-010 | Guild | Create `HlcServiceCollectionExtensions` for DI registration |
|
|
||||||
| 12 | HLC-012 | DONE | HLC-011 | Guild | Documentation: README.md, API docs, usage examples |
|
|
||||||
>>>>>>>> 47890273170663b2236a1eb995d218fe5de6b11a:docs-archived/implplan/SPRINT_20260105_002_001_LB_hlc_core_library.md
|
|
||||||
>>>>>>> 47890273170663b2236a1eb995d218fe5de6b11a
|
|
||||||
|
|
||||||
## Implementation Details
|
## Implementation Details
|
||||||
|
|
||||||
@@ -347,23 +335,12 @@ hlc_physical_time_offset_seconds{node_id} // Drift from wall clock
|
|||||||
| Date (UTC) | Update | Owner |
|
| Date (UTC) | Update | Owner |
|
||||||
|------------|--------|-------|
|
|------------|--------|-------|
|
||||||
| 2026-01-05 | Sprint created from product advisory gap analysis | Planning |
|
| 2026-01-05 | Sprint created from product advisory gap analysis | Planning |
|
||||||
<<<<<<< HEAD
|
|
||||||
| 2026-01-05 | HLC-001 to HLC-011 implemented: core library, state stores, JSON/Dapper serializers, DI extensions, 56 unit tests all passing | Agent |
|
|
||||||
| 2026-01-06 | HLC-010: Created StellaOps.HybridLogicalClock.Benchmarks project with tick throughput, memory allocation, and concurrency benchmarks | Agent |
|
|
||||||
| 2026-01-06 | HLC-012: Created comprehensive README.md with API reference, usage examples, configuration guide, and algorithm documentation | Agent |
|
|
||||||
| 2026-01-06 | Sprint COMPLETE: All 12 tasks done, 56 tests passing, benchmarks verified | Agent |
|
|
||||||
=======
|
|
||||||
<<<<<<<< HEAD:docs/implplan/SPRINT_20260105_002_001_LB_hlc_core_library.md
|
|
||||||
| 2026-01-05 | HLC-001 to HLC-011 implemented: core library, state stores, JSON/Dapper serializers, DI extensions, 56 unit tests all passing | Agent |
|
|
||||||
========
|
|
||||||
| 2026-01-06 | HLC-001 to HLC-006 and HLC-011 DONE: Created StellaOps.HybridLogicalClock project with HlcTimestamp record (comparison, parsing, serialization), HybridLogicalClock class (Tick/Receive/Current), IHybridLogicalClock and IHlcStateStore interfaces, InMemoryHlcStateStore, PostgresHlcStateStore (atomic upsert with conditional update for monotonicity), HlcClockSkewException, HlcTimestampJsonConverter (string and object format), NullableHlcTimestampJsonConverter, and HlcServiceCollectionExtensions. All builds verified. | Agent |
|
| 2026-01-06 | HLC-001 to HLC-006 and HLC-011 DONE: Created StellaOps.HybridLogicalClock project with HlcTimestamp record (comparison, parsing, serialization), HybridLogicalClock class (Tick/Receive/Current), IHybridLogicalClock and IHlcStateStore interfaces, InMemoryHlcStateStore, PostgresHlcStateStore (atomic upsert with conditional update for monotonicity), HlcClockSkewException, HlcTimestampJsonConverter (string and object format), NullableHlcTimestampJsonConverter, and HlcServiceCollectionExtensions. All builds verified. | Agent |
|
||||||
| 2026-01-06 | HLC-007 DONE: Created HlcTimestampTypeHandler.cs with HlcTimestampNpgsqlExtensions (AddHlcTimestamp, GetHlcTimestamp, GetHlcTimestampOrNull methods for NpgsqlCommand and NpgsqlDataReader), HlcTimestampDapperHandler, NullableHlcTimestampDapperHandler, and HlcTypeHandlerRegistration for DI. Added Dapper package reference. Build verified. | Agent |
|
| 2026-01-06 | HLC-007 DONE: Created HlcTimestampTypeHandler.cs with HlcTimestampNpgsqlExtensions (AddHlcTimestamp, GetHlcTimestamp, GetHlcTimestampOrNull methods for NpgsqlCommand and NpgsqlDataReader), HlcTimestampDapperHandler, NullableHlcTimestampDapperHandler, and HlcTypeHandlerRegistration for DI. Added Dapper package reference. Build verified. | Agent |
|
||||||
| 2026-01-06 | HLC-008 DONE: Created StellaOps.HybridLogicalClock.Tests project with comprehensive unit tests: HlcTimestampTests (20+ tests for parsing, comparison, operators, lexicographic ordering), HybridLogicalClockTests (25+ tests for tick monotonicity, receive merge, clock skew handling, state initialization/persistence, causal ordering), InMemoryHlcStateStoreTests (15+ tests for load/save/monotonicity), HlcTimestampJsonConverterTests (25+ tests for string and object JSON converters). Build verified. | Agent |
|
| 2026-01-06 | HLC-008 DONE: Created StellaOps.HybridLogicalClock.Tests project with comprehensive unit tests: HlcTimestampTests (20+ tests for parsing, comparison, operators, lexicographic ordering), HybridLogicalClockTests (25+ tests for tick monotonicity, receive merge, clock skew handling, state initialization/persistence, causal ordering), InMemoryHlcStateStoreTests (15+ tests for load/save/monotonicity), HlcTimestampJsonConverterTests (25+ tests for string and object JSON converters). Build verified. | Agent |
|
||||||
| 2026-01-06 | HLC-009 DONE: Added HybridLogicalClockIntegrationTests with 10+ integration tests covering: concurrent ticks (all unique, within-thread monotonicity), node restart recovery (resume from persisted, same physical time counter increment), multi-node causal ordering (request-response, broadcast-gather, clock skew detection, concurrent events total ordering), state store concurrency (no loss, maintains monotonicity). Build verified. | Agent |
|
| 2026-01-06 | HLC-009 DONE: Added HybridLogicalClockIntegrationTests with 10+ integration tests covering: concurrent ticks (all unique, within-thread monotonicity), node restart recovery (resume from persisted, same physical time counter increment), multi-node causal ordering (request-response, broadcast-gather, clock skew detection, concurrent events total ordering), state store concurrency (no loss, maintains monotonicity). Build verified. | Agent |
|
||||||
| 2026-01-06 | HLC-010 DONE: Created HybridLogicalClockBenchmarks.cs with 12+ performance benchmarks: tick throughput (single-thread 100K/sec target, multi-thread 50K/sec, with time advance), receive throughput (50K/sec), parse/serialize throughput (500K/sec), comparison throughput (10M/sec), memory allocation tests (value type verification, reasonable struct size), InMemoryStateStore throughput (save 100K/sec, load 500K/sec). Uses xUnit Facts with TestCategories.Performance trait. Build verified. | Agent |
|
| 2026-01-06 | HLC-010 DONE: Created HybridLogicalClockBenchmarks.cs with 12+ performance benchmarks: tick throughput (single-thread 100K/sec target, multi-thread 50K/sec, with time advance), receive throughput (50K/sec), parse/serialize throughput (500K/sec), comparison throughput (10M/sec), memory allocation tests (value type verification, reasonable struct size), InMemoryStateStore throughput (save 100K/sec, load 500K/sec). Uses xUnit Facts with TestCategories.Performance trait. Build verified. | Agent |
|
||||||
| 2026-01-06 | HLC-012 DONE: Created comprehensive README.md with: overview and problem statement, installation and quick start, DI registration (3 patterns), core types reference (HlcTimestamp, IHybridLogicalClock, IHlcStateStore), PostgreSQL persistence schema, JSON serialization (string and object formats), Npgsql/Dapper type handlers, clock skew handling, recovery from restart, testing patterns, HLC algorithm pseudocode, performance benchmarks table, and academic references. Sprint complete. | Agent |
|
| 2026-01-06 | HLC-012 DONE: Created comprehensive README.md with: overview and problem statement, installation and quick start, DI registration (3 patterns), core types reference (HlcTimestamp, IHybridLogicalClock, IHlcStateStore), PostgreSQL persistence schema, JSON serialization (string and object formats), Npgsql/Dapper type handlers, clock skew handling, recovery from restart, testing patterns, HLC algorithm pseudocode, performance benchmarks table, and academic references. Sprint complete. | Agent |
|
||||||
>>>>>>>> 47890273170663b2236a1eb995d218fe5de6b11a:docs-archived/implplan/SPRINT_20260105_002_001_LB_hlc_core_library.md
|
|
||||||
>>>>>>> 47890273170663b2236a1eb995d218fe5de6b11a
|
|
||||||
|
|
||||||
## Next Checkpoints
|
## Next Checkpoints
|
||||||
|
|
||||||
|
|||||||
@@ -419,22 +419,6 @@ public sealed class SchedulerOptions
|
|||||||
| Date (UTC) | Update | Owner |
|
| Date (UTC) | Update | Owner |
|
||||||
|------------|--------|-------|
|
|------------|--------|-------|
|
||||||
| 2026-01-05 | Sprint created from product advisory gap analysis | Planning |
|
| 2026-01-05 | Sprint created from product advisory gap analysis | Planning |
|
||||||
<<<<<<< HEAD:docs/implplan/SPRINT_20260105_002_002_SCHEDULER_hlc_queue_chain.md
|
|
||||||
| 2026-01-06 | SQC-001: Added HLC and CanonicalJson references to Scheduler.Persistence and Scheduler.Queue projects | Agent |
|
|
||||||
| 2026-01-06 | SQC-002-004: Created migration 002_hlc_queue_chain.sql with scheduler_log, batch_snapshot, chain_heads tables | Agent |
|
|
||||||
| 2026-01-06 | SQC-005-008: Implemented SchedulerChainLinking, ISchedulerLogRepository, PostgresSchedulerLogRepository, IChainHeadRepository, PostgresChainHeadRepository | Agent |
|
|
||||||
| 2026-01-06 | SQC-009: Implemented HlcSchedulerEnqueueService with chain linking and idempotency | Agent |
|
|
||||||
| 2026-01-06 | SQC-010: Implemented HlcSchedulerDequeueService with HLC-ordered retrieval and cursor pagination | Agent |
|
|
||||||
| 2026-01-06 | SQC-013: Implemented BatchSnapshotService with audit anchoring and optional DSSE signing | Agent |
|
|
||||||
| 2026-01-06 | SQC-015: Implemented SchedulerChainVerifier for chain integrity verification | Agent |
|
|
||||||
| 2026-01-06 | SQC-020: Added SchedulerHlcOptions with EnableHlcOrdering, DualWriteMode, VerifyOnDequeue flags | Agent |
|
|
||||||
| 2026-01-06 | SQC-022: Implemented HlcSchedulerMetrics with enqueue, dequeue, verification, and snapshot metrics | Agent |
|
|
||||||
| 2026-01-06 | Added HlcSchedulerServiceCollectionExtensions for DI registration | Agent |
|
|
||||||
| 2026-01-06 | SQC-011-012: Verified Redis and NATS adapters already have HLC support (IHybridLogicalClock injection, Tick(), header storage) | Agent |
|
|
||||||
| 2026-01-06 | SQC-021: Created HLC migration guide at docs/modules/scheduler/hlc-migration-guide.md | Agent |
|
|
||||||
| 2026-01-06 | SQC-014: Implemented BatchSnapshotDsseSigner with HMAC-SHA256 signing, PAE encoding, and verification | Agent |
|
|
||||||
| 2026-01-06 | SQC-019: Updated JobRepository with optional HLC ordering via JobRepositoryOptions; GetScheduledJobsAsync and GetByStatusAsync now join with scheduler_log when enabled | Agent |
|
|
||||||
=======
|
|
||||||
| 2026-01-06 | SQC-001 DONE: Added HybridLogicalClock project reference to StellaOps.Scheduler.Persistence and StellaOps.Scheduler.Queue. Build verified. | Agent |
|
| 2026-01-06 | SQC-001 DONE: Added HybridLogicalClock project reference to StellaOps.Scheduler.Persistence and StellaOps.Scheduler.Queue. Build verified. | Agent |
|
||||||
| 2026-01-06 | SQC-002-004 DONE: Created 002_hlc_queue_chain.sql migration with: scheduler_log (HLC-ordered queue with chain linking), batch_snapshot (audit anchors with optional DSSE), chain_heads (per-partition head tracking), and upsert_chain_head function for atomic monotonic updates. | Agent |
|
| 2026-01-06 | SQC-002-004 DONE: Created 002_hlc_queue_chain.sql migration with: scheduler_log (HLC-ordered queue with chain linking), batch_snapshot (audit anchors with optional DSSE), chain_heads (per-partition head tracking), and upsert_chain_head function for atomic monotonic updates. | Agent |
|
||||||
| 2026-01-06 | SQC-005-007 DONE: Created entity models (SchedulerLogEntity, BatchSnapshotEntity, ChainHeadEntity), ISchedulerLogRepository interface (insert, HLC-ordered query, range query, job/link lookup), SchedulerLogRepository (transactional insert with chain head update), IChainHeadRepository (get, upsert with monotonicity), ChainHeadRepository. Build verified. | Agent |
|
| 2026-01-06 | SQC-005-007 DONE: Created entity models (SchedulerLogEntity, BatchSnapshotEntity, ChainHeadEntity), ISchedulerLogRepository interface (insert, HLC-ordered query, range query, job/link lookup), SchedulerLogRepository (transactional insert with chain head update), IChainHeadRepository (get, upsert with monotonicity), ChainHeadRepository. Build verified. | Agent |
|
||||||
@@ -457,7 +441,6 @@ public sealed class SchedulerOptions
|
|||||||
| 2026-01-06 | SQC-014 DONE: Created ISchedulerSnapshotSigner interface with SignAsync() method and SnapshotSignResult record. Updated BatchSnapshotService to optionally sign snapshots when SignBatchSnapshots=true and signer is available. Added ComputeSnapshotDigest() for deterministic SHA-256 digest. Build verified. | Agent |
|
| 2026-01-06 | SQC-014 DONE: Created ISchedulerSnapshotSigner interface with SignAsync() method and SnapshotSignResult record. Updated BatchSnapshotService to optionally sign snapshots when SignBatchSnapshots=true and signer is available. Added ComputeSnapshotDigest() for deterministic SHA-256 digest. Build verified. | Agent |
|
||||||
| 2026-01-06 | SQC-019 DONE: Created HlcJobRepositoryDecorator implementing decorator pattern for IJobRepository. Supports dual-write mode (writes to both scheduler.jobs AND scheduler.scheduler_log) and HLC ordering for dequeue. Uses ISchedulerLogRepository.InsertWithChainUpdateAsync for atomic chain updates. Build verified. | Agent |
|
| 2026-01-06 | SQC-019 DONE: Created HlcJobRepositoryDecorator implementing decorator pattern for IJobRepository. Supports dual-write mode (writes to both scheduler.jobs AND scheduler.scheduler_log) and HLC ordering for dequeue. Uses ISchedulerLogRepository.InsertWithChainUpdateAsync for atomic chain updates. Build verified. | Agent |
|
||||||
| 2026-01-06 | SQC-017 DONE: Created HlcSchedulerPostgresFixture.cs (PostgreSQL test fixture with Testcontainers, scheduler schema migrations, table truncation) and HlcSchedulerIntegrationTests.cs with 13 integration tests: EnqueueAsync_SingleJob_CreatesLogEntryWithChainLink, EnqueueAsync_MultipleJobs_FormsChain, EnqueueAsync_UpdatesChainHead, DequeueAsync_ReturnsJobsInHlcOrder, DequeueAsync_EmptyQueue_ReturnsEmptyList, DequeueAsync_RespectsLimit, VerifyAsync_ValidChain_ReturnsTrue, VerifyAsync_EmptyChain_ReturnsTrue, GetByHlcRangeAsync_ReturnsJobsInRange, Enqueue_DifferentTenants_MaintainsSeparateChains, EnqueueAsync_DuplicateIdempotencyKey_ReturnsExistingJob, VerifySingleAsync_ValidEntry_ReturnsTrue, VerifySingleAsync_NonExistentJob_ReturnsFalse. Properly aligned API with SchedulerJobPayload, SchedulerEnqueueResult, SchedulerDequeueResult, ChainVerificationResult, and correct service constructors. Build verified. | Agent |
|
| 2026-01-06 | SQC-017 DONE: Created HlcSchedulerPostgresFixture.cs (PostgreSQL test fixture with Testcontainers, scheduler schema migrations, table truncation) and HlcSchedulerIntegrationTests.cs with 13 integration tests: EnqueueAsync_SingleJob_CreatesLogEntryWithChainLink, EnqueueAsync_MultipleJobs_FormsChain, EnqueueAsync_UpdatesChainHead, DequeueAsync_ReturnsJobsInHlcOrder, DequeueAsync_EmptyQueue_ReturnsEmptyList, DequeueAsync_RespectsLimit, VerifyAsync_ValidChain_ReturnsTrue, VerifyAsync_EmptyChain_ReturnsTrue, GetByHlcRangeAsync_ReturnsJobsInRange, Enqueue_DifferentTenants_MaintainsSeparateChains, EnqueueAsync_DuplicateIdempotencyKey_ReturnsExistingJob, VerifySingleAsync_ValidEntry_ReturnsTrue, VerifySingleAsync_NonExistentJob_ReturnsFalse. Properly aligned API with SchedulerJobPayload, SchedulerEnqueueResult, SchedulerDequeueResult, ChainVerificationResult, and correct service constructors. Build verified. | Agent |
|
||||||
>>>>>>> 47890273170663b2236a1eb995d218fe5de6b11a:docs-archived/implplan/SPRINT_20260105_002_002_SCHEDULER_hlc_queue_chain.md
|
|
||||||
|
|
||||||
## Next Checkpoints
|
## Next Checkpoints
|
||||||
|
|
||||||
|
|||||||
@@ -48,15 +48,16 @@
|
|||||||
| 14 | CICD-VAL-021 | TODO | CICD-VAL-020 | DevOps | Run test-matrix.yml with act (PR trigger). |
|
| 14 | CICD-VAL-021 | TODO | CICD-VAL-020 | DevOps | Run test-matrix.yml with act (PR trigger). |
|
||||||
| 15 | CICD-VAL-022 | TODO | CICD-VAL-021 | DevOps | Run build-test-deploy.yml with act (PR trigger). |
|
| 15 | CICD-VAL-022 | TODO | CICD-VAL-021 | DevOps | Run build-test-deploy.yml with act (PR trigger). |
|
||||||
| 16 | CICD-VAL-023 | TODO | CICD-VAL-022 | DevOps | Document act simulation results and gaps. |
|
| 16 | CICD-VAL-023 | TODO | CICD-VAL-022 | DevOps | Document act simulation results and gaps. |
|
||||||
|
| 17 | CICD-VAL-024 | DONE | Runner labels available | DevOps | Align workflow runner labels with available Gitea runner pool (configurable Linux label). |
|
||||||
|
|
||||||
### Phase 4: Test Failure Remediation
|
### Phase 4: Test Failure Remediation
|
||||||
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
|
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| 17 | CICD-VAL-030 | TODO | CICD-VAL-016 | DevOps | Fix test categorization (move integration tests from Unit). |
|
| 18 | CICD-VAL-030 | TODO | CICD-VAL-016 | DevOps | Fix test categorization (move integration tests from Unit). |
|
||||||
| 18 | CICD-VAL-031 | TODO | CICD-VAL-030 | DevOps | Fix PostgreSQL connection/fixture issues in tests. |
|
| 19 | CICD-VAL-031 | TODO | CICD-VAL-030 | DevOps | Fix PostgreSQL connection/fixture issues in tests. |
|
||||||
| 19 | CICD-VAL-032 | TODO | CICD-VAL-031 | DevOps | Fix golden fixture mismatches. |
|
| 20 | CICD-VAL-032 | TODO | CICD-VAL-031 | DevOps | Fix golden fixture mismatches. |
|
||||||
| 20 | CICD-VAL-033 | TODO | CICD-VAL-032 | DevOps | Fix remaining test failures (actual bugs). |
|
| 21 | CICD-VAL-033 | TODO | CICD-VAL-032 | DevOps | Fix remaining test failures (actual bugs). |
|
||||||
| 21 | CICD-VAL-034 | TODO | CICD-VAL-033 | DevOps | Re-run full test suite to verify all green. |
|
| 22 | CICD-VAL-034 | TODO | CICD-VAL-033 | DevOps | Re-run full test suite to verify all green. |
|
||||||
|
|
||||||
## Execution Log
|
## Execution Log
|
||||||
| Date (UTC) | Update | Owner |
|
| Date (UTC) | Update | Owner |
|
||||||
@@ -72,6 +73,10 @@
|
|||||||
| 2026-01-06 | BLOCKED: Act workflow simulation not yet run. Requires CI image build and further investigation of test categorization issues. | DevOps |
|
| 2026-01-06 | BLOCKED: Act workflow simulation not yet run. Requires CI image build and further investigation of test categorization issues. | DevOps |
|
||||||
| 2026-01-06 | SCOPE EXPANDED: Sprint amended to include Phase 2 (local test execution), Phase 3 (act simulation), Phase 4 (test remediation). | Planning |
|
| 2026-01-06 | SCOPE EXPANDED: Sprint amended to include Phase 2 (local test execution), Phase 3 (act simulation), Phase 4 (test remediation). | Planning |
|
||||||
| 2026-01-06 | CICD-VAL-010 analysis: 137 passed, 85 failed, 10 aborted. Root cause: 133 PostgreSQL connection exhaustion errors. Many tests tagged "Unit" require DB. | DevOps |
|
| 2026-01-06 | CICD-VAL-010 analysis: 137 passed, 85 failed, 10 aborted. Root cause: 133 PostgreSQL connection exhaustion errors. Many tests tagged "Unit" require DB. | DevOps |
|
||||||
|
| 2026-01-07 | Updated workflows to use a configurable Linux runner label defaulting to ubuntu-latest to avoid runner label mismatches. | DevOps |
|
||||||
|
| 2026-01-07 | Act dry-run for test-matrix (pr-gating-tests job only) progresses through discover and matrix setup; integration job still pending due to act service container handling. | DevOps |
|
||||||
|
| 2026-01-07 | Local smoke build step exceeded 10 minutes and was stopped; unit-split 1-5 failed in AdvisoryAI due to stale build outputs, re-run `dotnet test` for AdvisoryAI passed (207 tests). | DevOps |
|
||||||
|
| 2026-01-07 | Unit-split runs (projects 1-20) completed after AdvisoryAI rebuild; all 20 projects passed. | DevOps |
|
||||||
|
|
||||||
## Decisions & Risks
|
## Decisions & Risks
|
||||||
- Risk: local CI steps drift from pipeline definitions; mitigate with scheduled doc sync.
|
- Risk: local CI steps drift from pipeline definitions; mitigate with scheduled doc sync.
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ Quality gates are integrated into the main CI workflow:
|
|||||||
# .gitea/workflows/build-test-deploy.yml
|
# .gitea/workflows/build-test-deploy.yml
|
||||||
|
|
||||||
quality-gates:
|
quality-gates:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ${{ vars.LINUX_RUNNER_LABEL || 'ubuntu-latest' }}
|
||||||
needs: build-test
|
needs: build-test
|
||||||
steps:
|
steps:
|
||||||
- name: Reachability quality gate
|
- name: Reachability quality gate
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public sealed class AttestorVerificationEngine : IAttestorVerificationEngine
|
|||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(entry);
|
ArgumentNullException.ThrowIfNull(entry);
|
||||||
|
|
||||||
var signatureIssuer = await EvaluateSignatureAndIssuerAsync(entry, bundle, cancellationToken).ConfigureAwait(false);
|
var signatureIssuer = await EvaluateSignatureAndIssuerAsync(entry, bundle, evaluationTime, cancellationToken).ConfigureAwait(false);
|
||||||
var freshness = EvaluateFreshness(entry, evaluationTime);
|
var freshness = EvaluateFreshness(entry, evaluationTime);
|
||||||
var transparency = EvaluateTransparency(entry);
|
var transparency = EvaluateTransparency(entry);
|
||||||
var policy = EvaluatePolicy(entry, signatureIssuer.Signatures, signatureIssuer.Issuer, freshness, transparency, bundle is not null);
|
var policy = EvaluatePolicy(entry, signatureIssuer.Signatures, signatureIssuer.Issuer, freshness, transparency, bundle is not null);
|
||||||
@@ -55,6 +55,7 @@ public sealed class AttestorVerificationEngine : IAttestorVerificationEngine
|
|||||||
private async Task<(SignatureEvaluationResult Signatures, IssuerEvaluationResult Issuer)> EvaluateSignatureAndIssuerAsync(
|
private async Task<(SignatureEvaluationResult Signatures, IssuerEvaluationResult Issuer)> EvaluateSignatureAndIssuerAsync(
|
||||||
AttestorEntry entry,
|
AttestorEntry entry,
|
||||||
AttestorSubmissionRequest.SubmissionBundle? bundle,
|
AttestorSubmissionRequest.SubmissionBundle? bundle,
|
||||||
|
DateTimeOffset evaluationTime,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var signatureIssues = new List<string>();
|
var signatureIssues = new List<string>();
|
||||||
@@ -178,7 +179,7 @@ public sealed class AttestorVerificationEngine : IAttestorVerificationEngine
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "keyless":
|
case "keyless":
|
||||||
var keylessResult = EvaluateKeylessSignature(entry, bundle, preAuth, signatureIssues, issuerIssues);
|
var keylessResult = EvaluateKeylessSignature(entry, bundle, preAuth, signatureIssues, issuerIssues, evaluationTime);
|
||||||
verifiedSignatures = keylessResult.VerifiedSignatures;
|
verifiedSignatures = keylessResult.VerifiedSignatures;
|
||||||
subjectAlternativeName = keylessResult.SubjectAlternativeName;
|
subjectAlternativeName = keylessResult.SubjectAlternativeName;
|
||||||
break;
|
break;
|
||||||
@@ -270,7 +271,8 @@ public sealed class AttestorVerificationEngine : IAttestorVerificationEngine
|
|||||||
AttestorSubmissionRequest.SubmissionBundle bundle,
|
AttestorSubmissionRequest.SubmissionBundle bundle,
|
||||||
byte[] preAuthEncoding,
|
byte[] preAuthEncoding,
|
||||||
List<string> signatureIssues,
|
List<string> signatureIssues,
|
||||||
List<string> issuerIssues)
|
List<string> issuerIssues,
|
||||||
|
DateTimeOffset evaluationTime)
|
||||||
{
|
{
|
||||||
if (bundle.CertificateChain.Count == 0)
|
if (bundle.CertificateChain.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -296,46 +298,47 @@ public sealed class AttestorVerificationEngine : IAttestorVerificationEngine
|
|||||||
var leafCertificate = certificates[0];
|
var leafCertificate = certificates[0];
|
||||||
var subjectAltName = GetSubjectAlternativeNames(leafCertificate).FirstOrDefault();
|
var subjectAltName = GetSubjectAlternativeNames(leafCertificate).FirstOrDefault();
|
||||||
|
|
||||||
if (_options.Security.SignerIdentity.FulcioRoots.Count > 0)
|
if (_options.Security.SignerIdentity.FulcioRoots.Count > 0)
|
||||||
|
{
|
||||||
|
using var chain = new X509Chain
|
||||||
{
|
{
|
||||||
using var chain = new X509Chain
|
ChainPolicy =
|
||||||
{
|
|
||||||
ChainPolicy =
|
|
||||||
{
|
{
|
||||||
RevocationMode = X509RevocationMode.NoCheck,
|
RevocationMode = X509RevocationMode.NoCheck,
|
||||||
VerificationFlags = X509VerificationFlags.NoFlag,
|
VerificationFlags = X509VerificationFlags.NoFlag,
|
||||||
TrustMode = X509ChainTrustMode.CustomRootTrust
|
TrustMode = X509ChainTrustMode.CustomRootTrust,
|
||||||
|
VerificationTime = evaluationTime.UtcDateTime
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var rootPath in _options.Security.SignerIdentity.FulcioRoots)
|
foreach (var rootPath in _options.Security.SignerIdentity.FulcioRoots)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
if (File.Exists(rootPath))
|
||||||
{
|
{
|
||||||
if (File.Exists(rootPath))
|
var rootCertificate = X509CertificateLoader.LoadCertificateFromFile(rootPath);
|
||||||
{
|
chain.ChainPolicy.CustomTrustStore.Add(rootCertificate);
|
||||||
var rootCertificate = X509CertificateLoader.LoadCertificateFromFile(rootPath);
|
|
||||||
chain.ChainPolicy.CustomTrustStore.Add(rootCertificate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to load Fulcio root {Root}", rootPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
for (var i = 1; i < certificates.Count; i++)
|
|
||||||
{
|
{
|
||||||
chain.ChainPolicy.ExtraStore.Add(certificates[i]);
|
_logger.LogWarning(ex, "Failed to load Fulcio root {Root}", rootPath);
|
||||||
}
|
|
||||||
|
|
||||||
if (!chain.Build(leafCertificate))
|
|
||||||
{
|
|
||||||
var status = string.Join(";", chain.ChainStatus.Select(s => s.StatusInformation.Trim())).Trim(';');
|
|
||||||
issuerIssues.Add(string.IsNullOrEmpty(status) ? "certificate_chain_untrusted" : $"certificate_chain_untrusted:{status}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var i = 1; i < certificates.Count; i++)
|
||||||
|
{
|
||||||
|
chain.ChainPolicy.ExtraStore.Add(certificates[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chain.Build(leafCertificate))
|
||||||
|
{
|
||||||
|
var status = string.Join(";", chain.ChainStatus.Select(s => s.StatusInformation.Trim())).Trim(';');
|
||||||
|
issuerIssues.Add(string.IsNullOrEmpty(status) ? "certificate_chain_untrusted" : $"certificate_chain_untrusted:{status}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_options.Security.SignerIdentity.AllowedSans.Count > 0)
|
if (_options.Security.SignerIdentity.AllowedSans.Count > 0)
|
||||||
{
|
{
|
||||||
var sans = GetSubjectAlternativeNames(leafCertificate);
|
var sans = GetSubjectAlternativeNames(leafCertificate);
|
||||||
|
|||||||
@@ -223,9 +223,12 @@ public class AttestationChainBuilderTests
|
|||||||
result.IsSuccess.Should().BeTrue();
|
result.IsSuccess.Should().BeTrue();
|
||||||
result.LinksCreated.Should().HaveCount(3);
|
result.LinksCreated.Should().HaveCount(3);
|
||||||
// Links should be created in layer order
|
// Links should be created in layer order
|
||||||
result.LinksCreated[0].Metadata!.Annotations["layerIndex"].Should().Be("0");
|
Assert.NotNull(result.LinksCreated[0].Metadata?.Annotations);
|
||||||
result.LinksCreated[1].Metadata!.Annotations["layerIndex"].Should().Be("1");
|
Assert.NotNull(result.LinksCreated[1].Metadata?.Annotations);
|
||||||
result.LinksCreated[2].Metadata!.Annotations["layerIndex"].Should().Be("2");
|
Assert.NotNull(result.LinksCreated[2].Metadata?.Annotations);
|
||||||
|
result.LinksCreated[0].Metadata!.Annotations!["layerIndex"].Should().Be("0");
|
||||||
|
result.LinksCreated[1].Metadata!.Annotations!["layerIndex"].Should().Be("1");
|
||||||
|
result.LinksCreated[2].Metadata!.Annotations!["layerIndex"].Should().Be("2");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -319,7 +319,21 @@ public sealed class AttestorVerificationEngineTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static X509Certificate2 EnsurePrivateKey(X509Certificate2 certificate, ECDsa key)
|
private static X509Certificate2 EnsurePrivateKey(X509Certificate2 certificate, ECDsa key)
|
||||||
=> certificate.HasPrivateKey ? certificate : certificate.CopyWithPrivateKey(key);
|
{
|
||||||
|
if (certificate.HasPrivateKey)
|
||||||
|
{
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return certificate.CopyWithPrivateKey(key);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException)
|
||||||
|
{
|
||||||
|
return certificate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string ToPem(X509Certificate2 certificate)
|
private static string ToPem(X509Certificate2 certificate)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -65,35 +65,35 @@ public class AuthAbstractionsConstantsTests
|
|||||||
[nameof(StellaOpsClaimTypes.SessionId)] = "sid"
|
[nameof(StellaOpsClaimTypes.SessionId)] = "sid"
|
||||||
};
|
};
|
||||||
|
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Subject)], StellaOpsClaimTypes.Subject);
|
Assert.Equal(StellaOpsClaimTypes.Subject, expected[nameof(StellaOpsClaimTypes.Subject)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Tenant)], StellaOpsClaimTypes.Tenant);
|
Assert.Equal(StellaOpsClaimTypes.Tenant, expected[nameof(StellaOpsClaimTypes.Tenant)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Project)], StellaOpsClaimTypes.Project);
|
Assert.Equal(StellaOpsClaimTypes.Project, expected[nameof(StellaOpsClaimTypes.Project)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.ClientId)], StellaOpsClaimTypes.ClientId);
|
Assert.Equal(StellaOpsClaimTypes.ClientId, expected[nameof(StellaOpsClaimTypes.ClientId)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.ServiceAccount)], StellaOpsClaimTypes.ServiceAccount);
|
Assert.Equal(StellaOpsClaimTypes.ServiceAccount, expected[nameof(StellaOpsClaimTypes.ServiceAccount)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.TokenId)], StellaOpsClaimTypes.TokenId);
|
Assert.Equal(StellaOpsClaimTypes.TokenId, expected[nameof(StellaOpsClaimTypes.TokenId)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.AuthenticationMethod)], StellaOpsClaimTypes.AuthenticationMethod);
|
Assert.Equal(StellaOpsClaimTypes.AuthenticationMethod, expected[nameof(StellaOpsClaimTypes.AuthenticationMethod)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Scope)], StellaOpsClaimTypes.Scope);
|
Assert.Equal(StellaOpsClaimTypes.Scope, expected[nameof(StellaOpsClaimTypes.Scope)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.ScopeItem)], StellaOpsClaimTypes.ScopeItem);
|
Assert.Equal(StellaOpsClaimTypes.ScopeItem, expected[nameof(StellaOpsClaimTypes.ScopeItem)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.Audience)], StellaOpsClaimTypes.Audience);
|
Assert.Equal(StellaOpsClaimTypes.Audience, expected[nameof(StellaOpsClaimTypes.Audience)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.IdentityProvider)], StellaOpsClaimTypes.IdentityProvider);
|
Assert.Equal(StellaOpsClaimTypes.IdentityProvider, expected[nameof(StellaOpsClaimTypes.IdentityProvider)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.OperatorReason)], StellaOpsClaimTypes.OperatorReason);
|
Assert.Equal(StellaOpsClaimTypes.OperatorReason, expected[nameof(StellaOpsClaimTypes.OperatorReason)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.OperatorTicket)], StellaOpsClaimTypes.OperatorTicket);
|
Assert.Equal(StellaOpsClaimTypes.OperatorTicket, expected[nameof(StellaOpsClaimTypes.OperatorTicket)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.QuotaReason)], StellaOpsClaimTypes.QuotaReason);
|
Assert.Equal(StellaOpsClaimTypes.QuotaReason, expected[nameof(StellaOpsClaimTypes.QuotaReason)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.QuotaTicket)], StellaOpsClaimTypes.QuotaTicket);
|
Assert.Equal(StellaOpsClaimTypes.QuotaTicket, expected[nameof(StellaOpsClaimTypes.QuotaTicket)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.BackfillReason)], StellaOpsClaimTypes.BackfillReason);
|
Assert.Equal(StellaOpsClaimTypes.BackfillReason, expected[nameof(StellaOpsClaimTypes.BackfillReason)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.BackfillTicket)], StellaOpsClaimTypes.BackfillTicket);
|
Assert.Equal(StellaOpsClaimTypes.BackfillTicket, expected[nameof(StellaOpsClaimTypes.BackfillTicket)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyDigest)], StellaOpsClaimTypes.PolicyDigest);
|
Assert.Equal(StellaOpsClaimTypes.PolicyDigest, expected[nameof(StellaOpsClaimTypes.PolicyDigest)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyTicket)], StellaOpsClaimTypes.PolicyTicket);
|
Assert.Equal(StellaOpsClaimTypes.PolicyTicket, expected[nameof(StellaOpsClaimTypes.PolicyTicket)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyReason)], StellaOpsClaimTypes.PolicyReason);
|
Assert.Equal(StellaOpsClaimTypes.PolicyReason, expected[nameof(StellaOpsClaimTypes.PolicyReason)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PackRunId)], StellaOpsClaimTypes.PackRunId);
|
Assert.Equal(StellaOpsClaimTypes.PackRunId, expected[nameof(StellaOpsClaimTypes.PackRunId)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PackGateId)], StellaOpsClaimTypes.PackGateId);
|
Assert.Equal(StellaOpsClaimTypes.PackGateId, expected[nameof(StellaOpsClaimTypes.PackGateId)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PackPlanHash)], StellaOpsClaimTypes.PackPlanHash);
|
Assert.Equal(StellaOpsClaimTypes.PackPlanHash, expected[nameof(StellaOpsClaimTypes.PackPlanHash)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.PolicyOperation)], StellaOpsClaimTypes.PolicyOperation);
|
Assert.Equal(StellaOpsClaimTypes.PolicyOperation, expected[nameof(StellaOpsClaimTypes.PolicyOperation)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.IncidentReason)], StellaOpsClaimTypes.IncidentReason);
|
Assert.Equal(StellaOpsClaimTypes.IncidentReason, expected[nameof(StellaOpsClaimTypes.IncidentReason)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.VulnerabilityEnvironment)], StellaOpsClaimTypes.VulnerabilityEnvironment);
|
Assert.Equal(StellaOpsClaimTypes.VulnerabilityEnvironment, expected[nameof(StellaOpsClaimTypes.VulnerabilityEnvironment)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.VulnerabilityOwner)], StellaOpsClaimTypes.VulnerabilityOwner);
|
Assert.Equal(StellaOpsClaimTypes.VulnerabilityOwner, expected[nameof(StellaOpsClaimTypes.VulnerabilityOwner)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.VulnerabilityBusinessTier)], StellaOpsClaimTypes.VulnerabilityBusinessTier);
|
Assert.Equal(StellaOpsClaimTypes.VulnerabilityBusinessTier, expected[nameof(StellaOpsClaimTypes.VulnerabilityBusinessTier)]);
|
||||||
Assert.Equal(expected[nameof(StellaOpsClaimTypes.SessionId)], StellaOpsClaimTypes.SessionId);
|
Assert.Equal(StellaOpsClaimTypes.SessionId, expected[nameof(StellaOpsClaimTypes.SessionId)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Trait("Category", TestCategories.Unit)]
|
[Trait("Category", TestCategories.Unit)]
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class ServiceCollectionExtensionsTests
|
|||||||
using var provider = services.BuildServiceProvider();
|
using var provider = services.BuildServiceProvider();
|
||||||
|
|
||||||
var cache = provider.GetRequiredService<StellaOpsDiscoveryCache>();
|
var cache = provider.GetRequiredService<StellaOpsDiscoveryCache>();
|
||||||
var configuration = await cache.GetAsync(CancellationToken.None);
|
var configuration = await cache.GetAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
|
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
|
||||||
Assert.Equal(2, attemptCount);
|
Assert.Equal(2, attemptCount);
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ public class StellaOpsDiscoveryCacheTests
|
|||||||
var monitor = new TestOptionsMonitor<StellaOpsAuthClientOptions>(options);
|
var monitor = new TestOptionsMonitor<StellaOpsAuthClientOptions>(options);
|
||||||
var cache = new StellaOpsDiscoveryCache(httpClient, monitor, timeProvider, NullLogger<StellaOpsDiscoveryCache>.Instance);
|
var cache = new StellaOpsDiscoveryCache(httpClient, monitor, timeProvider, NullLogger<StellaOpsDiscoveryCache>.Instance);
|
||||||
|
|
||||||
var configuration = await cache.GetAsync(CancellationToken.None);
|
var configuration = await cache.GetAsync(TestContext.Current.CancellationToken);
|
||||||
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
|
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
|
||||||
|
|
||||||
timeProvider.Advance(TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(5));
|
timeProvider.Advance(TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
configuration = await cache.GetAsync(CancellationToken.None);
|
configuration = await cache.GetAsync(TestContext.Current.CancellationToken);
|
||||||
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
|
Assert.Equal(new Uri("https://authority.test/connect/token"), configuration.TokenEndpoint);
|
||||||
Assert.Equal(2, callCount);
|
Assert.Equal(2, callCount);
|
||||||
|
|
||||||
@@ -66,7 +66,7 @@ public class StellaOpsDiscoveryCacheTests
|
|||||||
HttpRequestException? exception = null;
|
HttpRequestException? exception = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await cache.GetAsync(CancellationToken.None);
|
await cache.GetAsync(TestContext.Current.CancellationToken);
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ public class StellaOpsJwksCacheTests
|
|||||||
var discoveryCache = new StellaOpsDiscoveryCache(discoveryClient, monitor, timeProvider, NullLogger<StellaOpsDiscoveryCache>.Instance);
|
var discoveryCache = new StellaOpsDiscoveryCache(discoveryClient, monitor, timeProvider, NullLogger<StellaOpsDiscoveryCache>.Instance);
|
||||||
var jwksCache = new StellaOpsJwksCache(jwksClient, discoveryCache, monitor, timeProvider, NullLogger<StellaOpsJwksCache>.Instance);
|
var jwksCache = new StellaOpsJwksCache(jwksClient, discoveryCache, monitor, timeProvider, NullLogger<StellaOpsJwksCache>.Instance);
|
||||||
|
|
||||||
var keys = await jwksCache.GetAsync(CancellationToken.None);
|
var keys = await jwksCache.GetAsync(TestContext.Current.CancellationToken);
|
||||||
Assert.NotNull(keys);
|
Assert.NotNull(keys);
|
||||||
|
|
||||||
timeProvider.Advance(TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(5));
|
timeProvider.Advance(TimeSpan.FromMinutes(1) + TimeSpan.FromSeconds(5));
|
||||||
|
|
||||||
keys = await jwksCache.GetAsync(CancellationToken.None);
|
keys = await jwksCache.GetAsync(TestContext.Current.CancellationToken);
|
||||||
Assert.NotNull(keys);
|
Assert.NotNull(keys);
|
||||||
Assert.Equal(2, jwksCallCount);
|
Assert.Equal(2, jwksCallCount);
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ public class StellaOpsJwksCacheTests
|
|||||||
|
|
||||||
Assert.True(offlineExpiry < timeProvider.GetUtcNow());
|
Assert.True(offlineExpiry < timeProvider.GetUtcNow());
|
||||||
|
|
||||||
await Assert.ThrowsAsync<HttpRequestException>(() => jwksCache.GetAsync(CancellationToken.None));
|
await Assert.ThrowsAsync<HttpRequestException>(() => jwksCache.GetAsync(TestContext.Current.CancellationToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpResponseMessage CreateJsonResponse(string json)
|
private static HttpResponseMessage CreateJsonResponse(string json)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class StellaOpsAuthorityConfigurationManagerTests
|
|||||||
var initialMetadataRequests = handler.MetadataRequests;
|
var initialMetadataRequests = handler.MetadataRequests;
|
||||||
var initialJwksRequests = handler.JwksRequests;
|
var initialJwksRequests = handler.JwksRequests;
|
||||||
|
|
||||||
var second = await manager.GetConfigurationAsync(CancellationToken.None);
|
var second = await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Cache must return same instance
|
// Cache must return same instance
|
||||||
Assert.Same(first, second);
|
Assert.Same(first, second);
|
||||||
@@ -70,11 +70,11 @@ public class StellaOpsAuthorityConfigurationManagerTests
|
|||||||
timeProvider,
|
timeProvider,
|
||||||
NullLogger<StellaOpsAuthorityConfigurationManager>.Instance);
|
NullLogger<StellaOpsAuthorityConfigurationManager>.Instance);
|
||||||
|
|
||||||
var first = await manager.GetConfigurationAsync(CancellationToken.None);
|
var first = await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
timeProvider.Advance(TimeSpan.FromMinutes(2));
|
timeProvider.Advance(TimeSpan.FromMinutes(2));
|
||||||
|
|
||||||
var second = await manager.GetConfigurationAsync(CancellationToken.None);
|
var second = await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Same(first, second);
|
Assert.Same(first, second);
|
||||||
Assert.Equal(2, handler.MetadataRequests);
|
Assert.Equal(2, handler.MetadataRequests);
|
||||||
@@ -100,12 +100,12 @@ public class StellaOpsAuthorityConfigurationManagerTests
|
|||||||
timeProvider,
|
timeProvider,
|
||||||
NullLogger<StellaOpsAuthorityConfigurationManager>.Instance);
|
NullLogger<StellaOpsAuthorityConfigurationManager>.Instance);
|
||||||
|
|
||||||
await manager.GetConfigurationAsync(CancellationToken.None);
|
await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
var updated = CreateOptions("https://authority2.test");
|
var updated = CreateOptions("https://authority2.test");
|
||||||
optionsMonitor.Set(updated);
|
optionsMonitor.Set(updated);
|
||||||
|
|
||||||
await manager.GetConfigurationAsync(CancellationToken.None);
|
await manager.GetConfigurationAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Equal(2, handler.MetadataRequests);
|
Assert.Equal(2, handler.MetadataRequests);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ public sealed class InMemoryLdapClaimsCacheTests
|
|||||||
["displayName"] = "Alice Example"
|
["displayName"] = "Alice Example"
|
||||||
});
|
});
|
||||||
|
|
||||||
await cache.SetAsync("uid=alice,ou=people,dc=example,dc=internal", claims, CancellationToken.None);
|
await cache.SetAsync("uid=alice,ou=people,dc=example,dc=internal", claims, TestContext.Current.CancellationToken);
|
||||||
var fetched = await cache.GetAsync("uid=alice,ou=people,dc=example,dc=internal", CancellationToken.None);
|
var fetched = await cache.GetAsync("uid=alice,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.NotNull(fetched);
|
Assert.NotNull(fetched);
|
||||||
Assert.Contains("operators", fetched!.Roles);
|
Assert.Contains("operators", fetched!.Roles);
|
||||||
@@ -35,10 +35,10 @@ public sealed class InMemoryLdapClaimsCacheTests
|
|||||||
var timeProvider = new TestTimeProvider(new DateTimeOffset(2025, 11, 9, 6, 0, 0, TimeSpan.Zero));
|
var timeProvider = new TestTimeProvider(new DateTimeOffset(2025, 11, 9, 6, 0, 0, TimeSpan.Zero));
|
||||||
var cache = CreateCache(enabled: true, ttlSeconds: 60, timeProvider: timeProvider);
|
var cache = CreateCache(enabled: true, ttlSeconds: 60, timeProvider: timeProvider);
|
||||||
|
|
||||||
await cache.SetAsync("uid=expired,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), CancellationToken.None);
|
await cache.SetAsync("uid=expired,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
timeProvider.Advance(TimeSpan.FromMinutes(5));
|
timeProvider.Advance(TimeSpan.FromMinutes(5));
|
||||||
var fetched = await cache.GetAsync("uid=expired,ou=people,dc=example,dc=internal", CancellationToken.None);
|
var fetched = await cache.GetAsync("uid=expired,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Null(fetched);
|
Assert.Null(fetched);
|
||||||
}
|
}
|
||||||
@@ -48,11 +48,11 @@ public sealed class InMemoryLdapClaimsCacheTests
|
|||||||
{
|
{
|
||||||
var cache = CreateCache(enabled: true, ttlSeconds: 600, maxEntries: 1);
|
var cache = CreateCache(enabled: true, ttlSeconds: 600, maxEntries: 1);
|
||||||
|
|
||||||
await cache.SetAsync("uid=first,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), CancellationToken.None);
|
await cache.SetAsync("uid=first,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), TestContext.Current.CancellationToken);
|
||||||
await cache.SetAsync("uid=second,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), CancellationToken.None);
|
await cache.SetAsync("uid=second,ou=people,dc=example,dc=internal", new LdapCachedClaims(Array.Empty<string>(), new Dictionary<string, string>()), TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
var first = await cache.GetAsync("uid=first,ou=people,dc=example,dc=internal", CancellationToken.None);
|
var first = await cache.GetAsync("uid=first,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
|
||||||
var second = await cache.GetAsync("uid=second,ou=people,dc=example,dc=internal", CancellationToken.None);
|
var second = await cache.GetAsync("uid=second,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Null(first);
|
Assert.Null(first);
|
||||||
Assert.NotNull(second);
|
Assert.NotNull(second);
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class LdapClaimsEnricherTests
|
|||||||
var identity = new ClaimsIdentity();
|
var identity = new ClaimsIdentity();
|
||||||
var context = CreateContext("uid=j.doe,ou=people,dc=example,dc=internal");
|
var context = CreateContext("uid=j.doe,ou=people,dc=example,dc=internal");
|
||||||
|
|
||||||
await enricher.EnrichAsync(identity, context, CancellationToken.None);
|
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "operators");
|
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "operators");
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ public class LdapClaimsEnricherTests
|
|||||||
var identity = new ClaimsIdentity();
|
var identity = new ClaimsIdentity();
|
||||||
var context = CreateContext("uid=ops,ou=people,dc=example,dc=internal");
|
var context = CreateContext("uid=ops,ou=people,dc=example,dc=internal");
|
||||||
|
|
||||||
await enricher.EnrichAsync(identity, context, CancellationToken.None);
|
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "incident");
|
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "incident");
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,7 @@ public class LdapClaimsEnricherTests
|
|||||||
var identity = new ClaimsIdentity();
|
var identity = new ClaimsIdentity();
|
||||||
var context = CreateContext("uid=alice,ou=people,dc=example,dc=internal");
|
var context = CreateContext("uid=alice,ou=people,dc=example,dc=internal");
|
||||||
|
|
||||||
await enricher.EnrichAsync(identity, context, CancellationToken.None);
|
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == "displayName" && claim.Value == "Alice Example");
|
Assert.Contains(identity.Claims, claim => claim.Type == "displayName" && claim.Value == "Alice Example");
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == "email" && claim.Value == "alice@example.test");
|
Assert.Contains(identity.Claims, claim => claim.Type == "email" && claim.Value == "alice@example.test");
|
||||||
@@ -134,11 +134,11 @@ public class LdapClaimsEnricherTests
|
|||||||
var identity = new ClaimsIdentity();
|
var identity = new ClaimsIdentity();
|
||||||
var context = CreateContext("uid=cached,ou=people,dc=example,dc=internal");
|
var context = CreateContext("uid=cached,ou=people,dc=example,dc=internal");
|
||||||
|
|
||||||
await enricher.EnrichAsync(identity, context, CancellationToken.None);
|
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "operators");
|
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "operators");
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == "displayName" && claim.Value == "Cached User");
|
Assert.Contains(identity.Claims, claim => claim.Type == "displayName" && claim.Value == "Cached User");
|
||||||
Assert.Equal(0, connection.Operations.Count);
|
Assert.Empty(connection.Operations);
|
||||||
Assert.Equal(0, cache.SetCount);
|
Assert.Equal(0, cache.SetCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class LdapCapabilityProbeTests
|
|||||||
checkClientProvisioning: true,
|
checkClientProvisioning: true,
|
||||||
checkBootstrap: true,
|
checkBootstrap: true,
|
||||||
options.CapabilityProbe.Timeout,
|
options.CapabilityProbe.Timeout,
|
||||||
CancellationToken.None);
|
TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(snapshot.ClientProvisioningWritable);
|
Assert.True(snapshot.ClientProvisioningWritable);
|
||||||
Assert.True(snapshot.BootstrapWritable);
|
Assert.True(snapshot.BootstrapWritable);
|
||||||
@@ -47,7 +47,7 @@ public class LdapCapabilityProbeTests
|
|||||||
checkClientProvisioning: true,
|
checkClientProvisioning: true,
|
||||||
checkBootstrap: true,
|
checkBootstrap: true,
|
||||||
options.CapabilityProbe.Timeout,
|
options.CapabilityProbe.Timeout,
|
||||||
CancellationToken.None);
|
TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.False(snapshot.ClientProvisioningWritable);
|
Assert.False(snapshot.ClientProvisioningWritable);
|
||||||
Assert.False(snapshot.BootstrapWritable);
|
Assert.False(snapshot.BootstrapWritable);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public sealed class LdapClientProvisioningStoreTests
|
|||||||
allowedScopes: new[] { "signer.sign" },
|
allowedScopes: new[] { "signer.sign" },
|
||||||
allowedAudiences: new[] { "signer" });
|
allowedAudiences: new[] { "signer" });
|
||||||
|
|
||||||
var result = await store.CreateOrUpdateAsync(registration, CancellationToken.None);
|
var result = await store.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.True(clientStore.Documents.ContainsKey("svc-bootstrap"));
|
Assert.True(clientStore.Documents.ContainsKey("svc-bootstrap"));
|
||||||
@@ -88,7 +88,7 @@ public sealed class LdapClientProvisioningStoreTests
|
|||||||
SecretHash = "hash"
|
SecretHash = "hash"
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await store.DeleteAsync("svc-bootstrap", CancellationToken.None);
|
var result = await store.DeleteAsync("svc-bootstrap", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.DoesNotContain("svc-bootstrap", clientStore.Documents.Keys);
|
Assert.DoesNotContain("svc-bootstrap", clientStore.Documents.Keys);
|
||||||
@@ -124,7 +124,7 @@ public sealed class LdapClientProvisioningStoreTests
|
|||||||
allowedGrantTypes: new[] { "client_credentials" },
|
allowedGrantTypes: new[] { "client_credentials" },
|
||||||
allowedScopes: new[] { "signer.sign" });
|
allowedScopes: new[] { "signer.sign" });
|
||||||
|
|
||||||
var result = await store.CreateOrUpdateAsync(registration, CancellationToken.None);
|
var result = await store.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.False(result.Succeeded);
|
Assert.False(result.Succeeded);
|
||||||
Assert.Equal("disabled", result.ErrorCode);
|
Assert.Equal("disabled", result.ErrorCode);
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class LdapCredentialStoreTests
|
|||||||
monitor,
|
monitor,
|
||||||
new FakeLdapConnectionFactory(connection));
|
new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.Equal(2, bindCalls.Count);
|
Assert.Equal(2, bindCalls.Count);
|
||||||
@@ -86,7 +86,7 @@ public class LdapCredentialStoreTests
|
|||||||
monitor,
|
monitor,
|
||||||
new FakeLdapConnectionFactory(connection));
|
new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("J.Doe", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.NotNull(result.User);
|
Assert.NotNull(result.User);
|
||||||
@@ -119,7 +119,7 @@ public class LdapCredentialStoreTests
|
|||||||
new FakeLdapConnectionFactory(connection),
|
new FakeLdapConnectionFactory(connection),
|
||||||
delayAsync: (_, _) => Task.CompletedTask);
|
delayAsync: (_, _) => Task.CompletedTask);
|
||||||
|
|
||||||
var result = await store.VerifyPasswordAsync("jdoe", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("jdoe", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.Equal(2, attempts);
|
Assert.Equal(2, attempts);
|
||||||
@@ -142,7 +142,7 @@ public class LdapCredentialStoreTests
|
|||||||
new FakeLdapConnectionFactory(connection),
|
new FakeLdapConnectionFactory(connection),
|
||||||
delayAsync: (_, _) => Task.CompletedTask);
|
delayAsync: (_, _) => Task.CompletedTask);
|
||||||
|
|
||||||
var result = await store.VerifyPasswordAsync("jdoe", "bad", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("jdoe", "bad", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.False(result.Succeeded);
|
Assert.False(result.Succeeded);
|
||||||
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, result.FailureCode);
|
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, result.FailureCode);
|
||||||
@@ -176,7 +176,7 @@ public class LdapCredentialStoreTests
|
|||||||
monitor,
|
monitor,
|
||||||
new FakeLdapConnectionFactory(connection));
|
new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
var result = await store.FindBySubjectAsync("uid=j.doe,ou=people,dc=example,dc=internal", CancellationToken.None);
|
var result = await store.FindBySubjectAsync("uid=j.doe,ou=people,dc=example,dc=internal", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal("uid=j.doe,ou=people,dc=example,dc=internal", result!.SubjectId);
|
Assert.Equal("uid=j.doe,ou=people,dc=example,dc=internal", result!.SubjectId);
|
||||||
@@ -201,7 +201,7 @@ public class LdapCredentialStoreTests
|
|||||||
email: "bootstrap@example.internal",
|
email: "bootstrap@example.internal",
|
||||||
requirePasswordReset: true);
|
requirePasswordReset: true);
|
||||||
|
|
||||||
var result = await store.UpsertUserAsync(registration, CancellationToken.None);
|
var result = await store.UpsertUserAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.Contains(connection.Operations, op => op.StartsWith("add:uid=bootstrap.user", StringComparison.OrdinalIgnoreCase));
|
Assert.Contains(connection.Operations, op => op.StartsWith("add:uid=bootstrap.user", StringComparison.OrdinalIgnoreCase));
|
||||||
@@ -234,7 +234,7 @@ public class LdapCredentialStoreTests
|
|||||||
email: "bootstrap@example.internal",
|
email: "bootstrap@example.internal",
|
||||||
requirePasswordReset: false);
|
requirePasswordReset: false);
|
||||||
|
|
||||||
var result = await store.UpsertUserAsync(registration, CancellationToken.None);
|
var result = await store.UpsertUserAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.Contains(connection.Operations, op => op.StartsWith("modify:uid=bootstrap.user", StringComparison.OrdinalIgnoreCase));
|
Assert.Contains(connection.Operations, op => op.StartsWith("modify:uid=bootstrap.user", StringComparison.OrdinalIgnoreCase));
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, connection);
|
var store = CreateStore(options, connection);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("noname", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("noname", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeTrue("Missing displayName should not prevent authentication");
|
result.Succeeded.Should().BeTrue("Missing displayName should not prevent authentication");
|
||||||
@@ -88,7 +88,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, connection);
|
var store = CreateStore(options, connection);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("nomail", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("nomail", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeTrue("Missing mail should not prevent authentication");
|
result.Succeeded.Should().BeTrue("Missing mail should not prevent authentication");
|
||||||
@@ -113,7 +113,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, connection);
|
var store = CreateStore(options, connection);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("nogroups", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("nogroups", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeTrue("Empty memberOf should not prevent authentication");
|
result.Succeeded.Should().BeTrue("Empty memberOf should not prevent authentication");
|
||||||
@@ -135,7 +135,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, connection);
|
var store = CreateStore(options, connection);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("nonexistent", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("nonexistent", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeFalse("Nonexistent user should fail authentication");
|
result.Succeeded.Should().BeFalse("Nonexistent user should fail authentication");
|
||||||
@@ -177,7 +177,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("user", "WrongPassword!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("user", "WrongPassword!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeFalse("Wrong password should fail authentication");
|
result.Succeeded.Should().BeFalse("Wrong password should fail authentication");
|
||||||
@@ -200,7 +200,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, connection);
|
var store = CreateStore(options, connection);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("malformed", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("malformed", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert - should handle gracefully (either succeed with warning or fail cleanly)
|
// Assert - should handle gracefully (either succeed with warning or fail cleanly)
|
||||||
// The exact behavior depends on implementation
|
// The exact behavior depends on implementation
|
||||||
@@ -225,7 +225,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", CancellationToken.None);
|
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await act.Should().ThrowAsync<TimeoutException>();
|
await act.Should().ThrowAsync<TimeoutException>();
|
||||||
@@ -247,7 +247,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", CancellationToken.None);
|
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await act.Should().ThrowAsync<InvalidOperationException>();
|
await act.Should().ThrowAsync<InvalidOperationException>();
|
||||||
@@ -303,7 +303,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, connection);
|
var store = CreateStore(options, connection);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("münchen-user", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("münchen-user", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeTrue("Unicode username should be handled");
|
result.Succeeded.Should().BeTrue("Unicode username should be handled");
|
||||||
@@ -329,7 +329,7 @@ public sealed class LdapConnectorResilienceTests
|
|||||||
var store = CreateStore(options, connection);
|
var store = CreateStore(options, connection);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("user+test", "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("user+test", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeTrue("Special characters in DN should be handled");
|
result.Succeeded.Should().BeTrue("Special characters in DN should be handled");
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync(maliciousUsername, "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync(maliciousUsername, "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeFalse("Injection attempt should fail");
|
result.Succeeded.Should().BeFalse("Injection attempt should fail");
|
||||||
@@ -103,7 +103,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync(emptyUsername, "Password1!", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync(emptyUsername, "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeFalse("Empty username should be rejected");
|
result.Succeeded.Should().BeFalse("Empty username should be rejected");
|
||||||
@@ -120,7 +120,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("user", null!, CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("user", null!, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeFalse("Null password should be rejected");
|
result.Succeeded.Should().BeFalse("Null password should be rejected");
|
||||||
@@ -137,7 +137,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("user", "", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("user", "", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Succeeded.Should().BeFalse("Empty password should be rejected");
|
result.Succeeded.Should().BeFalse("Empty password should be rejected");
|
||||||
@@ -169,7 +169,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", CancellationToken.None);
|
Func<Task> act = async () => await store.VerifyPasswordAsync("user", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
await act.Should().ThrowAsync<InvalidOperationException>();
|
await act.Should().ThrowAsync<InvalidOperationException>();
|
||||||
@@ -201,7 +201,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await store.VerifyPasswordAsync("targetuser", "Password1!", CancellationToken.None);
|
await store.VerifyPasswordAsync("targetuser", "Password1!", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
bindDns.Should().HaveCountGreaterThanOrEqualTo(2, "Should bind as service then as user");
|
bindDns.Should().HaveCountGreaterThanOrEqualTo(2, "Should bind as service then as user");
|
||||||
@@ -289,7 +289,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
// Act
|
// Act
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await store.VerifyPasswordAsync("user", "SuperSecret123!", CancellationToken.None);
|
await store.VerifyPasswordAsync("user", "SuperSecret123!", TestContext.Current.CancellationToken);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -308,7 +308,7 @@ public sealed class LdapConnectorSecurityTests
|
|||||||
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
var store = CreateStore(options, new FakeLdapConnectionFactory(connection));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await store.VerifyPasswordAsync("user", "MyPassword123", CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("user", "MyPassword123", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var resultString = result.ToString();
|
var resultString = result.ToString();
|
||||||
|
|||||||
@@ -112,13 +112,13 @@ public sealed class LdapConnectorSnapshotTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var fixtureFiles = Directory.Exists(FixturesPath)
|
var fixtureFiles = Directory.Exists(FixturesPath)
|
||||||
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).ToList()
|
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).Where(n => n is not null).Cast<string>().ToList()
|
||||||
: new List<string>();
|
: new List<string>();
|
||||||
|
|
||||||
var expectedFiles = Directory.Exists(ExpectedPath)
|
var expectedFiles = Directory.Exists(ExpectedPath)
|
||||||
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
|
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
|
||||||
.Select(f => Path.GetFileNameWithoutExtension(f).Replace(".canonical", ""))
|
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
|
||||||
.ToList()
|
.Where(n => n is not null).Cast<string>().ToList()
|
||||||
: new List<string>();
|
: new List<string>();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public sealed class OidcCredentialStoreTests
|
|||||||
username: "user@example.com",
|
username: "user@example.com",
|
||||||
signingCredentials: new SigningCredentials(symmetricKey, SecurityAlgorithms.HmacSha256));
|
signingCredentials: new SigningCredentials(symmetricKey, SecurityAlgorithms.HmacSha256));
|
||||||
|
|
||||||
var result = await store.VerifyPasswordAsync("user@example.com", token, CancellationToken.None);
|
var result = await store.VerifyPasswordAsync("user@example.com", token, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.False(result.Succeeded);
|
Assert.False(result.Succeeded);
|
||||||
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, result.FailureCode);
|
Assert.Equal(AuthorityCredentialFailureCode.InvalidCredentials, result.FailureCode);
|
||||||
@@ -102,10 +102,10 @@ public sealed class OidcCredentialStoreTests
|
|||||||
username: "user2@example.com",
|
username: "user2@example.com",
|
||||||
signingCredentials: new SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha256));
|
signingCredentials: new SigningCredentials(rsaKey, SecurityAlgorithms.RsaSha256));
|
||||||
|
|
||||||
var result = await storeA.VerifyPasswordAsync("user2@example.com", token, CancellationToken.None);
|
var result = await storeA.VerifyPasswordAsync("user2@example.com", token, TestContext.Current.CancellationToken);
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
|
|
||||||
var cached = await storeB.FindBySubjectAsync("user-2", CancellationToken.None);
|
var cached = await storeB.FindBySubjectAsync("user-2", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Null(cached);
|
Assert.Null(cached);
|
||||||
}
|
}
|
||||||
@@ -220,8 +220,8 @@ public sealed class OidcCredentialStoreTests
|
|||||||
|
|
||||||
public OidcPluginOptions CurrentValue => options.Values.First();
|
public OidcPluginOptions CurrentValue => options.Values.First();
|
||||||
|
|
||||||
public OidcPluginOptions Get(string name)
|
public OidcPluginOptions Get(string? name)
|
||||||
=> options.TryGetValue(name, out var value) ? value : options.Values.First();
|
=> name is not null && options.TryGetValue(name, out var value) ? value : options.Values.First();
|
||||||
|
|
||||||
public IDisposable OnChange(Action<OidcPluginOptions, string> listener)
|
public IDisposable OnChange(Action<OidcPluginOptions, string> listener)
|
||||||
=> new NoopDisposable();
|
=> new NoopDisposable();
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public sealed class OidcIdentityProviderPluginTests
|
|||||||
{
|
{
|
||||||
var (plugin, _) = CreatePlugin(HttpStatusCode.OK);
|
var (plugin, _) = CreatePlugin(HttpStatusCode.OK);
|
||||||
|
|
||||||
var result = await plugin.CheckHealthAsync(CancellationToken.None);
|
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Equal(AuthorityPluginHealthStatus.Healthy, result.Status);
|
Assert.Equal(AuthorityPluginHealthStatus.Healthy, result.Status);
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ public sealed class OidcIdentityProviderPluginTests
|
|||||||
{
|
{
|
||||||
var (plugin, _) = CreatePlugin(HttpStatusCode.ServiceUnavailable);
|
var (plugin, _) = CreatePlugin(HttpStatusCode.ServiceUnavailable);
|
||||||
|
|
||||||
var result = await plugin.CheckHealthAsync(CancellationToken.None);
|
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Equal(AuthorityPluginHealthStatus.Degraded, result.Status);
|
Assert.Equal(AuthorityPluginHealthStatus.Degraded, result.Status);
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ public sealed class OidcIdentityProviderPluginTests
|
|||||||
|
|
||||||
public OidcPluginOptions CurrentValue => options;
|
public OidcPluginOptions CurrentValue => options;
|
||||||
|
|
||||||
public OidcPluginOptions Get(string name)
|
public OidcPluginOptions Get(string? name)
|
||||||
=> string.Equals(name, pluginName, StringComparison.Ordinal) ? options : options;
|
=> string.Equals(name, pluginName, StringComparison.Ordinal) ? options : options;
|
||||||
|
|
||||||
public IDisposable OnChange(Action<OidcPluginOptions, string> listener)
|
public IDisposable OnChange(Action<OidcPluginOptions, string> listener)
|
||||||
|
|||||||
@@ -129,13 +129,13 @@ public sealed class OidcConnectorSnapshotTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var fixtureFiles = Directory.Exists(FixturesPath)
|
var fixtureFiles = Directory.Exists(FixturesPath)
|
||||||
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).ToList()
|
? Directory.EnumerateFiles(FixturesPath, "*.json").Select(Path.GetFileNameWithoutExtension).Where(n => n is not null).Cast<string>().ToList()
|
||||||
: new List<string>();
|
: new List<string>();
|
||||||
|
|
||||||
var expectedFiles = Directory.Exists(ExpectedPath)
|
var expectedFiles = Directory.Exists(ExpectedPath)
|
||||||
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
|
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
|
||||||
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
|
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
|
||||||
.ToList()
|
.Where(n => n is not null).Cast<string>().ToList()
|
||||||
: new List<string>();
|
: new List<string>();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public sealed class SamlIdentityProviderPluginTests
|
|||||||
{
|
{
|
||||||
var plugin = CreatePlugin(HttpStatusCode.OK);
|
var plugin = CreatePlugin(HttpStatusCode.OK);
|
||||||
|
|
||||||
var result = await plugin.CheckHealthAsync(CancellationToken.None);
|
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Equal(AuthorityPluginHealthStatus.Healthy, result.Status);
|
Assert.Equal(AuthorityPluginHealthStatus.Healthy, result.Status);
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ public sealed class SamlIdentityProviderPluginTests
|
|||||||
{
|
{
|
||||||
var plugin = CreatePlugin(HttpStatusCode.ServiceUnavailable);
|
var plugin = CreatePlugin(HttpStatusCode.ServiceUnavailable);
|
||||||
|
|
||||||
var result = await plugin.CheckHealthAsync(CancellationToken.None);
|
var result = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Equal(AuthorityPluginHealthStatus.Degraded, result.Status);
|
Assert.Equal(AuthorityPluginHealthStatus.Degraded, result.Status);
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ public sealed class SamlIdentityProviderPluginTests
|
|||||||
|
|
||||||
public SamlPluginOptions CurrentValue => options;
|
public SamlPluginOptions CurrentValue => options;
|
||||||
|
|
||||||
public SamlPluginOptions Get(string name)
|
public SamlPluginOptions Get(string? name)
|
||||||
=> string.Equals(name, pluginName, StringComparison.Ordinal) ? options : options;
|
=> string.Equals(name, pluginName, StringComparison.Ordinal) ? options : options;
|
||||||
|
|
||||||
public IDisposable OnChange(Action<SamlPluginOptions, string> listener)
|
public IDisposable OnChange(Action<SamlPluginOptions, string> listener)
|
||||||
|
|||||||
@@ -122,13 +122,13 @@ public sealed class SamlConnectorSnapshotTests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var fixtureFiles = Directory.Exists(FixturesPath)
|
var fixtureFiles = Directory.Exists(FixturesPath)
|
||||||
? Directory.EnumerateFiles(FixturesPath, "*.xml").Select(Path.GetFileNameWithoutExtension).ToList()
|
? Directory.EnumerateFiles(FixturesPath, "*.xml").Select(Path.GetFileNameWithoutExtension).Where(n => n is not null).Cast<string>().ToList()
|
||||||
: new List<string>();
|
: new List<string>();
|
||||||
|
|
||||||
var expectedFiles = Directory.Exists(ExpectedPath)
|
var expectedFiles = Directory.Exists(ExpectedPath)
|
||||||
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
|
? Directory.EnumerateFiles(ExpectedPath, "*.canonical.json")
|
||||||
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
|
.Select(f => Path.GetFileNameWithoutExtension(f)?.Replace(".canonical", ""))
|
||||||
.ToList()
|
.Where(n => n is not null).Cast<string>().ToList()
|
||||||
: new List<string>();
|
: new List<string>();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class StandardCredentialAuditLoggerTests
|
|||||||
failureCode: null,
|
failureCode: null,
|
||||||
reason: null,
|
reason: null,
|
||||||
properties: Array.Empty<AuthEventProperty>(),
|
properties: Array.Empty<AuthEventProperty>(),
|
||||||
CancellationToken.None);
|
TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
var record = Assert.Single(sink.Records);
|
var record = Assert.Single(sink.Records);
|
||||||
Assert.Equal("authority.plugin.standard.password_verification", record.EventType);
|
Assert.Equal("authority.plugin.standard.password_verification", record.EventType);
|
||||||
@@ -92,7 +92,7 @@ public class StandardCredentialAuditLoggerTests
|
|||||||
failureCode: AuthorityCredentialFailureCode.InvalidCredentials,
|
failureCode: AuthorityCredentialFailureCode.InvalidCredentials,
|
||||||
reason: "Invalid credentials.",
|
reason: "Invalid credentials.",
|
||||||
properties,
|
properties,
|
||||||
CancellationToken.None);
|
TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
var record = Assert.Single(sink.Records);
|
var record = Assert.Single(sink.Records);
|
||||||
Assert.Equal(AuthEventOutcome.Failure, record.Outcome);
|
Assert.Equal(AuthEventOutcome.Failure, record.Outcome);
|
||||||
@@ -147,7 +147,7 @@ public class StandardCredentialAuditLoggerTests
|
|||||||
failureCode: AuthorityCredentialFailureCode.LockedOut,
|
failureCode: AuthorityCredentialFailureCode.LockedOut,
|
||||||
reason: "Account locked.",
|
reason: "Account locked.",
|
||||||
properties,
|
properties,
|
||||||
CancellationToken.None);
|
TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
var record = Assert.Single(sink.Records);
|
var record = Assert.Single(sink.Records);
|
||||||
Assert.Equal(AuthEventOutcome.LockedOut, record.Outcome);
|
Assert.Equal(AuthEventOutcome.LockedOut, record.Outcome);
|
||||||
@@ -189,7 +189,7 @@ public class StandardCredentialAuditLoggerTests
|
|||||||
failureCode: AuthorityCredentialFailureCode.RequiresMfa,
|
failureCode: AuthorityCredentialFailureCode.RequiresMfa,
|
||||||
reason: "MFA required.",
|
reason: "MFA required.",
|
||||||
properties: null,
|
properties: null,
|
||||||
CancellationToken.None);
|
TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
var record = Assert.Single(sink.Records);
|
var record = Assert.Single(sink.Records);
|
||||||
var property = Assert.Single(record.Properties);
|
var property = Assert.Single(record.Properties);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public class StandardClaimsEnricherTests
|
|||||||
var identity = new ClaimsIdentity();
|
var identity = new ClaimsIdentity();
|
||||||
var enricher = new StandardClaimsEnricher();
|
var enricher = new StandardClaimsEnricher();
|
||||||
|
|
||||||
await enricher.EnrichAsync(identity, context, CancellationToken.None);
|
await enricher.EnrichAsync(identity, context, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "admin");
|
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "admin");
|
||||||
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "ops");
|
Assert.Contains(identity.Claims, claim => claim.Type == ClaimTypes.Role && claim.Value == "ops");
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class StandardClientProvisioningStoreTests
|
|||||||
allowedGrantTypes: new[] { "client_credentials" },
|
allowedGrantTypes: new[] { "client_credentials" },
|
||||||
allowedScopes: new[] { "scopeA" });
|
allowedScopes: new[] { "scopeA" });
|
||||||
|
|
||||||
var result = await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
|
var result = await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.True(store.Documents.TryGetValue("bootstrap-client", out var document));
|
Assert.True(store.Documents.TryGetValue("bootstrap-client", out var document));
|
||||||
@@ -40,7 +40,7 @@ public class StandardClientProvisioningStoreTests
|
|||||||
Assert.Equal(AuthoritySecretHasher.ComputeHash("SuperSecret1!"), document!.SecretHash);
|
Assert.Equal(AuthoritySecretHasher.ComputeHash("SuperSecret1!"), document!.SecretHash);
|
||||||
Assert.Equal("standard", document.Plugin);
|
Assert.Equal("standard", document.Plugin);
|
||||||
|
|
||||||
var descriptor = await provisioning.FindByClientIdAsync("bootstrap-client", CancellationToken.None);
|
var descriptor = await provisioning.FindByClientIdAsync("bootstrap-client", TestContext.Current.CancellationToken);
|
||||||
Assert.NotNull(descriptor);
|
Assert.NotNull(descriptor);
|
||||||
Assert.Equal("bootstrap-client", descriptor!.ClientId);
|
Assert.Equal("bootstrap-client", descriptor!.ClientId);
|
||||||
Assert.True(descriptor.Confidential);
|
Assert.True(descriptor.Confidential);
|
||||||
@@ -65,13 +65,13 @@ public class StandardClientProvisioningStoreTests
|
|||||||
allowedScopes: new[] { "scopeA" },
|
allowedScopes: new[] { "scopeA" },
|
||||||
tenant: " Tenant-Alpha " );
|
tenant: " Tenant-Alpha " );
|
||||||
|
|
||||||
await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
|
await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(store.Documents.TryGetValue("tenant-client", out var document));
|
Assert.True(store.Documents.TryGetValue("tenant-client", out var document));
|
||||||
Assert.NotNull(document);
|
Assert.NotNull(document);
|
||||||
Assert.Equal("tenant-alpha", document!.Properties[AuthorityClientMetadataKeys.Tenant]);
|
Assert.Equal("tenant-alpha", document!.Properties[AuthorityClientMetadataKeys.Tenant]);
|
||||||
|
|
||||||
var descriptor = await provisioning.FindByClientIdAsync("tenant-client", CancellationToken.None);
|
var descriptor = await provisioning.FindByClientIdAsync("tenant-client", TestContext.Current.CancellationToken);
|
||||||
Assert.NotNull(descriptor);
|
Assert.NotNull(descriptor);
|
||||||
Assert.Equal("tenant-alpha", descriptor!.Tenant);
|
Assert.Equal("tenant-alpha", descriptor!.Tenant);
|
||||||
}
|
}
|
||||||
@@ -92,14 +92,14 @@ public class StandardClientProvisioningStoreTests
|
|||||||
allowedScopes: new[] { "signer.sign" },
|
allowedScopes: new[] { "signer.sign" },
|
||||||
allowedAudiences: new[] { "attestor", "signer" });
|
allowedAudiences: new[] { "attestor", "signer" });
|
||||||
|
|
||||||
var result = await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
|
var result = await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.True(store.Documents.TryGetValue("signer", out var document));
|
Assert.True(store.Documents.TryGetValue("signer", out var document));
|
||||||
Assert.NotNull(document);
|
Assert.NotNull(document);
|
||||||
Assert.Equal("attestor signer", document!.Properties[AuthorityClientMetadataKeys.Audiences]);
|
Assert.Equal("attestor signer", document!.Properties[AuthorityClientMetadataKeys.Audiences]);
|
||||||
|
|
||||||
var descriptor = await provisioning.FindByClientIdAsync("signer", CancellationToken.None);
|
var descriptor = await provisioning.FindByClientIdAsync("signer", TestContext.Current.CancellationToken);
|
||||||
Assert.NotNull(descriptor);
|
Assert.NotNull(descriptor);
|
||||||
Assert.Equal(new[] { "attestor", "signer" }, descriptor!.AllowedAudiences.OrderBy(value => value, StringComparer.Ordinal));
|
Assert.Equal(new[] { "attestor", "signer" }, descriptor!.AllowedAudiences.OrderBy(value => value, StringComparer.Ordinal));
|
||||||
}
|
}
|
||||||
@@ -132,7 +132,7 @@ public class StandardClientProvisioningStoreTests
|
|||||||
allowedAudiences: new[] { "signer" },
|
allowedAudiences: new[] { "signer" },
|
||||||
certificateBindings: new[] { bindingRegistration });
|
certificateBindings: new[] { bindingRegistration });
|
||||||
|
|
||||||
await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
|
await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(store.Documents.TryGetValue("mtls-client", out var document));
|
Assert.True(store.Documents.TryGetValue("mtls-client", out var document));
|
||||||
Assert.NotNull(document);
|
Assert.NotNull(document);
|
||||||
@@ -164,9 +164,9 @@ public class StandardClientProvisioningStoreTests
|
|||||||
allowedGrantTypes: new[] { "client_credentials" },
|
allowedGrantTypes: new[] { "client_credentials" },
|
||||||
allowedScopes: new[] { "scopeA" });
|
allowedScopes: new[] { "scopeA" });
|
||||||
|
|
||||||
await provisioning.CreateOrUpdateAsync(registration, CancellationToken.None);
|
await provisioning.CreateOrUpdateAsync(registration, TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
var result = await provisioning.DeleteAsync("delete-me", CancellationToken.None);
|
var result = await provisioning.DeleteAsync("delete-me", TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.Succeeded);
|
Assert.True(result.Succeeded);
|
||||||
Assert.False(store.Documents.ContainsKey("delete-me"));
|
Assert.False(store.Documents.ContainsKey("delete-me"));
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class StandardIdentityProviderPluginTests
|
|||||||
new StandardClaimsEnricher(),
|
new StandardClaimsEnricher(),
|
||||||
NullLogger<StandardIdentityProviderPlugin>.Instance);
|
NullLogger<StandardIdentityProviderPlugin>.Instance);
|
||||||
|
|
||||||
var health = await plugin.CheckHealthAsync(CancellationToken.None);
|
var health = await plugin.CheckHealthAsync(TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
Assert.Equal(AuthorityPluginHealthStatus.Healthy, health.Status);
|
Assert.Equal(AuthorityPluginHealthStatus.Healthy, health.Status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class StandardPluginBootstrapperTests
|
|||||||
using var provider = services.BuildServiceProvider();
|
using var provider = services.BuildServiceProvider();
|
||||||
var bootstrapper = provider.GetRequiredService<StandardPluginBootstrapper>();
|
var bootstrapper = provider.GetRequiredService<StandardPluginBootstrapper>();
|
||||||
|
|
||||||
var exception = await Record.ExceptionAsync(() => bootstrapper.StartAsync(CancellationToken.None));
|
var exception = await Record.ExceptionAsync(() => bootstrapper.StartAsync(TestContext.Current.CancellationToken));
|
||||||
|
|
||||||
Assert.Null(exception);
|
Assert.Null(exception);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
|||||||
|
|
||||||
var updated = await store.UpsertUserAsync(update, CancellationToken.None);
|
var updated = await store.UpsertUserAsync(update, CancellationToken.None);
|
||||||
Assert.True(updated.Succeeded);
|
Assert.True(updated.Succeeded);
|
||||||
|
Assert.NotNull(updated.Value);
|
||||||
Assert.Contains("editor", updated.Value.Roles);
|
Assert.Contains("editor", updated.Value.Roles);
|
||||||
Assert.Contains("admin", updated.Value.Roles);
|
Assert.Contains("admin", updated.Value.Roles);
|
||||||
Assert.Equal("us", updated.Value.Attributes["region"]);
|
Assert.Equal("us", updated.Value.Attributes["region"]);
|
||||||
@@ -250,6 +251,7 @@ public class StandardUserCredentialStoreTests : IAsyncLifetime
|
|||||||
|
|
||||||
var created = await store.UpsertUserAsync(registration, CancellationToken.None);
|
var created = await store.UpsertUserAsync(registration, CancellationToken.None);
|
||||||
Assert.True(created.Succeeded);
|
Assert.True(created.Succeeded);
|
||||||
|
Assert.NotNull(created.Value);
|
||||||
|
|
||||||
var found = await store.FindBySubjectAsync(created.Value.SubjectId, CancellationToken.None);
|
var found = await store.FindBySubjectAsync(created.Value.SubjectId, CancellationToken.None);
|
||||||
Assert.NotNull(found);
|
Assert.NotNull(found);
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public sealed class AdvisoryAiRemoteInferenceEndpointTests : IClassFixture<Autho
|
|||||||
var expectedHash = ComputeSha256(payload.Prompt);
|
var expectedHash = ComputeSha256(payload.Prompt);
|
||||||
Assert.Equal(expectedHash, body["prompt_hash"]);
|
Assert.Equal(expectedHash, body["prompt_hash"]);
|
||||||
|
|
||||||
var doc = Assert.Single(lastLoginAttemptStore!.Records.Where(record => record.EventType == "authority.advisory_ai.remote_inference"));
|
var doc = Assert.Single(lastLoginAttemptStore!.Records, record => record.EventType == "authority.advisory_ai.remote_inference");
|
||||||
Assert.Equal("authority.advisory_ai.remote_inference", doc.EventType);
|
Assert.Equal("authority.advisory_ai.remote_inference", doc.EventType);
|
||||||
var properties = doc.Properties.ToDictionary(p => p.Name, p => p.Value);
|
var properties = doc.Properties.ToDictionary(p => p.Name, p => p.Value);
|
||||||
Assert.Equal(expectedHash, properties["advisory_ai.prompt.hash"]);
|
Assert.Equal(expectedHash, properties["advisory_ai.prompt.hash"]);
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class AuthorityAuditSinkTests
|
|||||||
Assert.Equal(record.OccurredAt, document.OccurredAt);
|
Assert.Equal(record.OccurredAt, document.OccurredAt);
|
||||||
Assert.Equal(new[] { "openid", "profile" }, document.Scopes);
|
Assert.Equal(new[] { "openid", "profile" }, document.Scopes);
|
||||||
|
|
||||||
var pluginProperty = Assert.Single(document.Properties.Where(property => property.Name == "plugin.failed_attempts"));
|
var pluginProperty = Assert.Single(document.Properties, property => property.Name == "plugin.failed_attempts");
|
||||||
Assert.Equal("0", pluginProperty.Value);
|
Assert.Equal("0", pluginProperty.Value);
|
||||||
Assert.Equal("none", pluginProperty.Classification);
|
Assert.Equal("none", pluginProperty.Classification);
|
||||||
|
|
||||||
|
|||||||
@@ -2926,9 +2926,9 @@ public class ClientCredentialsHandlersTests
|
|||||||
Assert.False(validateContext.IsRejected);
|
Assert.False(validateContext.IsRejected);
|
||||||
Assert.False(validateContext.Transaction.Properties.ContainsKey(AuthorityOpenIddictConstants.SenderConstraintProperty));
|
Assert.False(validateContext.Transaction.Properties.ContainsKey(AuthorityOpenIddictConstants.SenderConstraintProperty));
|
||||||
|
|
||||||
var bypassEvent = Assert.Single(auditSink.Events.Where(record => record.EventType == "authority.dpop.proof.bypass"));
|
var bypassEvent = Assert.Single(auditSink.Events, record => record.EventType == "authority.dpop.proof.bypass");
|
||||||
Assert.Equal(AuthEventOutcome.Success, bypassEvent.Outcome);
|
Assert.Equal(AuthEventOutcome.Success, bypassEvent.Outcome);
|
||||||
var reasonProperty = Assert.Single(bypassEvent.Properties.Where(property => property.Name == "dpop.reason_code"));
|
var reasonProperty = Assert.Single(bypassEvent.Properties, property => property.Name == "dpop.reason_code");
|
||||||
Assert.Equal("bypass", reasonProperty.Value.Value);
|
Assert.Equal("bypass", reasonProperty.Value.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3387,7 +3387,7 @@ public class ClientCredentialsHandlersTests
|
|||||||
var grantEvent = authSink.Events.LastOrDefault(evt => evt.EventType == "authority.client_credentials.grant");
|
var grantEvent = authSink.Events.LastOrDefault(evt => evt.EventType == "authority.client_credentials.grant");
|
||||||
Assert.NotNull(grantEvent);
|
Assert.NotNull(grantEvent);
|
||||||
|
|
||||||
var serviceProperty = Assert.Single(grantEvent!.Properties.Where(prop => prop.Name == "delegation.service_account"));
|
var serviceProperty = Assert.Single(grantEvent!.Properties, prop => prop.Name == "delegation.service_account");
|
||||||
Assert.Equal(serviceAccount.AccountId, serviceProperty.Value.Value);
|
Assert.Equal(serviceAccount.AccountId, serviceProperty.Value.Value);
|
||||||
|
|
||||||
var actorPropertyValues = grantEvent.Properties
|
var actorPropertyValues = grantEvent.Properties
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ namespace StellaOps.Authority.ConfigDiff.Tests;
|
|||||||
[Trait("BlastRadius", TestCategories.BlastRadius.Auth)]
|
[Trait("BlastRadius", TestCategories.BlastRadius.Auth)]
|
||||||
public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
||||||
{
|
{
|
||||||
|
private static readonly DateTimeOffset SnapshotTimestamp =
|
||||||
|
new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="AuthorityConfigDiffTests"/> class.
|
/// Initializes a new instance of the <see cref="AuthorityConfigDiffTests"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -61,7 +64,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
|||||||
async config => await GetSessionBehaviorAsync(config),
|
async config => await GetSessionBehaviorAsync(config),
|
||||||
async config => await GetRefreshBehaviorAsync(config),
|
async config => await GetRefreshBehaviorAsync(config),
|
||||||
async config => await GetAuthenticationBehaviorAsync(config)
|
async config => await GetAuthenticationBehaviorAsync(config)
|
||||||
]);
|
],
|
||||||
|
ct: TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.IsSuccess.Should().BeTrue(
|
result.IsSuccess.Should().BeTrue(
|
||||||
@@ -93,7 +97,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
|||||||
changedConfig,
|
changedConfig,
|
||||||
getBehavior: async config => await CaptureSessionBehaviorAsync(config),
|
getBehavior: async config => await CaptureSessionBehaviorAsync(config),
|
||||||
computeDelta: ComputeBehaviorSnapshotDelta,
|
computeDelta: ComputeBehaviorSnapshotDelta,
|
||||||
expectedDelta: expectedDelta);
|
expectedDelta: expectedDelta,
|
||||||
|
ct: TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.IsSuccess.Should().BeTrue(
|
result.IsSuccess.Should().BeTrue(
|
||||||
@@ -119,7 +124,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
|||||||
[
|
[
|
||||||
async config => await GetSessionBehaviorAsync(config),
|
async config => await GetSessionBehaviorAsync(config),
|
||||||
async config => await GetPasswordPolicyBehaviorAsync(config)
|
async config => await GetPasswordPolicyBehaviorAsync(config)
|
||||||
]);
|
],
|
||||||
|
ct: TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.IsSuccess.Should().BeTrue(
|
result.IsSuccess.Should().BeTrue(
|
||||||
@@ -151,7 +157,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
|||||||
changedConfig,
|
changedConfig,
|
||||||
getBehavior: async config => await CapturePasswordPolicyBehaviorAsync(config),
|
getBehavior: async config => await CapturePasswordPolicyBehaviorAsync(config),
|
||||||
computeDelta: ComputeBehaviorSnapshotDelta,
|
computeDelta: ComputeBehaviorSnapshotDelta,
|
||||||
expectedDelta: expectedDelta);
|
expectedDelta: expectedDelta,
|
||||||
|
ct: TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.IsSuccess.Should().BeTrue();
|
result.IsSuccess.Should().BeTrue();
|
||||||
@@ -176,7 +183,8 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
|||||||
[
|
[
|
||||||
async config => await GetTokenBehaviorAsync(config),
|
async config => await GetTokenBehaviorAsync(config),
|
||||||
async config => await GetSessionBehaviorAsync(config)
|
async config => await GetSessionBehaviorAsync(config)
|
||||||
]);
|
],
|
||||||
|
ct: TestContext.Current.CancellationToken);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.IsSuccess.Should().BeTrue(
|
result.IsSuccess.Should().BeTrue(
|
||||||
@@ -216,11 +224,11 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
|||||||
ConfigurationId: $"sessions-{config.MaxConcurrentSessions}",
|
ConfigurationId: $"sessions-{config.MaxConcurrentSessions}",
|
||||||
Behaviors:
|
Behaviors:
|
||||||
[
|
[
|
||||||
new CapturedBehavior("SessionLimit", config.MaxConcurrentSessions.ToString(), DateTimeOffset.UtcNow),
|
new CapturedBehavior("SessionLimit", config.MaxConcurrentSessions.ToString(), SnapshotTimestamp),
|
||||||
new CapturedBehavior("ConcurrencyPolicy",
|
new CapturedBehavior("ConcurrencyPolicy",
|
||||||
config.MaxConcurrentSessions > 5 ? "permissive" : "restrictive", DateTimeOffset.UtcNow)
|
config.MaxConcurrentSessions > 5 ? "permissive" : "restrictive", SnapshotTimestamp)
|
||||||
],
|
],
|
||||||
CapturedAt: DateTimeOffset.UtcNow);
|
CapturedAt: SnapshotTimestamp);
|
||||||
|
|
||||||
return Task.FromResult(snapshot);
|
return Task.FromResult(snapshot);
|
||||||
}
|
}
|
||||||
@@ -232,11 +240,11 @@ public class AuthorityConfigDiffTests : ConfigDiffTestBase
|
|||||||
Behaviors:
|
Behaviors:
|
||||||
[
|
[
|
||||||
new CapturedBehavior("PasswordComplexity",
|
new CapturedBehavior("PasswordComplexity",
|
||||||
config.MinPasswordLength >= 12 ? "enhanced" : "standard", DateTimeOffset.UtcNow),
|
config.MinPasswordLength >= 12 ? "enhanced" : "standard", SnapshotTimestamp),
|
||||||
new CapturedBehavior("ValidationRejectionRate",
|
new CapturedBehavior("ValidationRejectionRate",
|
||||||
config.MinPasswordLength >= 12 ? "increase" : "standard", DateTimeOffset.UtcNow)
|
config.MinPasswordLength >= 12 ? "increase" : "standard", SnapshotTimestamp)
|
||||||
],
|
],
|
||||||
CapturedAt: DateTimeOffset.UtcNow);
|
CapturedAt: SnapshotTimestamp);
|
||||||
|
|
||||||
return Task.FromResult(snapshot);
|
return Task.FromResult(snapshot);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public sealed class AuthorityPostgresFixture : PostgresIntegrationFixture, IColl
|
|||||||
public PostgresOptions CreateOptions()
|
public PostgresOptions CreateOptions()
|
||||||
{
|
{
|
||||||
var options = Fixture.CreateOptions();
|
var options = Fixture.CreateOptions();
|
||||||
options.SchemaName = SchemaName;
|
options.SchemaName = AuthorityDataSource.DefaultSchemaName;
|
||||||
options.MaxPoolSize = 10;
|
options.MaxPoolSize = 10;
|
||||||
options.MinPoolSize = 0;
|
options.MinPoolSize = 0;
|
||||||
return options;
|
return options;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Threading;
|
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
@@ -7,6 +6,7 @@ using Npgsql;
|
|||||||
using StellaOps.Authority.Core.Verdicts;
|
using StellaOps.Authority.Core.Verdicts;
|
||||||
using StellaOps.Authority.Persistence.Postgres;
|
using StellaOps.Authority.Persistence.Postgres;
|
||||||
using StellaOps.TestKit;
|
using StellaOps.TestKit;
|
||||||
|
using System.Text.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace StellaOps.Authority.Persistence.Tests;
|
namespace StellaOps.Authority.Persistence.Tests;
|
||||||
@@ -37,7 +37,7 @@ public sealed class VerdictManifestStoreTests : IAsyncLifetime
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task StoreAndGetById_RoundTripsManifest()
|
public async Task StoreAndGetById_RoundTripsManifest()
|
||||||
{
|
{
|
||||||
var evaluatedAt = DateTimeOffset.Parse("2025-01-15T10:00:00Z");
|
var evaluatedAt = new DateTimeOffset(2025, 1, 15, 10, 0, 0, TimeSpan.Zero);
|
||||||
var manifest = CreateManifest("tenant-1", "manifest-001", evaluatedAt, VexStatus.NotAffected);
|
var manifest = CreateManifest("tenant-1", "manifest-001", evaluatedAt, VexStatus.NotAffected);
|
||||||
|
|
||||||
await _store.StoreAsync(manifest);
|
await _store.StoreAsync(manifest);
|
||||||
@@ -58,12 +58,15 @@ public sealed class VerdictManifestStoreTests : IAsyncLifetime
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task StoreAsync_WritesStringEnumJson()
|
public async Task StoreAsync_WritesStringEnumJson()
|
||||||
{
|
{
|
||||||
var evaluatedAt = DateTimeOffset.Parse("2025-01-15T11:00:00Z");
|
var evaluatedAt = new DateTimeOffset(2025, 1, 15, 11, 0, 0, TimeSpan.Zero);
|
||||||
var manifest = CreateManifest("tenant-2", "manifest-002", evaluatedAt, VexStatus.UnderInvestigation);
|
var manifest = CreateManifest("tenant-2", "manifest-002", evaluatedAt, VexStatus.UnderInvestigation);
|
||||||
|
|
||||||
await _store.StoreAsync(manifest);
|
await _store.StoreAsync(manifest);
|
||||||
|
|
||||||
await using var conn = await _dataSource.OpenConnectionAsync(manifest.Tenant, "reader", CancellationToken.None);
|
await using var conn = await _dataSource.OpenConnectionAsync(
|
||||||
|
manifest.Tenant,
|
||||||
|
"reader",
|
||||||
|
TestContext.Current.CancellationToken);
|
||||||
await using var cmd = new NpgsqlCommand("""
|
await using var cmd = new NpgsqlCommand("""
|
||||||
SELECT result_json::text
|
SELECT result_json::text
|
||||||
FROM verdict_manifests
|
FROM verdict_manifests
|
||||||
@@ -77,7 +80,8 @@ public sealed class VerdictManifestStoreTests : IAsyncLifetime
|
|||||||
|
|
||||||
var json = (string?)await cmd.ExecuteScalarAsync();
|
var json = (string?)await cmd.ExecuteScalarAsync();
|
||||||
json.Should().NotBeNull();
|
json.Should().NotBeNull();
|
||||||
json.Should().Contain("\"status\":\"under_investigation\"");
|
using var document = JsonDocument.Parse(json!);
|
||||||
|
document.RootElement.GetProperty("status").GetString().Should().Be("under_investigation");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VerdictManifest CreateManifest(string tenant, string manifestId, DateTimeOffset evaluatedAt, VexStatus status)
|
private static VerdictManifest CreateManifest(string tenant, string manifestId, DateTimeOffset evaluatedAt, VexStatus status)
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ public sealed class CommandHandlersTests
|
|||||||
Assert.Equal("scan-missing", backend.LastEntryTraceScanId);
|
Assert.Equal("scan-missing", backend.LastEntryTraceScanId);
|
||||||
Assert.Contains("No EntryTrace data", output.Combined, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("No EntryTrace data", output.Combined, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var warning = Assert.Single(loggerProvider.Entries.Where(entry => entry.Level == LogLevel.Warning));
|
var warning = Assert.Single(loggerProvider.Entries, entry => entry.Level == LogLevel.Warning);
|
||||||
Assert.Contains("No EntryTrace data", warning.Message, StringComparison.OrdinalIgnoreCase);
|
Assert.Contains("No EntryTrace data", warning.Message, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
## Release History
|
; Unshipped analyzer releases
|
||||||
|
|
||||||
### Unreleased
|
### New Rules
|
||||||
|
|
||||||
- CONCELIER0004: Flag direct `new HttpClient()` usage inside `StellaOps.Concelier.Connector*` namespaces; require sandboxed `IHttpClientFactory` to enforce allow/deny lists. Exempts test assemblies and uses symbol-based namespace matching.
|
Rule ID | Category | Severity | Notes
|
||||||
|
--------|----------|----------|------
|
||||||
|
CONCELIER0004 | Sandbox | Warning | Flag direct `new HttpClient()` usage inside `StellaOps.Concelier.Connector*` namespaces
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public sealed class ConnectorHttpClientSandboxAnalyzer : DiagnosticAnalyzer
|
|||||||
private static readonly DiagnosticDescriptor Rule = new(
|
private static readonly DiagnosticDescriptor Rule = new(
|
||||||
id: DiagnosticId,
|
id: DiagnosticId,
|
||||||
title: "Connector HTTP clients must use sandboxed factory",
|
title: "Connector HTTP clients must use sandboxed factory",
|
||||||
messageFormat: "Use IHttpClientFactory or connector sandbox helpers instead of 'new HttpClient()' inside Concelier connectors.",
|
messageFormat: "Use IHttpClientFactory or connector sandbox helpers instead of 'new HttpClient()' inside Concelier connectors",
|
||||||
category: "Sandbox",
|
category: "Sandbox",
|
||||||
defaultSeverity: DiagnosticSeverity.Warning,
|
defaultSeverity: DiagnosticSeverity.Warning,
|
||||||
isEnabledByDefault: true,
|
isEnabledByDefault: true,
|
||||||
@@ -73,7 +73,7 @@ public sealed class ConnectorHttpClientSandboxAnalyzer : DiagnosticAnalyzer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return assemblyName.EndsWith(".Tests", StringComparison.OrdinalIgnoreCase)
|
return assemblyName!.EndsWith(".Tests", StringComparison.OrdinalIgnoreCase)
|
||||||
|| assemblyName.EndsWith(".Test", StringComparison.OrdinalIgnoreCase)
|
|| assemblyName.EndsWith(".Test", StringComparison.OrdinalIgnoreCase)
|
||||||
|| assemblyName.EndsWith(".Testing", StringComparison.OrdinalIgnoreCase);
|
|| assemblyName.EndsWith(".Testing", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
## Release History
|
; Unshipped analyzer releases
|
||||||
|
|
||||||
### Unreleased
|
### New Rules
|
||||||
|
|
||||||
#### New Rules
|
Rule ID | Category | Severity | Notes
|
||||||
|
--------|----------|----------|------
|
||||||
Rule ID | Title | Notes
|
CONCELIER0002 | Usage | Warning | Legacy merge service usage detected - flags references to `AdvisoryMergeService` and `AddMergeModule`
|
||||||
--------|-------|------
|
|
||||||
CONCELIER0002 | Legacy merge service usage detected | Flags references to `AdvisoryMergeService` and `AddMergeModule`.
|
|
||||||
|
|||||||
@@ -185,6 +185,11 @@ public sealed class AstraConnector : IFeedConnector
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
private async Task<string> FetchOvalDatabaseAsync(string version, CancellationToken cancellationToken)
|
private async Task<string> FetchOvalDatabaseAsync(string version, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
if (_fetchService is null || _rawDocumentStorage is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Fetch and raw document storage services are required for OVAL database fetch");
|
||||||
|
}
|
||||||
|
|
||||||
var uri = _options.BuildOvalDatabaseUri(version);
|
var uri = _options.BuildOvalDatabaseUri(version);
|
||||||
|
|
||||||
_logger.LogDebug("Fetching OVAL database for Astra Linux {Version} from {Uri}", version, uri);
|
_logger.LogDebug("Fetching OVAL database for Astra Linux {Version} from {Uri}", version, uri);
|
||||||
@@ -197,18 +202,24 @@ public sealed class AstraConnector : IFeedConnector
|
|||||||
|
|
||||||
var result = await _fetchService.FetchAsync(request, cancellationToken).ConfigureAwait(false);
|
var result = await _fetchService.FetchAsync(request, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!result.IsSuccess || result.Document is null)
|
if (result is null || !result.IsSuccess || result.Document is null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Failed to fetch OVAL database for version {version}");
|
throw new InvalidOperationException($"Failed to fetch OVAL database for version {version}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.Document.PayloadId.HasValue)
|
var document = result.Document;
|
||||||
|
|
||||||
|
if (!document.PayloadId.HasValue)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"OVAL database document for version {version} has no payload");
|
throw new InvalidOperationException($"OVAL database document for version {version} has no payload");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download the raw XML content
|
// Download the raw XML content
|
||||||
var payloadBytes = await _rawDocumentStorage.DownloadAsync(result.Document.PayloadId.Value, cancellationToken).ConfigureAwait(false);
|
var payloadBytes = await _rawDocumentStorage.DownloadAsync(document.PayloadId.Value, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (payloadBytes is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"OVAL database payload for version {version} not found");
|
||||||
|
}
|
||||||
return System.Text.Encoding.UTF8.GetString(payloadBytes);
|
return System.Text.Encoding.UTF8.GetString(payloadBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -359,7 +359,7 @@ public sealed class CccsHtmlParser
|
|||||||
|
|
||||||
var candidate = href.Trim();
|
var candidate = href.Trim();
|
||||||
var hasAbsolute = Uri.TryCreate(candidate, UriKind.Absolute, out var absolute);
|
var hasAbsolute = Uri.TryCreate(candidate, UriKind.Absolute, out var absolute);
|
||||||
if (!hasAbsolute || string.Equals(absolute.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase))
|
if (!hasAbsolute || absolute is null || string.Equals(absolute.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (baseUri is null || !Uri.TryCreate(baseUri, candidate, out absolute))
|
if (baseUri is null || !Uri.TryCreate(baseUri, candidate, out absolute))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public sealed class CertCcConnector : IFeedConnector
|
|||||||
await _documentStore.UpdateStatusAsync(result.Document.Id, DocumentStatuses.Mapped, cancellationToken).ConfigureAwait(false);
|
await _documentStore.UpdateStatusAsync(result.Document.Id, DocumentStatuses.Mapped, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shouldProcessNotes)
|
if (!shouldProcessNotes || result.Document is null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user