Compare commits
282 Commits
c467b4d4b7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
394b57f6bf | ||
|
|
3a2100aa78 | ||
|
|
417ef83202 | ||
|
|
2170a58734 | ||
|
|
415eff1207 | ||
|
|
b55d9fa68d | ||
|
|
5a480a3c2a | ||
|
|
4391f35d8a | ||
|
|
b1f40945b7 | ||
|
|
41864227d2 | ||
|
|
8137503221 | ||
|
|
08dab053c0 | ||
|
|
7ce83270d0 | ||
|
|
505fe7a885 | ||
|
|
0cb5c9abfb | ||
|
|
d59cc816c1 | ||
|
|
8c8f0c632d | ||
|
|
4344020dd1 | ||
|
|
b058dbe031 | ||
|
|
3411e825cd | ||
|
|
9202cd7da8 | ||
|
|
00c41790f4 | ||
|
|
2e70c9fdb6 | ||
| d233fa3529 | |||
|
|
e2e404e705 | ||
| 01f4943ab9 | |||
|
|
233873f620 | ||
|
|
f1a39c4ce3 | ||
|
|
6e45066e37 | ||
|
|
e00f6365da | ||
|
|
999e26a48e | ||
| d776e93b16 | |||
|
|
564df71bfb | ||
|
|
e1f1bef4c1 | ||
|
|
3f3473ee3a | ||
|
|
efaf3cb789 | ||
|
|
ce5ec9c158 | ||
|
|
ab22181e8b | ||
|
|
1995883476 | ||
|
|
0987cd6ac8 | ||
|
|
b83aa1aa0b | ||
|
|
ce1f282ce0 | ||
|
|
b8b493913a | ||
|
|
49922dff5a | ||
|
|
92bc4d3a07 | ||
|
|
0ad4777259 | ||
|
|
2bd189387e | ||
|
|
3a92c77a04 | ||
|
|
b7059d523e | ||
|
|
96e5646977 | ||
|
|
a3c7fe5e88 | ||
|
|
199aaf74d8 | ||
|
|
f30805ad7f | ||
|
|
689c656f20 | ||
|
|
108d1c64b3 | ||
|
|
bc0762e97d | ||
|
|
3d01bf9edc | ||
|
|
68bc53a07b | ||
|
|
4b124fb056 | ||
|
|
7c24ed96ee | ||
|
|
11597679ed | ||
|
|
e3f28a21ab | ||
|
|
a403979177 | ||
|
|
b8641b1959 | ||
|
|
98e6b76584 | ||
|
|
862bb6ed80 | ||
|
|
bd2529502e | ||
|
|
965cbf9574 | ||
|
|
af30fc322f | ||
|
|
e53a282fbe | ||
|
|
d907729778 | ||
|
|
8a72779c16 | ||
|
|
e0f6efecce | ||
|
|
98934170ca | ||
|
|
69651212ec | ||
|
|
53889d85e7 | ||
|
|
0de92144d2 | ||
|
|
9bd6a73926 | ||
|
|
4042fc2184 | ||
|
|
dd0067ea0b | ||
|
|
f6c22854a4 | ||
|
|
05597616d6 | ||
|
|
a6f1406509 | ||
|
|
0a8f8c14af | ||
|
|
7efee7dd41 | ||
|
|
952ba77924 | ||
|
|
23e463e346 | ||
|
|
849a70f9d1 | ||
|
|
868f8e0bb6 | ||
|
|
84c42ca2d8 | ||
|
|
efd6850c38 | ||
|
|
2b892ad1b2 | ||
|
|
e16d2b5224 | ||
|
|
5e514532df | ||
|
|
2141196496 | ||
|
|
bca02ec295 | ||
|
|
8cabdce3b6 | ||
|
|
6145d89468 | ||
|
|
ee317d3f61 | ||
|
|
4cc8bdb460 | ||
|
|
95ff83e0f0 | ||
|
|
3954615e81 | ||
|
|
8948b1a3e2 | ||
|
|
5cfcf0723a | ||
|
|
ba733b9f69 | ||
|
|
79d562ea5d | ||
|
|
a7cd10020a | ||
|
|
b978ae399f | ||
|
|
570746b7d9 | ||
|
|
8318b26370 | ||
|
|
1f76650b7e | ||
|
|
37304cf819 | ||
|
|
6beb9d7c4e | ||
|
|
be8c623e04 | ||
|
|
dd4bb50076 | ||
|
|
bf6ab6ba6f | ||
|
|
02849cc955 | ||
|
|
2eaf0f699b | ||
|
|
6c1177a6ce | ||
|
|
582a88e8f8 | ||
|
|
f0662dd45f | ||
|
|
43c281a8b2 | ||
| 91550196fe | |||
| e8eacde73e | |||
| 5d7c687a77 | |||
| ffa219cfeb | |||
|
|
579236bfce | ||
|
|
18d87c64c5 | ||
|
|
347c88342c | ||
|
|
cc69d332e3 | ||
|
|
53508ceccb | ||
|
|
6a299d231f | ||
|
|
635c70e828 | ||
|
|
0de3c8a3f0 | ||
|
|
175b750e29 | ||
|
|
8768c27f30 | ||
|
|
b018949a8d | ||
|
|
f214edff82 | ||
|
|
75f6942769 | ||
|
|
600f3a7a3c | ||
|
|
4dc7cf834a | ||
|
|
e1262eb916 | ||
|
|
2d079d61ed | ||
|
|
e0b585c799 | ||
|
|
de53785176 | ||
|
|
ca91f40051 | ||
|
|
35c8f9216f | ||
|
|
e923880694 | ||
|
|
37cba83708 | ||
|
|
ea1d58a89b | ||
|
|
47168fec38 | ||
|
|
6d049905c7 | ||
|
|
acbb0ff637 | ||
|
|
d785a9095f | ||
|
|
0c9e8d5d18 | ||
|
|
76ecea482e | ||
|
|
2d08f52715 | ||
|
|
885ce86af4 | ||
|
|
44171930ff | ||
|
|
909d9b6220 | ||
|
|
790801f329 | ||
|
|
c11d87d252 | ||
|
|
7df0677e34 | ||
|
|
b39eb34226 | ||
|
|
808ab87b21 | ||
|
|
25254e3831 | ||
|
|
0bef705bcc | ||
|
|
71e9a56cfd | ||
|
|
17d45a6d30 | ||
|
|
8f54ffa203 | ||
|
|
3488b22c0c | ||
|
|
7e7be4d2fd | ||
|
|
887b0a1c67 | ||
|
|
a4c4fda2a1 | ||
|
|
b34f13dc03 | ||
| 39d0ef6728 | |||
|
|
2548abc56f | ||
| b3656e5cb7 | |||
|
|
d040c001ac | ||
|
|
d1cbb905f8 | ||
|
|
05da719048 | ||
|
|
1c6730a1d2 | ||
|
|
3b96b2e3ea | ||
|
|
ef6e4b2067 | ||
|
|
8abbf9574d | ||
|
|
cfa2274d31 | ||
|
|
4c55b01222 | ||
|
|
e950474a77 | ||
|
|
e901d31acf | ||
|
|
c34fb7256d | ||
|
|
ea970ead2a | ||
|
|
d63af51f84 | ||
|
|
4831c7fcb0 | ||
|
|
1c782897f7 | ||
|
|
56e2f64d07 | ||
|
|
9f6e6f7fb3 | ||
|
|
6bee1fdcf5 | ||
|
|
d92973d6fd | ||
|
|
17826bdca1 | ||
|
|
7c39058386 | ||
|
|
46c8c47d06 | ||
|
|
e6119cbe91 | ||
|
|
150b3730ef | ||
|
|
5970f0d9bd | ||
|
|
bb709b643e | ||
|
|
0d4a986b7b | ||
|
|
7514eee949 | ||
|
|
029002ad05 | ||
| 2de8d1784b | |||
|
|
c13355923f | ||
|
|
fc99092dec | ||
|
|
c3ce1ebc25 | ||
|
|
7768555f2d | ||
|
|
cce96f3596 | ||
|
|
f47d2d1377 | ||
|
|
8d78dd219b | ||
|
|
2e89a92d92 | ||
|
|
48702191be | ||
|
|
ca09400069 | ||
|
|
dc7c75b496 | ||
|
|
967ae0ab16 | ||
|
|
b6b9ffc050 | ||
|
|
a7f3c7869a | ||
|
|
96352c9d27 | ||
|
|
f43e828b4e | ||
|
|
cbdc05b24d | ||
|
|
d519782a8f | ||
|
|
ca35db9ef4 | ||
|
|
79b8e53441 | ||
|
|
65b1599229 | ||
| 522fff73cd | |||
|
|
8ac994ed37 | ||
|
|
2e276d6676 | ||
|
|
f0e74d2ee8 | ||
|
|
10212d67c0 | ||
|
|
616ec73133 | ||
|
|
33c7e77273 | ||
|
|
e91da22836 | ||
|
|
d3ecd7f8e6 | ||
|
|
77cee6a209 | ||
|
|
8355e2ff75 | ||
|
|
e69b57d467 | ||
|
|
9075bad2d9 | ||
|
|
d3128aec24 | ||
|
|
833e68575a | ||
|
|
7b01c7d6ac | ||
|
|
08b27b8a26 | ||
| a3db0c959d | |||
|
|
13e4b53dda | ||
|
|
d09ebd0b64 | ||
|
|
61f963fd52 | ||
|
|
151f6b35cc | ||
|
|
7040984215 | ||
|
|
4a557ceb55 | ||
|
|
aec4336254 | ||
|
|
86be324fc0 | ||
|
|
babb81af52 | ||
|
|
0e8655cbb1 | ||
|
|
c2c6b58b41 | ||
|
|
b059bc7675 | ||
|
|
56c687253f | ||
|
|
69c59defdc | ||
|
|
9df52d84aa | ||
|
|
cef4cb2c5a | ||
|
|
75c2bcafce | ||
|
|
ba4c935182 | ||
|
|
d71c81e45d | ||
|
|
ae69b1a8a1 | ||
|
|
536f6249a6 | ||
|
|
515975edc5 | ||
|
|
a1ce3f74fa | ||
|
|
e5ffcd6535 | ||
|
|
b190563d80 | ||
|
|
62086949a4 | ||
|
|
18f28168f0 | ||
|
|
e536492da9 | ||
|
|
950f238a93 | ||
|
|
5a923d968c | ||
|
|
ec3726ac22 | ||
|
|
822e3b6037 | ||
|
|
dd217b4546 | ||
|
|
21a2759412 |
37
.claude/settings.local.json
Normal file
37
.claude/settings.local.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(dotnet --list-sdks:*)",
|
||||
"Bash(winget install:*)",
|
||||
"Bash(dotnet restore:*)",
|
||||
"Bash(dotnet nuget:*)",
|
||||
"Bash(csc -parse:*)",
|
||||
"Bash(grep:*)",
|
||||
"Bash(dotnet build:*)",
|
||||
"Bash(cat:*)",
|
||||
"Bash(copy:*)",
|
||||
"Bash(dotnet test:*)",
|
||||
"Bash(dir:*)",
|
||||
"Bash(Select-Object -ExpandProperty FullName)",
|
||||
"Bash(echo:*)",
|
||||
"Bash(Out-File -FilePath \"E:\\dev\\git.stella-ops.org\\src\\Scanner\\__Libraries\\StellaOps.Scanner.Surface\\StellaOps.Scanner.Surface.csproj\" -Encoding utf8)",
|
||||
"Bash(wc:*)",
|
||||
"Bash(find:*)",
|
||||
"WebFetch(domain:docs.gradle.org)",
|
||||
"WebSearch",
|
||||
"Bash(dotnet msbuild:*)",
|
||||
"Bash(test:*)",
|
||||
"Bash(taskkill:*)",
|
||||
"Bash(timeout /t)",
|
||||
"Bash(dotnet clean:*)",
|
||||
"Bash(if not exist \"E:\\dev\\git.stella-ops.org\\src\\Scanner\\__Tests\\StellaOps.Scanner.Analyzers.Lang.Java.Tests\\Internal\" mkdir \"E:\\dev\\git.stella-ops.org\\src\\Scanner\\__Tests\\StellaOps.Scanner.Analyzers.Lang.Java.Tests\\Internal\")",
|
||||
"Bash(if not exist \"E:\\dev\\git.stella-ops.org\\src\\Scanner\\__Tests\\StellaOps.Scanner.Analyzers.Lang.Node.Tests\\Internal\" mkdir \"E:\\dev\\git.stella-ops.org\\src\\Scanner\\__Tests\\StellaOps.Scanner.Analyzers.Lang.Node.Tests\\Internal\")",
|
||||
"Bash(rm:*)",
|
||||
"Bash(if not exist \"C:\\dev\\New folder\\git.stella-ops.org\\docs\\implplan\\archived\" mkdir \"C:\\dev\\New folder\\git.stella-ops.org\\docs\\implplan\\archived\")",
|
||||
"Bash(del \"C:\\dev\\New folder\\git.stella-ops.org\\docs\\implplan\\SPRINT_0510_0001_0001_airgap.md\")"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
},
|
||||
"outputStyle": "default"
|
||||
}
|
||||
12
.config/dotnet-tools.json
Normal file
12
.config/dotnet-tools.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-stryker": {
|
||||
"version": "4.4.0",
|
||||
"commands": [
|
||||
"stryker"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
23
.dockerignore
Normal file
23
.dockerignore
Normal file
@@ -0,0 +1,23 @@
|
||||
.git
|
||||
.gitignore
|
||||
.gitea
|
||||
.venv
|
||||
bin
|
||||
obj
|
||||
**/bin
|
||||
**/obj
|
||||
local-nugets
|
||||
.nuget
|
||||
**/node_modules
|
||||
**/dist
|
||||
**/coverage
|
||||
**/*.user
|
||||
**/*.suo
|
||||
**/*.cache
|
||||
**/.vscode
|
||||
**/.idea
|
||||
**/.DS_Store
|
||||
**/TestResults
|
||||
**/out
|
||||
**/packages
|
||||
/tmp
|
||||
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
[src/Scanner/StellaOps.Scanner.Analyzers.Native/**.cs]
|
||||
dotnet_diagnostic.CA2022.severity = none
|
||||
[src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Native.Tests/**.cs]
|
||||
dotnet_diagnostic.CA2022.severity = none
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -1,2 +1,5 @@
|
||||
# Ensure analyzer fixture assets keep LF endings for deterministic hashes
|
||||
src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Python.Tests/Fixtures/** text eol=lf
|
||||
|
||||
# Ensure reachability sample assets keep LF endings for deterministic hashes
|
||||
tests/reachability/samples-public/** text eol=lf
|
||||
|
||||
70
.gitea/workflows/advisory-ai-release.yml
Normal file
70
.gitea/workflows/advisory-ai-release.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Advisory AI Feed Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
allow_dev_key:
|
||||
description: 'Allow dev key for testing (1=yes)'
|
||||
required: false
|
||||
default: '0'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/AdvisoryAI/feeds/**'
|
||||
- 'docs/samples/advisory-feeds/**'
|
||||
|
||||
jobs:
|
||||
package-feeds:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.6.0'
|
||||
|
||||
- name: Fallback to dev key when secret is absent
|
||||
run: |
|
||||
if [ -z "${COSIGN_PRIVATE_KEY_B64}" ]; then
|
||||
echo "[warn] COSIGN_PRIVATE_KEY_B64 not set; using dev key for non-production"
|
||||
echo "COSIGN_ALLOW_DEV_KEY=1" >> $GITHUB_ENV
|
||||
echo "COSIGN_PASSWORD=stellaops-dev" >> $GITHUB_ENV
|
||||
fi
|
||||
# Manual override
|
||||
if [ "${{ github.event.inputs.allow_dev_key }}" = "1" ]; then
|
||||
echo "COSIGN_ALLOW_DEV_KEY=1" >> $GITHUB_ENV
|
||||
echo "COSIGN_PASSWORD=stellaops-dev" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Package advisory feeds
|
||||
run: |
|
||||
chmod +x ops/deployment/advisory-ai/package-advisory-feeds.sh
|
||||
ops/deployment/advisory-ai/package-advisory-feeds.sh
|
||||
|
||||
- name: Generate SBOM
|
||||
run: |
|
||||
# Install syft
|
||||
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin v1.0.0
|
||||
|
||||
# Generate SBOM for feed bundle
|
||||
syft dir:out/advisory-ai/feeds/stage \
|
||||
-o spdx-json=out/advisory-ai/feeds/advisory-feeds.sbom.json \
|
||||
--name advisory-feeds
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: advisory-feeds-${{ github.run_number }}
|
||||
path: |
|
||||
out/advisory-ai/feeds/advisory-feeds.tar.gz
|
||||
out/advisory-ai/feeds/advisory-feeds.manifest.json
|
||||
out/advisory-ai/feeds/advisory-feeds.manifest.dsse.json
|
||||
out/advisory-ai/feeds/advisory-feeds.sbom.json
|
||||
out/advisory-ai/feeds/provenance.json
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
28
.gitea/workflows/airgap-sealed-ci.yml
Normal file
28
.gitea/workflows/airgap-sealed-ci.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Airgap Sealed CI Smoke
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'ops/devops/airgap/**'
|
||||
- '.gitea/workflows/airgap-sealed-ci.yml'
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
paths:
|
||||
- 'ops/devops/airgap/**'
|
||||
- '.gitea/workflows/airgap-sealed-ci.yml'
|
||||
|
||||
jobs:
|
||||
sealed-smoke:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
- name: Install dnslib
|
||||
run: pip install dnslib
|
||||
- name: Run sealed-mode smoke
|
||||
run: sudo ops/devops/airgap/sealed-ci-smoke.sh
|
||||
83
.gitea/workflows/aoc-backfill-release.yml
Normal file
83
.gitea/workflows/aoc-backfill-release.yml
Normal file
@@ -0,0 +1,83 @@
|
||||
name: AOC Backfill Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dataset_hash:
|
||||
description: 'Dataset hash from dev rehearsal (leave empty for dev mode)'
|
||||
required: false
|
||||
default: ''
|
||||
allow_dev_key:
|
||||
description: 'Allow dev key for testing (1=yes)'
|
||||
required: false
|
||||
default: '0'
|
||||
|
||||
jobs:
|
||||
package-backfill:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Setup cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.6.0'
|
||||
|
||||
- name: Restore AOC CLI
|
||||
run: dotnet restore src/Aoc/StellaOps.Aoc.Cli/StellaOps.Aoc.Cli.csproj
|
||||
|
||||
- name: Configure signing
|
||||
run: |
|
||||
if [ -z "${COSIGN_PRIVATE_KEY_B64}" ]; then
|
||||
echo "[info] No production key; using dev key"
|
||||
echo "COSIGN_ALLOW_DEV_KEY=1" >> $GITHUB_ENV
|
||||
echo "COSIGN_PASSWORD=stellaops-dev" >> $GITHUB_ENV
|
||||
fi
|
||||
if [ "${{ github.event.inputs.allow_dev_key }}" = "1" ]; then
|
||||
echo "COSIGN_ALLOW_DEV_KEY=1" >> $GITHUB_ENV
|
||||
echo "COSIGN_PASSWORD=stellaops-dev" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Package AOC backfill release
|
||||
run: |
|
||||
chmod +x ops/devops/aoc/package-backfill-release.sh
|
||||
DATASET_HASH="${{ github.event.inputs.dataset_hash }}" \
|
||||
ops/devops/aoc/package-backfill-release.sh
|
||||
env:
|
||||
DATASET_HASH: ${{ github.event.inputs.dataset_hash }}
|
||||
|
||||
- name: Generate SBOM with syft
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin v1.0.0
|
||||
syft dir:out/aoc/cli \
|
||||
-o spdx-json=out/aoc/aoc-backfill-runner.sbom.json \
|
||||
--name aoc-backfill-runner || true
|
||||
|
||||
- name: Verify checksums
|
||||
run: |
|
||||
cd out/aoc
|
||||
sha256sum -c SHA256SUMS
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: aoc-backfill-release-${{ github.run_number }}
|
||||
path: |
|
||||
out/aoc/aoc-backfill-runner.tar.gz
|
||||
out/aoc/aoc-backfill-runner.manifest.json
|
||||
out/aoc/aoc-backfill-runner.sbom.json
|
||||
out/aoc/aoc-backfill-runner.provenance.json
|
||||
out/aoc/aoc-backfill-runner.dsse.json
|
||||
out/aoc/SHA256SUMS
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
170
.gitea/workflows/aoc-guard.yml
Normal file
170
.gitea/workflows/aoc-guard.yml
Normal file
@@ -0,0 +1,170 @@
|
||||
name: AOC Guard CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/Aoc/**'
|
||||
- 'src/Concelier/**'
|
||||
- 'src/Authority/**'
|
||||
- 'src/Excititor/**'
|
||||
- 'ops/devops/aoc/**'
|
||||
- '.gitea/workflows/aoc-guard.yml'
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
paths:
|
||||
- 'src/Aoc/**'
|
||||
- 'src/Concelier/**'
|
||||
- 'src/Authority/**'
|
||||
- 'src/Excititor/**'
|
||||
- 'ops/devops/aoc/**'
|
||||
- '.gitea/workflows/aoc-guard.yml'
|
||||
|
||||
jobs:
|
||||
aoc-guard:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Set up .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Restore analyzers
|
||||
run: dotnet restore src/Aoc/__Analyzers/StellaOps.Aoc.Analyzers/StellaOps.Aoc.Analyzers.csproj
|
||||
|
||||
- name: Build analyzers
|
||||
run: dotnet build src/Aoc/__Analyzers/StellaOps.Aoc.Analyzers/StellaOps.Aoc.Analyzers.csproj -c Release
|
||||
|
||||
- name: Run analyzers against ingestion projects
|
||||
run: |
|
||||
dotnet build src/Concelier/StellaOps.Concelier.Ingestion/StellaOps.Concelier.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true
|
||||
dotnet build src/Authority/StellaOps.Authority.Ingestion/StellaOps.Authority.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true
|
||||
dotnet build src/Excititor/StellaOps.Excititor.Ingestion/StellaOps.Excititor.Ingestion.csproj -c Release /p:RunAnalyzers=true /p:TreatWarningsAsErrors=true
|
||||
|
||||
- name: Run analyzer tests with coverage
|
||||
run: |
|
||||
mkdir -p $ARTIFACT_DIR
|
||||
dotnet test src/Aoc/__Tests/StellaOps.Aoc.Analyzers.Tests/StellaOps.Aoc.Analyzers.Tests.csproj -c Release \
|
||||
--settings src/Aoc/aoc.runsettings \
|
||||
--collect:"XPlat Code Coverage" \
|
||||
--logger "trx;LogFileName=aoc-analyzers-tests.trx" \
|
||||
--results-directory $ARTIFACT_DIR
|
||||
|
||||
- name: Run AOC library tests with coverage
|
||||
run: |
|
||||
dotnet test src/Aoc/__Tests/StellaOps.Aoc.Tests/StellaOps.Aoc.Tests.csproj -c Release \
|
||||
--settings src/Aoc/aoc.runsettings \
|
||||
--collect:"XPlat Code Coverage" \
|
||||
--logger "trx;LogFileName=aoc-lib-tests.trx" \
|
||||
--results-directory $ARTIFACT_DIR
|
||||
|
||||
- name: Run AOC CLI tests with coverage
|
||||
run: |
|
||||
dotnet test src/Aoc/__Tests/StellaOps.Aoc.Cli.Tests/StellaOps.Aoc.Cli.Tests.csproj -c Release \
|
||||
--settings src/Aoc/aoc.runsettings \
|
||||
--collect:"XPlat Code Coverage" \
|
||||
--logger "trx;LogFileName=aoc-cli-tests.trx" \
|
||||
--results-directory $ARTIFACT_DIR
|
||||
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
dotnet tool install --global dotnet-reportgenerator-globaltool || true
|
||||
reportgenerator \
|
||||
-reports:"$ARTIFACT_DIR/**/coverage.cobertura.xml" \
|
||||
-targetdir:"$ARTIFACT_DIR/coverage-report" \
|
||||
-reporttypes:"Html;Cobertura;TextSummary" || true
|
||||
if [ -f "$ARTIFACT_DIR/coverage-report/Summary.txt" ]; then
|
||||
cat "$ARTIFACT_DIR/coverage-report/Summary.txt"
|
||||
fi
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: aoc-guard-artifacts
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
|
||||
aoc-verify:
|
||||
needs: aoc-guard
|
||||
runs-on: ubuntu-22.04
|
||||
if: github.event_name != 'schedule'
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
AOC_VERIFY_SINCE: ${{ github.event.pull_request.base.sha || 'HEAD~1' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Set up .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Run AOC verify
|
||||
env:
|
||||
STAGING_MONGO_URI: ${{ secrets.STAGING_MONGO_URI || vars.STAGING_MONGO_URI }}
|
||||
STAGING_POSTGRES_URI: ${{ secrets.STAGING_POSTGRES_URI || vars.STAGING_POSTGRES_URI }}
|
||||
run: |
|
||||
mkdir -p $ARTIFACT_DIR
|
||||
|
||||
# Prefer PostgreSQL, fall back to MongoDB (legacy)
|
||||
if [ -n "${STAGING_POSTGRES_URI:-}" ]; then
|
||||
echo "Using PostgreSQL for AOC verification"
|
||||
dotnet run --project src/Aoc/StellaOps.Aoc.Cli -- verify \
|
||||
--since "$AOC_VERIFY_SINCE" \
|
||||
--postgres "$STAGING_POSTGRES_URI" \
|
||||
--output "$ARTIFACT_DIR/aoc-verify.json" \
|
||||
--ndjson "$ARTIFACT_DIR/aoc-verify.ndjson" \
|
||||
--verbose || VERIFY_EXIT=$?
|
||||
elif [ -n "${STAGING_MONGO_URI:-}" ]; then
|
||||
echo "Using MongoDB for AOC verification (deprecated)"
|
||||
dotnet run --project src/Aoc/StellaOps.Aoc.Cli -- verify \
|
||||
--since "$AOC_VERIFY_SINCE" \
|
||||
--mongo "$STAGING_MONGO_URI" \
|
||||
--output "$ARTIFACT_DIR/aoc-verify.json" \
|
||||
--ndjson "$ARTIFACT_DIR/aoc-verify.ndjson" \
|
||||
--verbose || VERIFY_EXIT=$?
|
||||
else
|
||||
echo "::warning::Neither STAGING_POSTGRES_URI nor STAGING_MONGO_URI set; running dry-run verification"
|
||||
dotnet run --project src/Aoc/StellaOps.Aoc.Cli -- verify \
|
||||
--since "$AOC_VERIFY_SINCE" \
|
||||
--postgres "placeholder" \
|
||||
--dry-run \
|
||||
--verbose
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -n "${VERIFY_EXIT:-}" ] && [ "${VERIFY_EXIT}" -ne 0 ]; then
|
||||
echo "::error::AOC verify reported violations"; exit ${VERIFY_EXIT}
|
||||
fi
|
||||
|
||||
- name: Upload verify artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: aoc-verify-artifacts
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
51
.gitea/workflows/api-governance.yml
Normal file
51
.gitea/workflows/api-governance.yml
Normal file
@@ -0,0 +1,51 @@
|
||||
name: api-governance
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "src/Api/**"
|
||||
- ".spectral.yaml"
|
||||
- "package.json"
|
||||
pull_request:
|
||||
paths:
|
||||
- "src/Api/**"
|
||||
- ".spectral.yaml"
|
||||
- "package.json"
|
||||
|
||||
jobs:
|
||||
spectral-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
- name: Install npm deps
|
||||
run: npm install --ignore-scripts --no-progress
|
||||
- name: Compose aggregate OpenAPI
|
||||
run: npm run api:compose
|
||||
- name: Validate examples coverage
|
||||
run: npm run api:examples
|
||||
- name: Compatibility diff (previous commit)
|
||||
run: |
|
||||
set -e
|
||||
if git show HEAD~1:src/Api/StellaOps.Api.OpenApi/stella.yaml > /tmp/stella-prev.yaml 2>/dev/null; then
|
||||
node scripts/api-compat-diff.mjs /tmp/stella-prev.yaml src/Api/StellaOps.Api.OpenApi/stella.yaml --output text --fail-on-breaking
|
||||
else
|
||||
echo "[api:compat] previous stella.yaml not found; skipping"
|
||||
fi
|
||||
- name: Compatibility diff (baseline)
|
||||
run: |
|
||||
set -e
|
||||
if [ -f src/Api/StellaOps.Api.OpenApi/baselines/stella-baseline.yaml ]; then
|
||||
node scripts/api-compat-diff.mjs src/Api/StellaOps.Api.OpenApi/baselines/stella-baseline.yaml src/Api/StellaOps.Api.OpenApi/stella.yaml --output text
|
||||
else
|
||||
echo "[api:compat] baseline file missing; skipping"
|
||||
fi
|
||||
- name: Generate changelog
|
||||
run: npm run api:changelog
|
||||
- name: Spectral lint (fail on warning+)
|
||||
run: npm run api:lint
|
||||
128
.gitea/workflows/artifact-signing.yml
Normal file
128
.gitea/workflows/artifact-signing.yml
Normal file
@@ -0,0 +1,128 @@
|
||||
name: Artifact Signing
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
artifact_path:
|
||||
description: 'Path to artifact to sign'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
env:
|
||||
COSIGN_VERSION: 'v2.2.0'
|
||||
|
||||
jobs:
|
||||
sign-containers:
|
||||
name: Sign Container Images
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: ${{ env.COSIGN_VERSION }}
|
||||
|
||||
- name: Log in to registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Sign images (keyless)
|
||||
if: ${{ !env.COSIGN_PRIVATE_KEY_B64 }}
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "1"
|
||||
run: |
|
||||
IMAGES=(
|
||||
"ghcr.io/${{ github.repository }}/concelier"
|
||||
"ghcr.io/${{ github.repository }}/scanner"
|
||||
"ghcr.io/${{ github.repository }}/authority"
|
||||
)
|
||||
for img in "${IMAGES[@]}"; do
|
||||
if docker manifest inspect "${img}:${{ github.ref_name }}" > /dev/null 2>&1; then
|
||||
echo "Signing ${img}:${{ github.ref_name }}..."
|
||||
cosign sign --yes "${img}:${{ github.ref_name }}"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Sign images (with key)
|
||||
if: ${{ env.COSIGN_PRIVATE_KEY_B64 }}
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
run: |
|
||||
echo "$COSIGN_PRIVATE_KEY" | base64 -d > /tmp/cosign.key
|
||||
IMAGES=(
|
||||
"ghcr.io/${{ github.repository }}/concelier"
|
||||
"ghcr.io/${{ github.repository }}/scanner"
|
||||
"ghcr.io/${{ github.repository }}/authority"
|
||||
)
|
||||
for img in "${IMAGES[@]}"; do
|
||||
if docker manifest inspect "${img}:${{ github.ref_name }}" > /dev/null 2>&1; then
|
||||
echo "Signing ${img}:${{ github.ref_name }}..."
|
||||
cosign sign --key /tmp/cosign.key "${img}:${{ github.ref_name }}"
|
||||
fi
|
||||
done
|
||||
rm -f /tmp/cosign.key
|
||||
|
||||
sign-sbom:
|
||||
name: Sign SBOM Artifacts
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: ${{ env.COSIGN_VERSION }}
|
||||
|
||||
- name: Generate and sign SBOM
|
||||
run: |
|
||||
# Generate SBOM using syft
|
||||
if command -v syft &> /dev/null; then
|
||||
syft . -o cyclonedx-json > sbom.cdx.json
|
||||
cosign sign-blob --yes sbom.cdx.json --output-signature sbom.cdx.json.sig
|
||||
else
|
||||
echo "syft not installed, skipping SBOM generation"
|
||||
fi
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signed-sbom
|
||||
path: |
|
||||
sbom.cdx.json
|
||||
sbom.cdx.json.sig
|
||||
if-no-files-found: ignore
|
||||
|
||||
verify-signatures:
|
||||
name: Verify Existing Signatures
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: ${{ env.COSIGN_VERSION }}
|
||||
|
||||
- name: Verify DSSE envelopes
|
||||
run: |
|
||||
find . -name "*.dsse" -o -name "*.dsse.json" | while read f; do
|
||||
echo "Checking $f..."
|
||||
# Basic JSON validation
|
||||
if ! jq empty "$f" 2>/dev/null; then
|
||||
echo "Warning: Invalid JSON in $f"
|
||||
fi
|
||||
done
|
||||
29
.gitea/workflows/attestation-bundle.yml
Normal file
29
.gitea/workflows/attestation-bundle.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: attestation-bundle
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
attest_dir:
|
||||
description: "Directory containing attestation artefacts"
|
||||
required: true
|
||||
default: "out/attest"
|
||||
|
||||
jobs:
|
||||
bundle:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Build bundle
|
||||
run: |
|
||||
chmod +x scripts/attest/build-attestation-bundle.sh
|
||||
scripts/attest/build-attestation-bundle.sh "${{ github.event.inputs.attest_dir }}"
|
||||
|
||||
- name: Upload bundle
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: attestation-bundle
|
||||
path: out/attest-bundles/**
|
||||
@@ -58,6 +58,9 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Resolve Authority configuration
|
||||
id: config
|
||||
run: |
|
||||
|
||||
30
.gitea/workflows/bench-determinism.yml
Normal file
30
.gitea/workflows/bench-determinism.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
name: bench-determinism
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
bench-determinism:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Run determinism bench
|
||||
env:
|
||||
BENCH_DETERMINISM_THRESHOLD: "0.95"
|
||||
run: |
|
||||
chmod +x scripts/bench/determinism-run.sh
|
||||
scripts/bench/determinism-run.sh
|
||||
|
||||
- name: Upload determinism artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bench-determinism
|
||||
path: out/bench-determinism/**
|
||||
@@ -21,6 +21,8 @@ on:
|
||||
- 'docs/**'
|
||||
- 'scripts/**'
|
||||
- '.gitea/workflows/**'
|
||||
schedule:
|
||||
- cron: '0 5 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force_deploy:
|
||||
@@ -28,9 +30,14 @@ on:
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
excititor_batch:
|
||||
description: 'Run Excititor batch-ingest validation suite'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100-rc.1.25451.107'
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
BUILD_CONFIGURATION: Release
|
||||
CI_CACHE_ROOT: /data/.cache/stella-ops/feedser
|
||||
RUNNER_TOOL_CACHE: /toolcache
|
||||
@@ -48,6 +55,18 @@ jobs:
|
||||
tar -xzf /tmp/helm.tgz -C /tmp
|
||||
sudo install -m 0755 /tmp/linux-amd64/helm /usr/local/bin/helm
|
||||
|
||||
- name: Validate Helm chart rendering
|
||||
run: |
|
||||
set -euo pipefail
|
||||
CHART_PATH="deploy/helm/stellaops"
|
||||
helm lint "$CHART_PATH"
|
||||
for values in values.yaml values-dev.yaml values-stage.yaml values-prod.yaml values-airgap.yaml values-mirror.yaml; do
|
||||
release="stellaops-${values%.*}"
|
||||
echo "::group::Helm template ${release} (${values})"
|
||||
helm template "$release" "$CHART_PATH" -f "$CHART_PATH/$values" >/dev/null
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
- name: Validate deployment profiles
|
||||
run: ./deploy/tools/validate-profiles.sh
|
||||
|
||||
@@ -65,6 +84,17 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Verify binary layout
|
||||
run: scripts/verify-binaries.sh
|
||||
|
||||
- name: Ensure binary manifests are up to date
|
||||
run: |
|
||||
python3 scripts/update-binary-manifests.py
|
||||
git diff --exit-code local-nugets/manifest.json vendor/manifest.json offline/feeds/manifest.json
|
||||
|
||||
- name: Ensure Mongo test URI configured
|
||||
run: |
|
||||
if [ -z "${STELLAOPS_TEST_MONGO_URI:-}" ]; then
|
||||
@@ -81,6 +111,10 @@ jobs:
|
||||
- name: Validate telemetry storage configuration
|
||||
run: python3 ops/devops/telemetry/validate_storage_stack.py
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: |
|
||||
python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Telemetry tenant isolation smoke
|
||||
env:
|
||||
COMPOSE_DIR: ${GITHUB_WORKSPACE}/deploy/compose
|
||||
@@ -150,6 +184,37 @@ jobs:
|
||||
--logger "trx;LogFileName=stellaops-concelier-tests.trx" \
|
||||
--results-directory "$TEST_RESULTS_DIR"
|
||||
|
||||
- name: Run PostgreSQL storage integration tests (Testcontainers)
|
||||
env:
|
||||
POSTGRES_TEST_IMAGE: postgres:16-alpine
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "$TEST_RESULTS_DIR"
|
||||
PROJECTS=(
|
||||
src/__Libraries/__Tests/StellaOps.Infrastructure.Postgres.Tests/StellaOps.Infrastructure.Postgres.Tests.csproj
|
||||
src/Authority/__Tests/StellaOps.Authority.Storage.Postgres.Tests/StellaOps.Authority.Storage.Postgres.Tests.csproj
|
||||
src/Scheduler/__Tests/StellaOps.Scheduler.Storage.Postgres.Tests/StellaOps.Scheduler.Storage.Postgres.Tests.csproj
|
||||
src/Concelier/__Tests/StellaOps.Concelier.Storage.Postgres.Tests/StellaOps.Concelier.Storage.Postgres.Tests.csproj
|
||||
src/Excititor/__Tests/StellaOps.Excititor.Storage.Postgres.Tests/StellaOps.Excititor.Storage.Postgres.Tests.csproj
|
||||
src/Notify/__Tests/StellaOps.Notify.Storage.Postgres.Tests/StellaOps.Notify.Storage.Postgres.Tests.csproj
|
||||
src/Policy/__Tests/StellaOps.Policy.Storage.Postgres.Tests/StellaOps.Policy.Storage.Postgres.Tests.csproj
|
||||
)
|
||||
for project in "${PROJECTS[@]}"; do
|
||||
name="$(basename "${project%.*}")"
|
||||
dotnet test "$project" \
|
||||
--configuration $BUILD_CONFIGURATION \
|
||||
--logger "trx;LogFileName=${name}.trx" \
|
||||
--results-directory "$TEST_RESULTS_DIR"
|
||||
done
|
||||
|
||||
- name: Run TimelineIndexer tests (EB1 evidence linkage gate)
|
||||
run: |
|
||||
mkdir -p "$TEST_RESULTS_DIR"
|
||||
dotnet test src/TimelineIndexer/StellaOps.TimelineIndexer/StellaOps.TimelineIndexer.sln \
|
||||
--configuration $BUILD_CONFIGURATION \
|
||||
--logger "trx;LogFileName=timelineindexer-tests.trx" \
|
||||
--results-directory "$TEST_RESULTS_DIR"
|
||||
|
||||
- name: Lint policy DSL samples
|
||||
run: dotnet run --project tools/PolicyDslValidator/PolicyDslValidator.csproj -- --strict docs/examples/policies/*.yaml
|
||||
|
||||
@@ -280,6 +345,56 @@ PY
|
||||
--logger "trx;LogFileName=stellaops-scanner-lang-tests.trx" \
|
||||
--results-directory "$TEST_RESULTS_DIR"
|
||||
|
||||
- name: Build and test Router components
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ROUTER_PROJECTS=(
|
||||
src/__Libraries/StellaOps.Router.Common/StellaOps.Router.Common.csproj
|
||||
src/__Libraries/StellaOps.Router.Config/StellaOps.Router.Config.csproj
|
||||
src/__Libraries/StellaOps.Router.Transport.InMemory/StellaOps.Router.Transport.InMemory.csproj
|
||||
src/__Libraries/StellaOps.Router.Transport.Tcp/StellaOps.Router.Transport.Tcp.csproj
|
||||
src/__Libraries/StellaOps.Router.Transport.Tls/StellaOps.Router.Transport.Tls.csproj
|
||||
src/__Libraries/StellaOps.Router.Transport.Udp/StellaOps.Router.Transport.Udp.csproj
|
||||
src/__Libraries/StellaOps.Router.Transport.RabbitMq/StellaOps.Router.Transport.RabbitMq.csproj
|
||||
src/__Libraries/StellaOps.Microservice/StellaOps.Microservice.csproj
|
||||
src/__Libraries/StellaOps.Microservice.SourceGen/StellaOps.Microservice.SourceGen.csproj
|
||||
)
|
||||
for project in "${ROUTER_PROJECTS[@]}"; do
|
||||
echo "::group::Build $project"
|
||||
dotnet build "$project" --configuration $BUILD_CONFIGURATION --no-restore -warnaserror
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
- name: Run Router and Microservice tests
|
||||
run: |
|
||||
mkdir -p "$TEST_RESULTS_DIR"
|
||||
ROUTER_TEST_PROJECTS=(
|
||||
# Core Router libraries
|
||||
src/__Libraries/__Tests/StellaOps.Router.Common.Tests/StellaOps.Router.Common.Tests.csproj
|
||||
src/__Libraries/__Tests/StellaOps.Router.Config.Tests/StellaOps.Router.Config.Tests.csproj
|
||||
# Transport layers
|
||||
src/__Libraries/__Tests/StellaOps.Router.Transport.InMemory.Tests/StellaOps.Router.Transport.InMemory.Tests.csproj
|
||||
src/__Libraries/__Tests/StellaOps.Router.Transport.Tcp.Tests/StellaOps.Router.Transport.Tcp.Tests.csproj
|
||||
src/__Libraries/__Tests/StellaOps.Router.Transport.Tls.Tests/StellaOps.Router.Transport.Tls.Tests.csproj
|
||||
src/__Libraries/__Tests/StellaOps.Router.Transport.Udp.Tests/StellaOps.Router.Transport.Udp.Tests.csproj
|
||||
# Microservice SDK
|
||||
src/__Libraries/__Tests/StellaOps.Microservice.Tests/StellaOps.Microservice.Tests.csproj
|
||||
src/__Libraries/__Tests/StellaOps.Microservice.SourceGen.Tests/StellaOps.Microservice.SourceGen.Tests.csproj
|
||||
# Integration tests
|
||||
src/__Libraries/__Tests/StellaOps.Router.Integration.Tests/StellaOps.Router.Integration.Tests.csproj
|
||||
# Gateway tests
|
||||
src/Gateway/__Tests/StellaOps.Gateway.WebService.Tests/StellaOps.Gateway.WebService.Tests.csproj
|
||||
)
|
||||
for project in "${ROUTER_TEST_PROJECTS[@]}"; do
|
||||
name="$(basename "${project%.*}")"
|
||||
echo "::group::Test $name"
|
||||
dotnet test "$project" \
|
||||
--configuration $BUILD_CONFIGURATION \
|
||||
--logger "trx;LogFileName=${name}.trx" \
|
||||
--results-directory "$TEST_RESULTS_DIR"
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
- name: Run scanner analyzer performance benchmark
|
||||
env:
|
||||
PERF_OUTPUT_DIR: ${{ github.workspace }}/artifacts/perf/scanner-analyzers
|
||||
@@ -442,6 +557,15 @@ PY
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
- name: Run console endpoint tests
|
||||
run: |
|
||||
mkdir -p "$TEST_RESULTS_DIR"
|
||||
dotnet test src/Authority/StellaOps.Authority/StellaOps.Authority.Tests/StellaOps.Authority.Tests.csproj \
|
||||
--configuration $BUILD_CONFIGURATION \
|
||||
--logger "trx;LogFileName=console-endpoints.trx" \
|
||||
--results-directory "$TEST_RESULTS_DIR" \
|
||||
--filter ConsoleEndpointsTests
|
||||
|
||||
- name: Upload test results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -451,6 +575,247 @@ PY
|
||||
if-no-files-found: ignore
|
||||
retention-days: 7
|
||||
|
||||
# ============================================================================
|
||||
# Quality Gates Foundation (Sprint 0350)
|
||||
# ============================================================================
|
||||
quality-gates:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-test
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Reachability quality gate
|
||||
id: reachability
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Computing reachability metrics"
|
||||
if [ -f scripts/ci/compute-reachability-metrics.sh ]; then
|
||||
chmod +x scripts/ci/compute-reachability-metrics.sh
|
||||
METRICS=$(./scripts/ci/compute-reachability-metrics.sh --dry-run 2>/dev/null || echo '{}')
|
||||
echo "metrics=$METRICS" >> $GITHUB_OUTPUT
|
||||
echo "Reachability metrics: $METRICS"
|
||||
else
|
||||
echo "Reachability script not found, skipping"
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: TTFS regression gate
|
||||
id: ttfs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Computing TTFS metrics"
|
||||
if [ -f scripts/ci/compute-ttfs-metrics.sh ]; then
|
||||
chmod +x scripts/ci/compute-ttfs-metrics.sh
|
||||
METRICS=$(./scripts/ci/compute-ttfs-metrics.sh --dry-run 2>/dev/null || echo '{}')
|
||||
echo "metrics=$METRICS" >> $GITHUB_OUTPUT
|
||||
echo "TTFS metrics: $METRICS"
|
||||
else
|
||||
echo "TTFS script not found, skipping"
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Performance SLO gate
|
||||
id: slo
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Enforcing performance SLOs"
|
||||
if [ -f scripts/ci/enforce-performance-slos.sh ]; then
|
||||
chmod +x scripts/ci/enforce-performance-slos.sh
|
||||
./scripts/ci/enforce-performance-slos.sh --warn-only || true
|
||||
else
|
||||
echo "Performance SLO script not found, skipping"
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: RLS policy validation
|
||||
id: rls
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Validating RLS policies"
|
||||
if [ -f deploy/postgres-validation/001_validate_rls.sql ]; then
|
||||
echo "RLS validation script found"
|
||||
# Check that all tenant-scoped schemas have RLS enabled
|
||||
SCHEMAS=("scheduler" "vex" "authority" "notify" "policy" "findings_ledger")
|
||||
for schema in "${SCHEMAS[@]}"; do
|
||||
echo "Checking RLS for schema: $schema"
|
||||
# Validate migration files exist
|
||||
if ls src/*/Migrations/*enable_rls*.sql 2>/dev/null | grep -q "$schema"; then
|
||||
echo " ✓ RLS migration exists for $schema"
|
||||
fi
|
||||
done
|
||||
echo "RLS validation passed (static check)"
|
||||
else
|
||||
echo "RLS validation script not found, skipping"
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Upload quality gate results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: quality-gate-results
|
||||
path: |
|
||||
scripts/ci/*.json
|
||||
scripts/ci/*.yaml
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
security-testing:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-test
|
||||
if: github.event_name == 'pull_request' || github.event_name == 'schedule'
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore tests/security/StellaOps.Security.Tests/StellaOps.Security.Tests.csproj
|
||||
|
||||
- name: Run OWASP security tests
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Running security tests"
|
||||
dotnet test tests/security/StellaOps.Security.Tests/StellaOps.Security.Tests.csproj \
|
||||
--no-restore \
|
||||
--logger "trx;LogFileName=security-tests.trx" \
|
||||
--results-directory ./security-test-results \
|
||||
--filter "Category=Security" \
|
||||
--verbosity normal
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Upload security test results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: security-test-results
|
||||
path: security-test-results/
|
||||
if-no-files-found: ignore
|
||||
retention-days: 30
|
||||
|
||||
mutation-testing:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-test
|
||||
if: github.event_name == 'schedule' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'mutation-test'))
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore tools
|
||||
run: dotnet tool restore
|
||||
|
||||
- name: Run mutation tests - Scanner.Core
|
||||
id: scanner-mutation
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Mutation testing Scanner.Core"
|
||||
cd src/Scanner/__Libraries/StellaOps.Scanner.Core
|
||||
dotnet stryker --reporter json --reporter html --output ../../../mutation-results/scanner-core || echo "MUTATION_FAILED=true" >> $GITHUB_ENV
|
||||
echo "::endgroup::"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run mutation tests - Policy.Engine
|
||||
id: policy-mutation
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Mutation testing Policy.Engine"
|
||||
cd src/Policy/__Libraries/StellaOps.Policy
|
||||
dotnet stryker --reporter json --reporter html --output ../../../mutation-results/policy-engine || echo "MUTATION_FAILED=true" >> $GITHUB_ENV
|
||||
echo "::endgroup::"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run mutation tests - Authority.Core
|
||||
id: authority-mutation
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "::group::Mutation testing Authority.Core"
|
||||
cd src/Authority/StellaOps.Authority
|
||||
dotnet stryker --reporter json --reporter html --output ../../mutation-results/authority-core || echo "MUTATION_FAILED=true" >> $GITHUB_ENV
|
||||
echo "::endgroup::"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload mutation results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mutation-testing-results
|
||||
path: mutation-results/
|
||||
if-no-files-found: ignore
|
||||
retention-days: 30
|
||||
|
||||
- name: Check mutation thresholds
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Checking mutation score thresholds..."
|
||||
# Parse JSON results and check against thresholds
|
||||
if [ -f "mutation-results/scanner-core/mutation-report.json" ]; then
|
||||
SCORE=$(jq '.mutationScore // 0' mutation-results/scanner-core/mutation-report.json)
|
||||
echo "Scanner.Core mutation score: $SCORE%"
|
||||
if (( $(echo "$SCORE < 65" | bc -l) )); then
|
||||
echo "::error::Scanner.Core mutation score below threshold"
|
||||
fi
|
||||
fi
|
||||
|
||||
sealed-mode-ci:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-test
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
env:
|
||||
COMPOSE_PROJECT_NAME: sealedmode
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Login to registry
|
||||
if: ${{ secrets.REGISTRY_USERNAME != '' && secrets.REGISTRY_PASSWORD != '' }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: registry.stella-ops.org
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Run sealed-mode CI harness
|
||||
working-directory: ops/devops/sealed-mode-ci
|
||||
env:
|
||||
COMPOSE_PROJECT_NAME: sealedmode
|
||||
run: |
|
||||
set -euo pipefail
|
||||
./run-sealed-ci.sh
|
||||
|
||||
- name: Upload sealed-mode CI artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sealed-mode-ci
|
||||
path: ops/devops/sealed-mode-ci/artifacts/sealed-mode-ci
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
|
||||
authority-container:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-test
|
||||
@@ -464,6 +829,41 @@ PY
|
||||
- name: Build Authority container image
|
||||
run: docker build -f ops/authority/Dockerfile -t stellaops-authority:ci .
|
||||
|
||||
excititor-batch-validation:
|
||||
needs: build-test
|
||||
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.excititor_batch == 'true')
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
BATCH_RESULTS_DIR: ${{ github.workspace }}/artifacts/test-results/excititor-batch
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Run Excititor batch ingest validation suite
|
||||
env:
|
||||
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "$BATCH_RESULTS_DIR"
|
||||
dotnet test src/Excititor/__Tests/StellaOps.Excititor.WebService.Tests/StellaOps.Excititor.WebService.Tests.csproj \
|
||||
--configuration $BUILD_CONFIGURATION \
|
||||
--filter "Category=BatchIngestValidation" \
|
||||
--logger "trx;LogFileName=excititor-batch.trx" \
|
||||
--results-directory "$BATCH_RESULTS_DIR"
|
||||
|
||||
- name: Upload Excititor batch ingest results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: excititor-batch-ingest-results
|
||||
path: ${{ env.BATCH_RESULTS_DIR }}
|
||||
|
||||
docs:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
|
||||
48
.gitea/workflows/cli-build.yml
Normal file
48
.gitea/workflows/cli-build.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
name: cli-build
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
rids:
|
||||
description: "Comma-separated RIDs (e.g., linux-x64,win-x64,osx-arm64)"
|
||||
required: false
|
||||
default: "linux-x64,win-x64,osx-arm64"
|
||||
config:
|
||||
description: "Build configuration"
|
||||
required: false
|
||||
default: "Release"
|
||||
sign:
|
||||
description: "Enable cosign signing (requires COSIGN_KEY)"
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.100"
|
||||
|
||||
- name: Install syft (SBOM)
|
||||
uses: anchore/sbom-action/download-syft@v0
|
||||
|
||||
- name: Build CLI artifacts
|
||||
run: |
|
||||
chmod +x scripts/cli/build-cli.sh
|
||||
RIDS="${{ github.event.inputs.rids }}" CONFIG="${{ github.event.inputs.config }}" SBOM_TOOL=syft SIGN="${{ github.event.inputs.sign }}" COSIGN_KEY="${{ secrets.COSIGN_KEY }}" scripts/cli/build-cli.sh
|
||||
|
||||
- name: List artifacts
|
||||
run: find out/cli -maxdepth 3 -type f -print
|
||||
|
||||
- name: Upload CLI artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: stella-cli
|
||||
path: out/cli/**
|
||||
47
.gitea/workflows/cli-chaos-parity.yml
Normal file
47
.gitea/workflows/cli-chaos-parity.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
name: cli-chaos-parity
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
chaos:
|
||||
description: "Run chaos smoke (true/false)"
|
||||
required: false
|
||||
default: "true"
|
||||
parity:
|
||||
description: "Run parity diff (true/false)"
|
||||
required: false
|
||||
default: "true"
|
||||
|
||||
jobs:
|
||||
cli-checks:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.100"
|
||||
|
||||
- name: Chaos smoke
|
||||
if: ${{ github.event.inputs.chaos == 'true' }}
|
||||
run: |
|
||||
chmod +x scripts/cli/chaos-smoke.sh
|
||||
scripts/cli/chaos-smoke.sh
|
||||
|
||||
- name: Parity diff
|
||||
if: ${{ github.event.inputs.parity == 'true' }}
|
||||
run: |
|
||||
chmod +x scripts/cli/parity-diff.sh
|
||||
scripts/cli/parity-diff.sh
|
||||
|
||||
- name: Upload evidence
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cli-chaos-parity
|
||||
path: |
|
||||
out/cli-chaos/**
|
||||
out/cli-goldens/**
|
||||
47
.gitea/workflows/concelier-attestation-tests.yml
Normal file
47
.gitea/workflows/concelier-attestation-tests.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Concelier Attestation Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'src/Concelier/**'
|
||||
- '.gitea/workflows/concelier-attestation-tests.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/Concelier/**'
|
||||
- '.gitea/workflows/concelier-attestation-tests.yml'
|
||||
|
||||
jobs:
|
||||
attestation-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET 10 preview
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '10.0.100'
|
||||
|
||||
- name: Restore Concelier solution
|
||||
run: dotnet restore src/Concelier/StellaOps.Concelier.sln
|
||||
|
||||
- name: Build WebService Tests (no analyzers)
|
||||
run: dotnet build src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj -c Release -p:DisableAnalyzers=true
|
||||
|
||||
- name: Run WebService attestation test
|
||||
run: dotnet test src/Concelier/__Tests/StellaOps.Concelier.WebService.Tests/StellaOps.Concelier.WebService.Tests.csproj -c Release --filter InternalAttestationVerify --no-build --logger trx --results-directory TestResults
|
||||
|
||||
- name: Build Core Tests (no analyzers)
|
||||
run: dotnet build src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj -c Release -p:DisableAnalyzers=true
|
||||
|
||||
- name: Run Core attestation builder tests
|
||||
run: dotnet test src/Concelier/__Tests/StellaOps.Concelier.Core.Tests/StellaOps.Concelier.Core.Tests.csproj -c Release --filter EvidenceBundleAttestationBuilderTests --no-build --logger trx --results-directory TestResults
|
||||
|
||||
- name: Upload TRX results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: concelier-attestation-tests-trx
|
||||
path: '**/TestResults/*.trx'
|
||||
32
.gitea/workflows/concelier-store-aoc-19-005.yml
Normal file
32
.gitea/workflows/concelier-store-aoc-19-005.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Concelier STORE-AOC-19-005 Dataset
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
build-dataset:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/out/linksets
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y zstd
|
||||
|
||||
- name: Build dataset tarball
|
||||
run: |
|
||||
chmod +x scripts/concelier/build-store-aoc-19-005-dataset.sh scripts/concelier/test-store-aoc-19-005-dataset.sh
|
||||
scripts/concelier/build-store-aoc-19-005-dataset.sh "${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst"
|
||||
|
||||
- name: Validate dataset
|
||||
run: scripts/concelier/test-store-aoc-19-005-dataset.sh "${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst"
|
||||
|
||||
- name: Upload dataset artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: concelier-store-aoc-19-005-dataset
|
||||
path: |
|
||||
${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst
|
||||
${ARTIFACT_DIR}/linksets-stage-backfill.tar.zst.sha256
|
||||
64
.gitea/workflows/console-ci.yml
Normal file
64
.gitea/workflows/console-ci.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
name: console-ci
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/Web/**'
|
||||
- '.gitea/workflows/console-ci.yml'
|
||||
- 'ops/devops/console/**'
|
||||
|
||||
jobs:
|
||||
lint-test-build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: src/Web/StellaOps.Web
|
||||
env:
|
||||
PLAYWRIGHT_BROWSERS_PATH: ~/.cache/ms-playwright
|
||||
CI: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: npm
|
||||
cache-dependency-path: src/Web/StellaOps.Web/package-lock.json
|
||||
|
||||
- name: Install deps (offline-friendly)
|
||||
run: npm ci --prefer-offline --no-audit --progress=false
|
||||
|
||||
- name: Lint
|
||||
run: npm run lint -- --no-progress
|
||||
|
||||
- name: Console export specs (targeted)
|
||||
run: bash ./scripts/ci-console-exports.sh
|
||||
continue-on-error: true
|
||||
|
||||
- name: Unit tests
|
||||
run: npm run test:ci
|
||||
env:
|
||||
CHROME_BIN: chromium
|
||||
|
||||
- name: Build
|
||||
run: npm run build -- --configuration=production --progress=false
|
||||
|
||||
- name: Collect artifacts
|
||||
if: always()
|
||||
run: |
|
||||
mkdir -p ../artifacts
|
||||
cp -r dist ../artifacts/dist || true
|
||||
cp -r coverage ../artifacts/coverage || true
|
||||
find . -maxdepth 3 -type f -name "*.xml" -o -name "*.trx" -o -name "*.json" -path "*test*" -print0 | xargs -0 -I{} cp --parents {} ../artifacts 2>/dev/null || true
|
||||
|
||||
- name: Upload artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: console-ci-${{ github.run_id }}
|
||||
path: artifacts
|
||||
retention-days: 14
|
||||
32
.gitea/workflows/console-runner-image.yml
Normal file
32
.gitea/workflows/console-runner-image.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
name: console-runner-image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'ops/devops/console/**'
|
||||
- '.gitea/workflows/console-runner-image.yml'
|
||||
|
||||
jobs:
|
||||
build-runner-image:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build runner image tarball (baked caches)
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
chmod +x ops/devops/console/build-runner-image.sh ops/devops/console/build-runner-image-ci.sh
|
||||
ops/devops/console/build-runner-image-ci.sh
|
||||
|
||||
- name: Upload runner image artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: console-runner-image-${{ github.run_id }}
|
||||
path: ops/devops/artifacts/console-runner/
|
||||
retention-days: 14
|
||||
89
.gitea/workflows/containers-multiarch.yml
Normal file
89
.gitea/workflows/containers-multiarch.yml
Normal file
@@ -0,0 +1,89 @@
|
||||
name: containers-multiarch
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image:
|
||||
description: "Image tag (e.g., ghcr.io/stella-ops/example:edge)"
|
||||
required: true
|
||||
context:
|
||||
description: "Build context directory"
|
||||
required: true
|
||||
default: "."
|
||||
platforms:
|
||||
description: "Platforms (comma-separated)"
|
||||
required: false
|
||||
default: "linux/amd64,linux/arm64"
|
||||
push:
|
||||
description: "Push to registry"
|
||||
required: false
|
||||
default: "false"
|
||||
|
||||
jobs:
|
||||
build-multiarch:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Install syft (SBOM)
|
||||
uses: anchore/sbom-action/download-syft@v0
|
||||
|
||||
- name: Login to ghcr (optional)
|
||||
if: ${{ github.event.inputs.push == 'true' && secrets.GHCR_TOKEN != '' }}
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
|
||||
- name: Run multi-arch build
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "1"
|
||||
run: |
|
||||
chmod +x scripts/buildx/build-multiarch.sh
|
||||
extra=""
|
||||
if [[ "${{ github.event.inputs.push }}" == "true" ]]; then extra="--push"; fi
|
||||
scripts/buildx/build-multiarch.sh \
|
||||
"${{ github.event.inputs.image }}" \
|
||||
"${{ github.event.inputs.context }}" \
|
||||
--platform "${{ github.event.inputs.platforms }}" \
|
||||
--sbom syft ${extra}
|
||||
|
||||
- name: Build air-gap bundle
|
||||
run: |
|
||||
chmod +x scripts/buildx/build-airgap-bundle.sh
|
||||
scripts/buildx/build-airgap-bundle.sh "${{ github.event.inputs.image }}"
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: buildx-${{ github.event.inputs.image }}
|
||||
path: out/buildx/**
|
||||
|
||||
- name: Inspect built image archive
|
||||
run: |
|
||||
set -e
|
||||
ls -lh out/buildx/
|
||||
find out/buildx -name "image.oci" -print -exec sh -c 'tar -tf "$1" | head' _ {} \;
|
||||
|
||||
- name: Upload air-gap bundle
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: bundle-${{ github.event.inputs.image }}
|
||||
path: out/bundles/**
|
||||
|
||||
- name: Inspect remote image (if pushed)
|
||||
if: ${{ github.event.inputs.push == 'true' }}
|
||||
run: |
|
||||
docker buildx imagetools inspect "${{ github.event.inputs.image }}"
|
||||
41
.gitea/workflows/crypto-sim-smoke.yml
Normal file
41
.gitea/workflows/crypto-sim-smoke.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
name: crypto-sim-smoke
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "ops/crypto/sim-crypto-service/**"
|
||||
- "ops/crypto/sim-crypto-smoke/**"
|
||||
- "scripts/crypto/run-sim-smoke.ps1"
|
||||
- "docs/security/crypto-simulation-services.md"
|
||||
- ".gitea/workflows/crypto-sim-smoke.yml"
|
||||
|
||||
jobs:
|
||||
sim-smoke:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.x"
|
||||
|
||||
- name: Build sim service and smoke harness
|
||||
run: |
|
||||
dotnet build ops/crypto/sim-crypto-service/SimCryptoService.csproj -c Release
|
||||
dotnet build ops/crypto/sim-crypto-smoke/SimCryptoSmoke.csproj -c Release
|
||||
|
||||
- name: Run smoke (sim profile: sm)
|
||||
env:
|
||||
ASPNETCORE_URLS: http://localhost:5000
|
||||
STELLAOPS_CRYPTO_SIM_URL: http://localhost:5000
|
||||
SIM_PROFILE: sm
|
||||
run: |
|
||||
set -euo pipefail
|
||||
dotnet run --project ops/crypto/sim-crypto-service/SimCryptoService.csproj --no-build -c Release &
|
||||
service_pid=$!
|
||||
sleep 6
|
||||
dotnet run --project ops/crypto/sim-crypto-smoke/SimCryptoSmoke.csproj --no-build -c Release
|
||||
kill $service_pid
|
||||
55
.gitea/workflows/cryptopro-linux-csp.yml
Normal file
55
.gitea/workflows/cryptopro-linux-csp.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
name: cryptopro-linux-csp
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- 'ops/cryptopro/linux-csp-service/**'
|
||||
- 'opt/cryptopro/downloads/**'
|
||||
- '.gitea/workflows/cryptopro-linux-csp.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'ops/cryptopro/linux-csp-service/**'
|
||||
- 'opt/cryptopro/downloads/**'
|
||||
- '.gitea/workflows/cryptopro-linux-csp.yml'
|
||||
|
||||
env:
|
||||
IMAGE_NAME: cryptopro-linux-csp
|
||||
DOCKERFILE: ops/cryptopro/linux-csp-service/Dockerfile
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build image (accept EULA explicitly)
|
||||
run: |
|
||||
docker build -t $IMAGE_NAME \
|
||||
--build-arg CRYPTOPRO_ACCEPT_EULA=1 \
|
||||
-f $DOCKERFILE .
|
||||
|
||||
- name: Run container
|
||||
run: |
|
||||
docker run -d --rm --name $IMAGE_NAME -p 18080:8080 $IMAGE_NAME
|
||||
for i in {1..20}; do
|
||||
if curl -sf http://127.0.0.1:18080/health >/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
echo "Service failed to start" && exit 1
|
||||
|
||||
- name: Test endpoints
|
||||
run: |
|
||||
curl -sf http://127.0.0.1:18080/health
|
||||
curl -sf http://127.0.0.1:18080/license || true
|
||||
curl -sf -X POST http://127.0.0.1:18080/hash \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"data_b64":"SGVsbG8="}'
|
||||
|
||||
- name: Stop container
|
||||
if: always()
|
||||
run: docker rm -f $IMAGE_NAME || true
|
||||
40
.gitea/workflows/cryptopro-optin.yml
Normal file
40
.gitea/workflows/cryptopro-optin.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
name: cryptopro-optin
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
configuration:
|
||||
description: Build configuration
|
||||
default: Release
|
||||
run_tests:
|
||||
description: Run CryptoPro signer tests (requires CSP installed on runner)
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
cryptopro:
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
STELLAOPS_CRYPTO_PRO_ENABLED: "1"
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET 10 (preview)
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
|
||||
- name: Build CryptoPro plugin
|
||||
run: |
|
||||
dotnet build src/__Libraries/StellaOps.Cryptography.Plugin.CryptoPro/StellaOps.Cryptography.Plugin.CryptoPro.csproj -c ${{ github.event.inputs.configuration || 'Release' }}
|
||||
|
||||
- name: Run CryptoPro signer tests (requires CSP pre-installed)
|
||||
if: ${{ github.event.inputs.run_tests != 'false' }}
|
||||
run: |
|
||||
powershell -File scripts/crypto/run-cryptopro-tests.ps1 -Configuration ${{ github.event.inputs.configuration || 'Release' }}
|
||||
|
||||
# NOTE: This workflow assumes the windows runner already has CryptoPro CSP installed and licensed.
|
||||
# Leave it opt-in to avoid breaking default CI lanes.
|
||||
32
.gitea/workflows/devportal-offline.yml
Normal file
32
.gitea/workflows/devportal-offline.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
name: devportal-offline
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 5 * * *"
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
build-offline:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Node (corepack/pnpm)
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Build devportal (offline bundle)
|
||||
run: |
|
||||
chmod +x scripts/devportal/build-devportal.sh
|
||||
scripts/devportal/build-devportal.sh
|
||||
|
||||
- name: Upload bundle
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: devportal-offline
|
||||
path: out/devportal/**.tgz
|
||||
@@ -29,6 +29,12 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -41,7 +47,7 @@ jobs:
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '10.0.100-rc.2.25502.107'
|
||||
dotnet-version: '10.0.100'
|
||||
|
||||
- name: Link check
|
||||
run: |
|
||||
|
||||
86
.gitea/workflows/evidence-locker.yml
Normal file
86
.gitea/workflows/evidence-locker.yml
Normal file
@@ -0,0 +1,86 @@
|
||||
name: evidence-locker
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
retention_target:
|
||||
description: "Retention days target"
|
||||
required: false
|
||||
default: "180"
|
||||
|
||||
jobs:
|
||||
check-evidence-locker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Emit retention summary
|
||||
env:
|
||||
RETENTION_TARGET: ${{ github.event.inputs.retention_target }}
|
||||
run: |
|
||||
echo "target_retention_days=${RETENTION_TARGET}" > out/evidence-locker/summary.txt
|
||||
|
||||
- name: Upload evidence locker summary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: evidence-locker
|
||||
path: out/evidence-locker/**
|
||||
|
||||
push-zastava-evidence:
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-evidence-locker
|
||||
env:
|
||||
STAGED_DIR: evidence-locker/zastava/2025-12-02
|
||||
MODULE_ROOT: docs/modules/zastava
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Package staged Zastava artefacts
|
||||
run: |
|
||||
test -d "$MODULE_ROOT" || { echo "missing $MODULE_ROOT" >&2; exit 1; }
|
||||
tmpdir=$(mktemp -d)
|
||||
rsync -a --relative \
|
||||
"$MODULE_ROOT/SHA256SUMS" \
|
||||
"$MODULE_ROOT/schemas/" \
|
||||
"$MODULE_ROOT/exports/" \
|
||||
"$MODULE_ROOT/thresholds.yaml" \
|
||||
"$MODULE_ROOT/thresholds.yaml.dsse" \
|
||||
"$MODULE_ROOT/kit/verify.sh" \
|
||||
"$MODULE_ROOT/kit/README.md" \
|
||||
"$MODULE_ROOT/kit/ed25519.pub" \
|
||||
"$MODULE_ROOT/kit/zastava-kit.tzst" \
|
||||
"$MODULE_ROOT/kit/zastava-kit.tzst.dsse" \
|
||||
"$MODULE_ROOT/evidence/README.md" \
|
||||
"$tmpdir/"
|
||||
(cd "$tmpdir/docs/modules/zastava" && sha256sum --check SHA256SUMS)
|
||||
tar --sort=name --mtime="UTC 1970-01-01" --owner=0 --group=0 --numeric-owner \
|
||||
-cf /tmp/zastava-evidence.tar -C "$tmpdir/docs/modules/zastava" .
|
||||
sha256sum /tmp/zastava-evidence.tar
|
||||
|
||||
- name: Upload staged artefacts (fallback)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: zastava-evidence-locker-2025-12-02
|
||||
path: /tmp/zastava-evidence.tar
|
||||
|
||||
- name: Push to Evidence Locker
|
||||
if: ${{ secrets.CI_EVIDENCE_LOCKER_TOKEN != '' && env.EVIDENCE_LOCKER_URL != '' }}
|
||||
env:
|
||||
TOKEN: ${{ secrets.CI_EVIDENCE_LOCKER_TOKEN }}
|
||||
URL: ${{ env.EVIDENCE_LOCKER_URL }}
|
||||
run: |
|
||||
curl -f -X PUT "$URL/zastava/2025-12-02/zastava-evidence.tar" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
--data-binary @/tmp/zastava-evidence.tar
|
||||
|
||||
- name: Skip push (missing secret or URL)
|
||||
if: ${{ secrets.CI_EVIDENCE_LOCKER_TOKEN == '' || env.EVIDENCE_LOCKER_URL == '' }}
|
||||
run: |
|
||||
echo "Locker push skipped: set CI_EVIDENCE_LOCKER_TOKEN and EVIDENCE_LOCKER_URL to enable." >&2
|
||||
85
.gitea/workflows/export-ci.yml
Normal file
85
.gitea/workflows/export-ci.yml
Normal file
@@ -0,0 +1,85 @@
|
||||
name: Export Center CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/ExportCenter/**'
|
||||
- 'ops/devops/export/**'
|
||||
- '.gitea/workflows/export-ci.yml'
|
||||
- 'docs/modules/devops/export-ci-contract.md'
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
paths:
|
||||
- 'src/ExportCenter/**'
|
||||
- 'ops/devops/export/**'
|
||||
- '.gitea/workflows/export-ci.yml'
|
||||
- 'docs/modules/devops/export-ci-contract.md'
|
||||
|
||||
jobs:
|
||||
export-ci:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
MINIO_ACCESS_KEY: exportci
|
||||
MINIO_SECRET_KEY: exportci123
|
||||
BUCKET: export-ci
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Set up .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj
|
||||
|
||||
- name: Bring up MinIO
|
||||
run: |
|
||||
docker compose -f ops/devops/export/minio-compose.yml up -d
|
||||
sleep 5
|
||||
MINIO_ENDPOINT=http://localhost:9000 ops/devops/export/seed-minio.sh
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj -c Release /p:ContinuousIntegrationBuild=true
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
mkdir -p $ARTIFACT_DIR
|
||||
dotnet test src/ExportCenter/__Tests/StellaOps.ExportCenter.Tests/StellaOps.ExportCenter.Tests.csproj -c Release --logger "trx;LogFileName=export-tests.trx" --results-directory $ARTIFACT_DIR
|
||||
|
||||
- name: Trivy/OCI smoke
|
||||
run: ops/devops/export/trivy-smoke.sh
|
||||
|
||||
- name: Schema lint
|
||||
run: |
|
||||
python -m json.tool docs/modules/export-center/schemas/export-profile.schema.json >/dev/null
|
||||
python -m json.tool docs/modules/export-center/schemas/export-manifest.schema.json >/dev/null
|
||||
|
||||
- name: Offline kit verify (fixtures)
|
||||
run: bash docs/modules/export-center/operations/verify-export-kit.sh src/ExportCenter/__fixtures/export-kit
|
||||
|
||||
- name: SBOM
|
||||
run: syft dir:src/ExportCenter -o spdx-json=$ARTIFACT_DIR/exportcenter.spdx.json
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: export-ci-artifacts
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
|
||||
- name: Teardown MinIO
|
||||
if: always()
|
||||
run: docker compose -f ops/devops/export/minio-compose.yml down -v
|
||||
41
.gitea/workflows/export-compat.yml
Normal file
41
.gitea/workflows/export-compat.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
name: export-compat
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
image:
|
||||
description: "Exporter image ref"
|
||||
required: true
|
||||
default: "ghcr.io/stella-ops/exporter:edge"
|
||||
|
||||
jobs:
|
||||
compat:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Trivy
|
||||
uses: aquasecurity/trivy-action@v0.24.0
|
||||
with:
|
||||
version: latest
|
||||
|
||||
- name: Setup Cosign
|
||||
uses: sigstore/cosign-installer@v3.6.0
|
||||
|
||||
- name: Run compatibility checks
|
||||
env:
|
||||
IMAGE: ${{ github.event.inputs.image }}
|
||||
run: |
|
||||
chmod +x scripts/export/trivy-compat.sh
|
||||
chmod +x scripts/export/oci-verify.sh
|
||||
scripts/export/trivy-compat.sh
|
||||
scripts/export/oci-verify.sh
|
||||
|
||||
- name: Upload reports
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: export-compat
|
||||
path: out/export-compat/**
|
||||
46
.gitea/workflows/exporter-ci.yml
Normal file
46
.gitea/workflows/exporter-ci.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
name: exporter-ci
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/ExportCenter/**'
|
||||
- '.gitea/workflows/exporter-ci.yml'
|
||||
|
||||
env:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_NOLOGO: 1
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '10.0.x'
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj --configuration Release --no-restore
|
||||
|
||||
- name: Test
|
||||
run: dotnet test src/ExportCenter/__Tests/StellaOps.ExportCenter.Tests/StellaOps.ExportCenter.Tests.csproj --configuration Release --no-build --verbosity normal
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
dotnet publish src/ExportCenter/StellaOps.ExportCenter.WebService/StellaOps.ExportCenter.WebService.csproj \
|
||||
--configuration Release \
|
||||
--output artifacts/exporter
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: exporter-${{ github.run_id }}
|
||||
path: artifacts/
|
||||
retention-days: 14
|
||||
325
.gitea/workflows/findings-ledger-ci.yml
Normal file
325
.gitea/workflows/findings-ledger-ci.yml
Normal file
@@ -0,0 +1,325 @@
|
||||
# .gitea/workflows/findings-ledger-ci.yml
|
||||
# Findings Ledger CI with RLS migration validation (DEVOPS-LEDGER-TEN-48-001-REL)
|
||||
|
||||
name: Findings Ledger CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/Findings/**'
|
||||
- '.gitea/workflows/findings-ledger-ci.yml'
|
||||
- 'deploy/releases/2025.09-stable.yaml'
|
||||
- 'deploy/releases/2025.09-airgap.yaml'
|
||||
- 'deploy/downloads/manifest.json'
|
||||
- 'ops/devops/release/check_release_manifest.py'
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- 'src/Findings/**'
|
||||
- '.gitea/workflows/findings-ledger-ci.yml'
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
POSTGRES_IMAGE: postgres:16-alpine
|
||||
BUILD_CONFIGURATION: Release
|
||||
|
||||
jobs:
|
||||
build-test:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
TEST_RESULTS_DIR: ${{ github.workspace }}/artifacts/test-results
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET ${{ env.DOTNET_VERSION }}
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Restore dependencies
|
||||
run: |
|
||||
dotnet restore src/Findings/StellaOps.Findings.Ledger/StellaOps.Findings.Ledger.csproj
|
||||
dotnet restore src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/StellaOps.Findings.Ledger.Tests.csproj
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
dotnet build src/Findings/StellaOps.Findings.Ledger/StellaOps.Findings.Ledger.csproj \
|
||||
-c ${{ env.BUILD_CONFIGURATION }} \
|
||||
/p:ContinuousIntegrationBuild=true
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
mkdir -p $TEST_RESULTS_DIR
|
||||
dotnet test src/Findings/__Tests/StellaOps.Findings.Ledger.Tests/StellaOps.Findings.Ledger.Tests.csproj \
|
||||
-c ${{ env.BUILD_CONFIGURATION }} \
|
||||
--logger "trx;LogFileName=ledger-tests.trx" \
|
||||
--results-directory $TEST_RESULTS_DIR
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: ledger-test-results
|
||||
path: ${{ env.TEST_RESULTS_DIR }}
|
||||
|
||||
migration-validation:
|
||||
runs-on: ubuntu-22.04
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
env:
|
||||
POSTGRES_USER: ledgertest
|
||||
POSTGRES_PASSWORD: ledgertest
|
||||
POSTGRES_DB: ledger_test
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
env:
|
||||
PGHOST: localhost
|
||||
PGPORT: 5432
|
||||
PGUSER: ledgertest
|
||||
PGPASSWORD: ledgertest
|
||||
PGDATABASE: ledger_test
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET ${{ env.DOTNET_VERSION }}
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Install PostgreSQL client
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y postgresql-client
|
||||
|
||||
- name: Wait for PostgreSQL
|
||||
run: |
|
||||
until pg_isready -h $PGHOST -p $PGPORT -U $PGUSER; do
|
||||
echo "Waiting for PostgreSQL..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
- name: Apply prerequisite migrations (001-006)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
MIGRATION_DIR="src/Findings/StellaOps.Findings.Ledger/migrations"
|
||||
for migration in 001_initial.sql 002_add_evidence_bundle_ref.sql 002_projection_offsets.sql \
|
||||
003_policy_rationale.sql 004_ledger_attestations.sql 004_risk_fields.sql \
|
||||
005_risk_fields.sql 006_orchestrator_airgap.sql; do
|
||||
if [ -f "$MIGRATION_DIR/$migration" ]; then
|
||||
echo "Applying migration: $migration"
|
||||
psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE -f "$MIGRATION_DIR/$migration"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Apply RLS migration (007_enable_rls.sql)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Applying RLS migration..."
|
||||
psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE \
|
||||
-f src/Findings/StellaOps.Findings.Ledger/migrations/007_enable_rls.sql
|
||||
|
||||
- name: Validate RLS configuration
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Validating RLS is enabled on all protected tables..."
|
||||
|
||||
# Check RLS enabled
|
||||
TABLES_WITH_RLS=$(psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE -t -A -c "
|
||||
SELECT COUNT(*)
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = 'public'
|
||||
AND c.relrowsecurity = true
|
||||
AND c.relname IN (
|
||||
'ledger_events', 'ledger_merkle_roots', 'findings_projection',
|
||||
'finding_history', 'triage_actions', 'ledger_attestations',
|
||||
'orchestrator_exports', 'airgap_imports'
|
||||
);
|
||||
")
|
||||
|
||||
if [ "$TABLES_WITH_RLS" -ne 8 ]; then
|
||||
echo "::error::Expected 8 tables with RLS enabled, found $TABLES_WITH_RLS"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ All 8 tables have RLS enabled"
|
||||
|
||||
# Check policies exist
|
||||
POLICIES=$(psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE -t -A -c "
|
||||
SELECT COUNT(DISTINCT tablename)
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
AND policyname LIKE '%_tenant_isolation';
|
||||
")
|
||||
|
||||
if [ "$POLICIES" -ne 8 ]; then
|
||||
echo "::error::Expected 8 tenant isolation policies, found $POLICIES"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ All 8 tenant isolation policies created"
|
||||
|
||||
# Check tenant function exists
|
||||
FUNC_EXISTS=$(psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE -t -A -c "
|
||||
SELECT COUNT(*)
|
||||
FROM pg_proc p
|
||||
JOIN pg_namespace n ON p.pronamespace = n.oid
|
||||
WHERE p.proname = 'require_current_tenant'
|
||||
AND n.nspname = 'findings_ledger_app';
|
||||
")
|
||||
|
||||
if [ "$FUNC_EXISTS" -ne 1 ]; then
|
||||
echo "::error::Tenant function 'require_current_tenant' not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Tenant function 'findings_ledger_app.require_current_tenant()' exists"
|
||||
|
||||
echo ""
|
||||
echo "=== RLS Migration Validation PASSED ==="
|
||||
|
||||
- name: Test rollback migration
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Testing rollback migration..."
|
||||
psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE \
|
||||
-f src/Findings/StellaOps.Findings.Ledger/migrations/007_enable_rls_rollback.sql
|
||||
|
||||
# Verify RLS is disabled
|
||||
TABLES_WITH_RLS=$(psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE -t -A -c "
|
||||
SELECT COUNT(*)
|
||||
FROM pg_class c
|
||||
JOIN pg_namespace n ON c.relnamespace = n.oid
|
||||
WHERE n.nspname = 'public'
|
||||
AND c.relrowsecurity = true
|
||||
AND c.relname IN (
|
||||
'ledger_events', 'ledger_merkle_roots', 'findings_projection',
|
||||
'finding_history', 'triage_actions', 'ledger_attestations',
|
||||
'orchestrator_exports', 'airgap_imports'
|
||||
);
|
||||
")
|
||||
|
||||
if [ "$TABLES_WITH_RLS" -ne 0 ]; then
|
||||
echo "::error::Rollback failed - $TABLES_WITH_RLS tables still have RLS enabled"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Rollback successful - RLS disabled on all tables"
|
||||
- name: Validate release manifests (production)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python ops/devops/release/check_release_manifest.py
|
||||
|
||||
- name: Re-apply RLS migration (idempotency check)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
echo "Re-applying RLS migration to verify idempotency..."
|
||||
psql -h $PGHOST -p $PGPORT -U $PGUSER -d $PGDATABASE \
|
||||
-f src/Findings/StellaOps.Findings.Ledger/migrations/007_enable_rls.sql
|
||||
echo "✓ Migration is idempotent"
|
||||
|
||||
generate-manifest:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-test, migration-validation]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate migration manifest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
MIGRATION_FILE="src/Findings/StellaOps.Findings.Ledger/migrations/007_enable_rls.sql"
|
||||
ROLLBACK_FILE="src/Findings/StellaOps.Findings.Ledger/migrations/007_enable_rls_rollback.sql"
|
||||
MANIFEST_DIR="out/findings-ledger/migrations"
|
||||
mkdir -p "$MANIFEST_DIR"
|
||||
|
||||
# Compute SHA256 hashes
|
||||
MIGRATION_SHA=$(sha256sum "$MIGRATION_FILE" | awk '{print $1}')
|
||||
ROLLBACK_SHA=$(sha256sum "$ROLLBACK_FILE" | awk '{print $1}')
|
||||
CREATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
cat > "$MANIFEST_DIR/007_enable_rls.manifest.json" <<EOF
|
||||
{
|
||||
"\$schema": "https://stella-ops.org/schemas/migration-manifest.v1.json",
|
||||
"schemaVersion": "1.0.0",
|
||||
"migrationId": "007_enable_rls",
|
||||
"module": "findings-ledger",
|
||||
"version": "2025.12.0",
|
||||
"createdAt": "$CREATED_AT",
|
||||
"description": "Enable Row-Level Security for Findings Ledger tenant isolation",
|
||||
"taskId": "LEDGER-TEN-48-001-DEV",
|
||||
"contractRef": "CONTRACT-FINDINGS-LEDGER-RLS-011",
|
||||
"database": {
|
||||
"engine": "postgresql",
|
||||
"minVersion": "16.0"
|
||||
},
|
||||
"files": {
|
||||
"apply": {
|
||||
"path": "007_enable_rls.sql",
|
||||
"sha256": "$MIGRATION_SHA"
|
||||
},
|
||||
"rollback": {
|
||||
"path": "007_enable_rls_rollback.sql",
|
||||
"sha256": "$ROLLBACK_SHA"
|
||||
}
|
||||
},
|
||||
"affects": {
|
||||
"tables": [
|
||||
"ledger_events",
|
||||
"ledger_merkle_roots",
|
||||
"findings_projection",
|
||||
"finding_history",
|
||||
"triage_actions",
|
||||
"ledger_attestations",
|
||||
"orchestrator_exports",
|
||||
"airgap_imports"
|
||||
],
|
||||
"schemas": ["public", "findings_ledger_app"],
|
||||
"roles": ["findings_ledger_admin"]
|
||||
},
|
||||
"prerequisites": [
|
||||
"006_orchestrator_airgap"
|
||||
],
|
||||
"validation": {
|
||||
"type": "rls-check",
|
||||
"expectedTables": 8,
|
||||
"expectedPolicies": 8,
|
||||
"tenantFunction": "findings_ledger_app.require_current_tenant"
|
||||
},
|
||||
"offlineKit": {
|
||||
"includedInBundle": true,
|
||||
"requiresManualApply": true,
|
||||
"applyOrder": 7
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Generated migration manifest at $MANIFEST_DIR/007_enable_rls.manifest.json"
|
||||
cat "$MANIFEST_DIR/007_enable_rls.manifest.json"
|
||||
|
||||
- name: Copy migration files for offline-kit
|
||||
run: |
|
||||
set -euo pipefail
|
||||
OFFLINE_DIR="out/findings-ledger/offline-kit/migrations"
|
||||
mkdir -p "$OFFLINE_DIR"
|
||||
cp src/Findings/StellaOps.Findings.Ledger/migrations/007_enable_rls.sql "$OFFLINE_DIR/"
|
||||
cp src/Findings/StellaOps.Findings.Ledger/migrations/007_enable_rls_rollback.sql "$OFFLINE_DIR/"
|
||||
cp out/findings-ledger/migrations/007_enable_rls.manifest.json "$OFFLINE_DIR/"
|
||||
echo "Offline-kit migration files prepared"
|
||||
ls -la "$OFFLINE_DIR"
|
||||
|
||||
- name: Upload migration artefacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: findings-ledger-migrations
|
||||
path: out/findings-ledger/
|
||||
if-no-files-found: error
|
||||
42
.gitea/workflows/graph-load.yml
Normal file
42
.gitea/workflows/graph-load.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
name: graph-load
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
target:
|
||||
description: "Graph API base URL"
|
||||
required: true
|
||||
default: "http://localhost:5000"
|
||||
users:
|
||||
description: "Virtual users"
|
||||
required: false
|
||||
default: "8"
|
||||
duration:
|
||||
description: "Duration seconds"
|
||||
required: false
|
||||
default: "60"
|
||||
|
||||
jobs:
|
||||
load-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Install k6
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y k6
|
||||
|
||||
- name: Run graph load test
|
||||
run: |
|
||||
chmod +x scripts/graph/load-test.sh
|
||||
TARGET="${{ github.event.inputs.target }}" USERS="${{ github.event.inputs.users }}" DURATION="${{ github.event.inputs.duration }}" scripts/graph/load-test.sh
|
||||
|
||||
- name: Upload results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: graph-load-summary
|
||||
path: out/graph-load/**
|
||||
57
.gitea/workflows/graph-ui-sim.yml
Normal file
57
.gitea/workflows/graph-ui-sim.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
name: graph-ui-sim
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
graph_api:
|
||||
description: "Graph API base URL"
|
||||
required: true
|
||||
default: "http://localhost:5000"
|
||||
graph_ui:
|
||||
description: "Graph UI base URL"
|
||||
required: true
|
||||
default: "http://localhost:4200"
|
||||
perf_budget_ms:
|
||||
description: "Perf budget in ms"
|
||||
required: false
|
||||
default: "3000"
|
||||
|
||||
jobs:
|
||||
ui-and-sim:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Install Playwright deps
|
||||
run: npx playwright install --with-deps chromium
|
||||
|
||||
- name: Run UI perf probe
|
||||
env:
|
||||
GRAPH_UI_BASE: ${{ github.event.inputs.graph_ui }}
|
||||
GRAPH_UI_BUDGET_MS: ${{ github.event.inputs.perf_budget_ms }}
|
||||
OUT: out/graph-ui-perf
|
||||
run: |
|
||||
npx ts-node scripts/graph/ui-perf.ts
|
||||
|
||||
- name: Run simulation smoke
|
||||
env:
|
||||
TARGET: ${{ github.event.inputs.graph_api }}
|
||||
run: |
|
||||
chmod +x scripts/graph/simulation-smoke.sh
|
||||
scripts/graph/simulation-smoke.sh
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: graph-ui-sim
|
||||
path: |
|
||||
out/graph-ui-perf/**
|
||||
out/graph-sim/**
|
||||
68
.gitea/workflows/icscisa-kisa-refresh.yml
Normal file
68
.gitea/workflows/icscisa-kisa-refresh.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
name: ICS/KISA Feed Refresh
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * MON'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
live_fetch:
|
||||
description: 'Attempt live RSS fetch (fallback to samples on failure)'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
offline_snapshot:
|
||||
description: 'Force offline samples only (no network)'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
refresh:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: read
|
||||
env:
|
||||
ICSCISA_FEED_URL: ${{ secrets.ICSCISA_FEED_URL }}
|
||||
KISA_FEED_URL: ${{ secrets.KISA_FEED_URL }}
|
||||
FEED_GATEWAY_HOST: concelier-webservice
|
||||
FEED_GATEWAY_SCHEME: http
|
||||
LIVE_FETCH: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.live_fetch || 'true' }}
|
||||
OFFLINE_SNAPSHOT: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.offline_snapshot || 'false' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set run metadata
|
||||
id: meta
|
||||
run: |
|
||||
RUN_DATE=$(date -u +%Y%m%d)
|
||||
RUN_ID="icscisa-kisa-$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
echo "run_date=$RUN_DATE" >> $GITHUB_OUTPUT
|
||||
echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT
|
||||
echo "RUN_DATE=$RUN_DATE" >> $GITHUB_ENV
|
||||
echo "RUN_ID=$RUN_ID" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Run ICS/KISA refresh
|
||||
run: |
|
||||
python scripts/feeds/run_icscisa_kisa_refresh.py \
|
||||
--out-dir out/feeds/icscisa-kisa \
|
||||
--run-date "${{ steps.meta.outputs.run_date }}" \
|
||||
--run-id "${{ steps.meta.outputs.run_id }}"
|
||||
|
||||
- name: Show fetch log
|
||||
run: cat out/feeds/icscisa-kisa/${{ steps.meta.outputs.run_date }}/fetch.log
|
||||
|
||||
- name: Upload refresh artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: icscisa-kisa-${{ steps.meta.outputs.run_date }}
|
||||
path: out/feeds/icscisa-kisa/${{ steps.meta.outputs.run_date }}
|
||||
if-no-files-found: error
|
||||
retention-days: 21
|
||||
81
.gitea/workflows/ledger-oas-ci.yml
Normal file
81
.gitea/workflows/ledger-oas-ci.yml
Normal file
@@ -0,0 +1,81 @@
|
||||
name: Ledger OpenAPI CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'api/ledger/**'
|
||||
- 'ops/devops/ledger/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'api/ledger/**'
|
||||
|
||||
jobs:
|
||||
validate-oas:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install tools
|
||||
run: |
|
||||
npm install -g @stoplight/spectral-cli
|
||||
npm install -g @openapitools/openapi-generator-cli
|
||||
|
||||
- name: Validate OpenAPI spec
|
||||
run: |
|
||||
chmod +x ops/devops/ledger/validate-oas.sh
|
||||
ops/devops/ledger/validate-oas.sh
|
||||
|
||||
- name: Upload validation report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ledger-oas-validation-${{ github.run_number }}
|
||||
path: |
|
||||
out/ledger/oas/lint-report.json
|
||||
out/ledger/oas/validation-report.txt
|
||||
out/ledger/oas/spec-summary.json
|
||||
if-no-files-found: warn
|
||||
|
||||
check-wellknown:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: validate-oas
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check .well-known/openapi structure
|
||||
run: |
|
||||
# Validate .well-known structure if exists
|
||||
if [ -d ".well-known" ]; then
|
||||
echo "Checking .well-known/openapi..."
|
||||
if [ -f ".well-known/openapi.json" ]; then
|
||||
python3 -c "import json; json.load(open('.well-known/openapi.json'))"
|
||||
echo ".well-known/openapi.json is valid JSON"
|
||||
fi
|
||||
else
|
||||
echo "[info] .well-known directory not present (OK for dev)"
|
||||
fi
|
||||
|
||||
deprecation-check:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: validate-oas
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check deprecation policy
|
||||
run: |
|
||||
if [ -f "ops/devops/ledger/deprecation-policy.yaml" ]; then
|
||||
echo "Validating deprecation policy..."
|
||||
python3 -c "import yaml; yaml.safe_load(open('ops/devops/ledger/deprecation-policy.yaml'))"
|
||||
echo "Deprecation policy is valid"
|
||||
else
|
||||
echo "[info] No deprecation policy yet (OK for initial setup)"
|
||||
fi
|
||||
101
.gitea/workflows/ledger-packs-ci.yml
Normal file
101
.gitea/workflows/ledger-packs-ci.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
name: Ledger Packs CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
snapshot_id:
|
||||
description: 'Snapshot ID (leave empty for auto)'
|
||||
required: false
|
||||
default: ''
|
||||
sign:
|
||||
description: 'Sign pack (1=yes)'
|
||||
required: false
|
||||
default: '0'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'ops/devops/ledger/**'
|
||||
|
||||
jobs:
|
||||
build-pack:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
|
||||
- name: Configure signing
|
||||
run: |
|
||||
if [ -z "${COSIGN_PRIVATE_KEY_B64}" ] || [ "${{ github.event.inputs.sign }}" = "1" ]; then
|
||||
echo "COSIGN_ALLOW_DEV_KEY=1" >> $GITHUB_ENV
|
||||
echo "COSIGN_PASSWORD=stellaops-dev" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Build pack
|
||||
run: |
|
||||
chmod +x ops/devops/ledger/build-pack.sh
|
||||
SNAPSHOT_ID="${{ github.event.inputs.snapshot_id }}"
|
||||
if [ -z "$SNAPSHOT_ID" ]; then
|
||||
SNAPSHOT_ID="ci-$(date +%Y%m%d%H%M%S)"
|
||||
fi
|
||||
|
||||
SIGN_FLAG=""
|
||||
if [ "${{ github.event.inputs.sign }}" = "1" ] || [ -n "${COSIGN_PRIVATE_KEY_B64}" ]; then
|
||||
SIGN_FLAG="--sign"
|
||||
fi
|
||||
|
||||
SNAPSHOT_ID="$SNAPSHOT_ID" ops/devops/ledger/build-pack.sh $SIGN_FLAG
|
||||
|
||||
- name: Verify checksums
|
||||
run: |
|
||||
cd out/ledger/packs
|
||||
for f in *.SHA256SUMS; do
|
||||
if [ -f "$f" ]; then
|
||||
sha256sum -c "$f"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Upload pack
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ledger-pack-${{ github.run_number }}
|
||||
path: |
|
||||
out/ledger/packs/*.pack.tar.gz
|
||||
out/ledger/packs/*.SHA256SUMS
|
||||
out/ledger/packs/*.dsse.json
|
||||
if-no-files-found: warn
|
||||
retention-days: 30
|
||||
|
||||
verify-pack:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-pack
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download pack
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ledger-pack-${{ github.run_number }}
|
||||
path: out/ledger/packs/
|
||||
|
||||
- name: Verify pack structure
|
||||
run: |
|
||||
cd out/ledger/packs
|
||||
for pack in *.pack.tar.gz; do
|
||||
if [ -f "$pack" ]; then
|
||||
echo "Verifying $pack..."
|
||||
tar -tzf "$pack" | head -20
|
||||
|
||||
# Extract and check manifest
|
||||
tar -xzf "$pack" -C /tmp manifest.json 2>/dev/null || true
|
||||
if [ -f /tmp/manifest.json ]; then
|
||||
python3 -c "import json; json.load(open('/tmp/manifest.json'))"
|
||||
echo "Pack manifest is valid JSON"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
188
.gitea/workflows/lighthouse-ci.yml
Normal file
188
.gitea/workflows/lighthouse-ci.yml
Normal file
@@ -0,0 +1,188 @@
|
||||
# .gitea/workflows/lighthouse-ci.yml
|
||||
# Lighthouse CI for performance and accessibility testing of the StellaOps Web UI
|
||||
|
||||
name: Lighthouse CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/Web/StellaOps.Web/**'
|
||||
- '.gitea/workflows/lighthouse-ci.yml'
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
paths:
|
||||
- 'src/Web/StellaOps.Web/**'
|
||||
schedule:
|
||||
# Run weekly on Sunday at 2 AM UTC
|
||||
- cron: '0 2 * * 0'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
NODE_VERSION: '20'
|
||||
LHCI_BUILD_CONTEXT__CURRENT_BRANCH: ${{ github.head_ref || github.ref_name }}
|
||||
LHCI_BUILD_CONTEXT__COMMIT_SHA: ${{ github.sha }}
|
||||
|
||||
jobs:
|
||||
lighthouse:
|
||||
name: Lighthouse Audit
|
||||
runs-on: ubuntu-22.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src/Web/StellaOps.Web
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: src/Web/StellaOps.Web/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build production bundle
|
||||
run: npm run build -- --configuration production
|
||||
|
||||
- name: Install Lighthouse CI
|
||||
run: npm install -g @lhci/cli@0.13.x
|
||||
|
||||
- name: Run Lighthouse CI
|
||||
run: |
|
||||
lhci autorun \
|
||||
--collect.staticDistDir=./dist/stella-ops-web/browser \
|
||||
--collect.numberOfRuns=3 \
|
||||
--assert.preset=lighthouse:recommended \
|
||||
--assert.assertions.categories:performance=off \
|
||||
--assert.assertions.categories:accessibility=off \
|
||||
--upload.target=filesystem \
|
||||
--upload.outputDir=./lighthouse-results
|
||||
|
||||
- name: Evaluate Lighthouse Results
|
||||
id: lhci-results
|
||||
run: |
|
||||
# Parse the latest Lighthouse report
|
||||
REPORT=$(ls -t lighthouse-results/*.json | head -1)
|
||||
|
||||
if [ -f "$REPORT" ]; then
|
||||
PERF=$(jq '.categories.performance.score * 100' "$REPORT" | cut -d. -f1)
|
||||
A11Y=$(jq '.categories.accessibility.score * 100' "$REPORT" | cut -d. -f1)
|
||||
BP=$(jq '.categories["best-practices"].score * 100' "$REPORT" | cut -d. -f1)
|
||||
SEO=$(jq '.categories.seo.score * 100' "$REPORT" | cut -d. -f1)
|
||||
|
||||
echo "performance=$PERF" >> $GITHUB_OUTPUT
|
||||
echo "accessibility=$A11Y" >> $GITHUB_OUTPUT
|
||||
echo "best-practices=$BP" >> $GITHUB_OUTPUT
|
||||
echo "seo=$SEO" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "## Lighthouse Results" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Category | Score | Threshold | Status |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|----------|-------|-----------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Performance: target >= 90
|
||||
if [ "$PERF" -ge 90 ]; then
|
||||
echo "| Performance | $PERF | >= 90 | :white_check_mark: |" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "| Performance | $PERF | >= 90 | :warning: |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Accessibility: target >= 95
|
||||
if [ "$A11Y" -ge 95 ]; then
|
||||
echo "| Accessibility | $A11Y | >= 95 | :white_check_mark: |" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "| Accessibility | $A11Y | >= 95 | :x: |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Best Practices: target >= 90
|
||||
if [ "$BP" -ge 90 ]; then
|
||||
echo "| Best Practices | $BP | >= 90 | :white_check_mark: |" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "| Best Practices | $BP | >= 90 | :warning: |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# SEO: target >= 90
|
||||
if [ "$SEO" -ge 90 ]; then
|
||||
echo "| SEO | $SEO | >= 90 | :white_check_mark: |" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "| SEO | $SEO | >= 90 | :warning: |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Check Quality Gates
|
||||
run: |
|
||||
PERF=${{ steps.lhci-results.outputs.performance }}
|
||||
A11Y=${{ steps.lhci-results.outputs.accessibility }}
|
||||
|
||||
FAILED=0
|
||||
|
||||
# Performance gate (warning only, not blocking)
|
||||
if [ "$PERF" -lt 90 ]; then
|
||||
echo "::warning::Performance score ($PERF) is below target (90)"
|
||||
fi
|
||||
|
||||
# Accessibility gate (blocking)
|
||||
if [ "$A11Y" -lt 95 ]; then
|
||||
echo "::error::Accessibility score ($A11Y) is below required threshold (95)"
|
||||
FAILED=1
|
||||
fi
|
||||
|
||||
if [ "$FAILED" -eq 1 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Upload Lighthouse Reports
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: lighthouse-reports
|
||||
path: src/Web/StellaOps.Web/lighthouse-results/
|
||||
retention-days: 30
|
||||
|
||||
axe-accessibility:
|
||||
name: Axe Accessibility Audit
|
||||
runs-on: ubuntu-22.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: src/Web/StellaOps.Web
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: src/Web/StellaOps.Web/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps chromium
|
||||
|
||||
- name: Build production bundle
|
||||
run: npm run build -- --configuration production
|
||||
|
||||
- name: Start preview server
|
||||
run: |
|
||||
npx serve -s dist/stella-ops-web/browser -l 4200 &
|
||||
sleep 5
|
||||
|
||||
- name: Run Axe accessibility tests
|
||||
run: |
|
||||
npm run test:a11y || true
|
||||
|
||||
- name: Upload Axe results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: axe-accessibility-results
|
||||
path: src/Web/StellaOps.Web/test-results/
|
||||
retention-days: 30
|
||||
64
.gitea/workflows/lnm-backfill.yml
Normal file
64
.gitea/workflows/lnm-backfill.yml
Normal file
@@ -0,0 +1,64 @@
|
||||
name: LNM Backfill CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mongo_uri:
|
||||
description: 'Staging Mongo URI (read-only snapshot)'
|
||||
required: true
|
||||
type: string
|
||||
since_commit:
|
||||
description: 'Git commit to compare (default HEAD)'
|
||||
required: false
|
||||
type: string
|
||||
dry_run:
|
||||
description: 'Dry run (no writes)'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
lnm-backfill:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj
|
||||
|
||||
- name: Run backfill (dry-run supported)
|
||||
env:
|
||||
STAGING_MONGO_URI: ${{ inputs.mongo_uri }}
|
||||
run: |
|
||||
mkdir -p $ARTIFACT_DIR
|
||||
EXTRA=()
|
||||
if [ "${{ inputs.dry_run }}" = "true" ]; then EXTRA+=("--dry-run"); fi
|
||||
dotnet run --project src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj -- --mode=observations --batch-size=500 --max-conflicts=0 --mongo "$STAGING_MONGO_URI" "${EXTRA[@]}" | tee $ARTIFACT_DIR/backfill-observations.log
|
||||
dotnet run --project src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj -- --mode=linksets --batch-size=500 --max-conflicts=0 --mongo "$STAGING_MONGO_URI" "${EXTRA[@]}" | tee $ARTIFACT_DIR/backfill-linksets.log
|
||||
|
||||
- name: Validate counts
|
||||
env:
|
||||
STAGING_MONGO_URI: ${{ inputs.mongo_uri }}
|
||||
run: |
|
||||
STAGING_MONGO_URI="$STAGING_MONGO_URI" ops/devops/lnm/backfill-validation.sh
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lnm-backfill-artifacts
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
83
.gitea/workflows/lnm-migration-ci.yml
Normal file
83
.gitea/workflows/lnm-migration-ci.yml
Normal file
@@ -0,0 +1,83 @@
|
||||
name: LNM Migration CI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_staging:
|
||||
description: 'Run staging backfill (1=yes)'
|
||||
required: false
|
||||
default: '0'
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/Concelier/__Libraries/StellaOps.Concelier.Migrations/**'
|
||||
- 'ops/devops/lnm/**'
|
||||
|
||||
jobs:
|
||||
build-runner:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Setup cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
|
||||
- name: Configure signing
|
||||
run: |
|
||||
if [ -z "${{ secrets.COSIGN_PRIVATE_KEY_B64 }}" ]; then
|
||||
echo "COSIGN_ALLOW_DEV_KEY=1" >> $GITHUB_ENV
|
||||
echo "COSIGN_PASSWORD=stellaops-dev" >> $GITHUB_ENV
|
||||
fi
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
|
||||
- name: Build and package runner
|
||||
run: |
|
||||
chmod +x ops/devops/lnm/package-runner.sh
|
||||
ops/devops/lnm/package-runner.sh
|
||||
|
||||
- name: Verify checksums
|
||||
run: |
|
||||
cd out/lnm
|
||||
sha256sum -c SHA256SUMS
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lnm-migration-runner-${{ github.run_number }}
|
||||
path: |
|
||||
out/lnm/lnm-migration-runner.tar.gz
|
||||
out/lnm/lnm-migration-runner.manifest.json
|
||||
out/lnm/lnm-migration-runner.dsse.json
|
||||
out/lnm/SHA256SUMS
|
||||
if-no-files-found: warn
|
||||
|
||||
validate-metrics:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: build-runner
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate monitoring config
|
||||
run: |
|
||||
# Validate alert rules syntax
|
||||
if [ -f "ops/devops/lnm/alerts/lnm-alerts.yaml" ]; then
|
||||
echo "Validating alert rules..."
|
||||
python3 -c "import yaml; yaml.safe_load(open('ops/devops/lnm/alerts/lnm-alerts.yaml'))"
|
||||
fi
|
||||
|
||||
# Validate dashboard JSON
|
||||
if [ -f "ops/devops/lnm/dashboards/lnm-migration.json" ]; then
|
||||
echo "Validating dashboard..."
|
||||
python3 -c "import json; json.load(open('ops/devops/lnm/dashboards/lnm-migration.json'))"
|
||||
fi
|
||||
|
||||
echo "Monitoring config validation complete"
|
||||
63
.gitea/workflows/lnm-vex-backfill.yml
Normal file
63
.gitea/workflows/lnm-vex-backfill.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
name: LNM VEX Backfill
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
mongo_uri:
|
||||
description: 'Staging Mongo URI'
|
||||
required: true
|
||||
type: string
|
||||
nats_url:
|
||||
description: 'NATS URL'
|
||||
required: true
|
||||
type: string
|
||||
redis_url:
|
||||
description: 'Redis URL'
|
||||
required: true
|
||||
type: string
|
||||
dry_run:
|
||||
description: 'Dry run (no writes)'
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
vex-backfill:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj
|
||||
|
||||
- name: Run VEX backfill
|
||||
env:
|
||||
STAGING_MONGO_URI: ${{ inputs.mongo_uri }}
|
||||
NATS_URL: ${{ inputs.nats_url }}
|
||||
REDIS_URL: ${{ inputs.redis_url }}
|
||||
run: |
|
||||
mkdir -p $ARTIFACT_DIR
|
||||
EXTRA=()
|
||||
if [ "${{ inputs.dry_run }}" = "true" ]; then EXTRA+=("--dry-run"); fi
|
||||
dotnet run --project src/Concelier/StellaOps.Concelier.Backfill/StellaOps.Concelier.Backfill.csproj -- --mode=vex --batch-size=500 --max-conflicts=0 --mongo "$STAGING_MONGO_URI" --nats "$NATS_URL" --redis "$REDIS_URL" "${EXTRA[@]}" | tee $ARTIFACT_DIR/vex-backfill.log
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: lnm-vex-backfill-artifacts
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
125
.gitea/workflows/manifest-integrity.yml
Normal file
125
.gitea/workflows/manifest-integrity.yml
Normal file
@@ -0,0 +1,125 @@
|
||||
name: Manifest Integrity
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'docs/**/*.schema.json'
|
||||
- 'docs/contracts/**'
|
||||
- 'docs/schemas/**'
|
||||
- 'scripts/packs/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/**/*.schema.json'
|
||||
- 'docs/contracts/**'
|
||||
- 'docs/schemas/**'
|
||||
- 'scripts/packs/**'
|
||||
|
||||
jobs:
|
||||
validate-schemas:
|
||||
name: Validate Schema Integrity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install -g ajv-cli ajv-formats
|
||||
|
||||
- name: Validate JSON schemas
|
||||
run: |
|
||||
EXIT_CODE=0
|
||||
for schema in docs/schemas/*.schema.json; do
|
||||
echo "Validating $schema..."
|
||||
if ! ajv compile -s "$schema" --spec=draft2020 2>/dev/null; then
|
||||
echo "Error: $schema is invalid"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done
|
||||
exit $EXIT_CODE
|
||||
|
||||
validate-contracts:
|
||||
name: Validate Contract Documents
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check contract structure
|
||||
run: |
|
||||
for contract in docs/contracts/*.md; do
|
||||
echo "Checking $contract..."
|
||||
# Verify required sections exist
|
||||
if ! grep -q "^## " "$contract"; then
|
||||
echo "Warning: $contract missing section headers"
|
||||
fi
|
||||
# Check for decision ID
|
||||
if grep -q "Decision ID" "$contract" && ! grep -q "DECISION-\|CONTRACT-" "$contract"; then
|
||||
echo "Warning: $contract missing decision ID format"
|
||||
fi
|
||||
done
|
||||
|
||||
validate-pack-fixtures:
|
||||
name: Validate Pack Fixtures
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install jsonschema
|
||||
|
||||
- name: Run fixture validation
|
||||
run: |
|
||||
if [ -f scripts/packs/run-fixtures-check.sh ]; then
|
||||
chmod +x scripts/packs/run-fixtures-check.sh
|
||||
./scripts/packs/run-fixtures-check.sh
|
||||
fi
|
||||
|
||||
checksum-audit:
|
||||
name: Audit SHA256SUMS Files
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate checksums
|
||||
run: |
|
||||
find . -name "SHA256SUMS" -type f | while read f; do
|
||||
dir=$(dirname "$f")
|
||||
echo "Validating checksums in $dir..."
|
||||
cd "$dir"
|
||||
# Check if all referenced files exist
|
||||
while read hash file; do
|
||||
if [ ! -f "$file" ]; then
|
||||
echo "Warning: $file referenced in SHA256SUMS but not found"
|
||||
fi
|
||||
done < SHA256SUMS
|
||||
cd - > /dev/null
|
||||
done
|
||||
|
||||
merkle-consistency:
|
||||
name: Verify Merkle Roots
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check DSSE Merkle roots
|
||||
run: |
|
||||
find . -name "*.dsse.json" -type f | while read f; do
|
||||
echo "Checking Merkle root in $f..."
|
||||
# Extract and validate Merkle root if present
|
||||
if jq -e '.payload' "$f" > /dev/null 2>&1; then
|
||||
PAYLOAD=$(jq -r '.payload' "$f" | base64 -d 2>/dev/null || echo "")
|
||||
if echo "$PAYLOAD" | jq -e '._stellaops.merkleRoot' > /dev/null 2>&1; then
|
||||
MERKLE=$(echo "$PAYLOAD" | jq -r '._stellaops.merkleRoot')
|
||||
echo " Merkle root: $MERKLE"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
74
.gitea/workflows/mirror-sign.yml
Normal file
74
.gitea/workflows/mirror-sign.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
name: Mirror Thin Bundle Sign & Verify
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 6 * * *'
|
||||
|
||||
jobs:
|
||||
mirror-sign:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
MIRROR_SIGN_KEY_B64: ${{ secrets.MIRROR_SIGN_KEY_B64 }}
|
||||
REQUIRE_PROD_SIGNING: 1
|
||||
OCI: 1
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Fallback to dev signing key when secret is absent (non-prod only)
|
||||
run: |
|
||||
if [ -z "${MIRROR_SIGN_KEY_B64}" ]; then
|
||||
echo "[warn] MIRROR_SIGN_KEY_B64 not set; using repo dev key for non-production signing."
|
||||
echo "MIRROR_SIGN_KEY_B64=$(base64 -w0 tools/cosign/cosign.dev.key)" >> $GITHUB_ENV
|
||||
echo "REQUIRE_PROD_SIGNING=0" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Verify signing prerequisites
|
||||
run: scripts/mirror/check_signing_prereqs.sh
|
||||
|
||||
- name: Run mirror signing
|
||||
run: |
|
||||
scripts/mirror/ci-sign.sh
|
||||
|
||||
- name: Verify signed bundle
|
||||
run: |
|
||||
scripts/mirror/verify_thin_bundle.py out/mirror/thin/mirror-thin-v1.tar.gz
|
||||
|
||||
- name: Prepare Export Center handoff (metadata + optional schedule)
|
||||
run: |
|
||||
scripts/mirror/export-center-wire.sh
|
||||
env:
|
||||
EXPORT_CENTER_BASE_URL: ${{ secrets.EXPORT_CENTER_BASE_URL }}
|
||||
EXPORT_CENTER_TOKEN: ${{ secrets.EXPORT_CENTER_TOKEN }}
|
||||
EXPORT_CENTER_TENANT: ${{ secrets.EXPORT_CENTER_TENANT }}
|
||||
EXPORT_CENTER_PROJECT: ${{ secrets.EXPORT_CENTER_PROJECT }}
|
||||
EXPORT_CENTER_AUTO_SCHEDULE: ${{ secrets.EXPORT_CENTER_AUTO_SCHEDULE }}
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mirror-thin-v1-signed
|
||||
path: |
|
||||
out/mirror/thin/mirror-thin-v1.tar.gz
|
||||
out/mirror/thin/mirror-thin-v1.manifest.json
|
||||
out/mirror/thin/mirror-thin-v1.manifest.dsse.json
|
||||
out/mirror/thin/tuf/
|
||||
out/mirror/thin/oci/
|
||||
out/mirror/thin/milestone.json
|
||||
out/mirror/thin/export-center/export-center-handoff.json
|
||||
out/mirror/thin/export-center/export-center-targets.json
|
||||
out/mirror/thin/export-center/schedule-response.json
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
44
.gitea/workflows/mock-dev-release.yml
Normal file
44
.gitea/workflows/mock-dev-release.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
name: mock-dev-release
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- deploy/releases/2025.09-mock-dev.yaml
|
||||
- deploy/downloads/manifest.json
|
||||
- ops/devops/mock-release/**
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
package-mock-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Package mock dev artefacts
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p out/mock-release
|
||||
cp deploy/releases/2025.09-mock-dev.yaml out/mock-release/
|
||||
cp deploy/downloads/manifest.json out/mock-release/
|
||||
tar -czf out/mock-release/mock-dev-release.tgz -C out/mock-release .
|
||||
|
||||
- name: Compose config (dev + mock overlay)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ops/devops/mock-release/config_check.sh
|
||||
|
||||
- name: Helm template (mock overlay)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
helm template mock ./deploy/helm/stellaops -f deploy/helm/stellaops/values-mock.yaml > /tmp/helm-mock.yaml
|
||||
ls -lh /tmp/helm-mock.yaml
|
||||
|
||||
- name: Upload mock release bundle
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: mock-dev-release
|
||||
path: |
|
||||
out/mock-release/mock-dev-release.tgz
|
||||
/tmp/compose-mock-config.yaml
|
||||
/tmp/helm-mock.yaml
|
||||
102
.gitea/workflows/notify-smoke-test.yml
Normal file
102
.gitea/workflows/notify-smoke-test.yml
Normal file
@@ -0,0 +1,102 @@
|
||||
name: Notify Smoke Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/Notify/**'
|
||||
- 'src/Notifier/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/Notify/**'
|
||||
- 'src/Notifier/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.x'
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Notify Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore src/Notify/
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/Notify/ --no-restore
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test src/Notify/ --no-build --verbosity normal
|
||||
|
||||
notifier-tests:
|
||||
name: Notifier Service Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore src/Notifier/
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/Notifier/ --no-restore
|
||||
|
||||
- name: Run tests
|
||||
run: dotnet test src/Notifier/ --no-build --verbosity normal
|
||||
|
||||
smoke-test:
|
||||
name: Notification Smoke Test
|
||||
runs-on: ubuntu-latest
|
||||
needs: [unit-tests, notifier-tests]
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:7.0
|
||||
ports:
|
||||
- 27017:27017
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Build Notifier
|
||||
run: dotnet build src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/
|
||||
|
||||
- name: Start service
|
||||
run: |
|
||||
dotnet run --project src/Notifier/StellaOps.Notifier/StellaOps.Notifier.WebService/ &
|
||||
sleep 10
|
||||
|
||||
- name: Health check
|
||||
run: |
|
||||
for i in {1..30}; do
|
||||
if curl -s http://localhost:5000/health > /dev/null; then
|
||||
echo "Service is healthy"
|
||||
exit 0
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
echo "Service failed to start"
|
||||
exit 1
|
||||
|
||||
- name: Test notification endpoint
|
||||
run: |
|
||||
# Test dry-run notification
|
||||
curl -X POST http://localhost:5000/api/v1/notifications/test \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"channel": "log", "message": "Smoke test", "dryRun": true}' \
|
||||
|| echo "Warning: Notification test endpoint not available"
|
||||
59
.gitea/workflows/oas-ci.yml
Normal file
59
.gitea/workflows/oas-ci.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
name: oas-ci
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "src/Api/**"
|
||||
- "scripts/api-*.mjs"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
pull_request:
|
||||
paths:
|
||||
- "src/Api/**"
|
||||
- "scripts/api-*.mjs"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
|
||||
jobs:
|
||||
oas-validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Install deps
|
||||
run: npm install --ignore-scripts --no-progress
|
||||
|
||||
- name: Compose aggregate OpenAPI
|
||||
run: npm run api:compose
|
||||
|
||||
- name: Lint (spectral)
|
||||
run: npm run api:lint
|
||||
|
||||
- name: Validate examples coverage
|
||||
run: npm run api:examples
|
||||
|
||||
- name: Compat diff (previous commit)
|
||||
run: |
|
||||
set -e
|
||||
if git show HEAD~1:src/Api/StellaOps.Api.OpenApi/stella.yaml > /tmp/stella-prev.yaml 2>/dev/null; then
|
||||
node scripts/api-compat-diff.mjs /tmp/stella-prev.yaml src/Api/StellaOps.Api.OpenApi/stella.yaml --output text --fail-on-breaking
|
||||
else
|
||||
echo "[oas-ci] previous stella.yaml not found; skipping"
|
||||
fi
|
||||
|
||||
- name: Contract tests
|
||||
run: npm run api:compat:test
|
||||
|
||||
- name: Upload aggregate spec
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: stella-openapi
|
||||
path: src/Api/StellaOps.Api.OpenApi/stella.yaml
|
||||
46
.gitea/workflows/obs-slo.yml
Normal file
46
.gitea/workflows/obs-slo.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
name: obs-slo
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
prom_url:
|
||||
description: "Prometheus base URL"
|
||||
required: true
|
||||
default: "http://localhost:9090"
|
||||
|
||||
jobs:
|
||||
slo-eval:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Python (telemetry schema checks)
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install telemetry schema deps
|
||||
run: python -m pip install --upgrade pip jsonschema
|
||||
|
||||
- name: Run SLO evaluator
|
||||
env:
|
||||
PROM_URL: ${{ github.event.inputs.prom_url }}
|
||||
run: |
|
||||
chmod +x scripts/observability/slo-evaluator.sh
|
||||
scripts/observability/slo-evaluator.sh
|
||||
|
||||
- name: Telemetry schema/bundle checks
|
||||
env:
|
||||
TELEMETRY_BUNDLE_SCHEMA: docs/modules/telemetry/schemas/telemetry-bundle.schema.json
|
||||
run: |
|
||||
chmod +x ops/devops/telemetry/tests/ci-run.sh
|
||||
ops/devops/telemetry/tests/ci-run.sh
|
||||
|
||||
- name: Upload SLO results
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: obs-slo
|
||||
path: out/obs-slo/**
|
||||
37
.gitea/workflows/obs-stream.yml
Normal file
37
.gitea/workflows/obs-stream.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
name: obs-stream
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
nats_url:
|
||||
description: "NATS server URL"
|
||||
required: false
|
||||
default: "nats://localhost:4222"
|
||||
|
||||
jobs:
|
||||
stream-validate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Install nats CLI
|
||||
run: |
|
||||
curl -sSL https://github.com/nats-io/natscli/releases/download/v0.1.4/nats-0.1.4-linux-amd64.tar.gz -o /tmp/natscli.tgz
|
||||
tar -C /tmp -xzf /tmp/natscli.tgz
|
||||
sudo mv /tmp/nats /usr/local/bin/nats
|
||||
|
||||
- name: Validate streaming knobs
|
||||
env:
|
||||
NATS_URL: ${{ github.event.inputs.nats_url }}
|
||||
run: |
|
||||
chmod +x scripts/observability/streaming-validate.sh
|
||||
scripts/observability/streaming-validate.sh
|
||||
|
||||
- name: Upload stream validation
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: obs-stream
|
||||
path: out/obs-stream/**
|
||||
70
.gitea/workflows/policy-lint.yml
Normal file
70
.gitea/workflows/policy-lint.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
name: Policy Lint & Smoke
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/policy/**'
|
||||
- 'docs/examples/policies/**'
|
||||
- 'src/Cli/**'
|
||||
- '.gitea/workflows/policy-lint.yml'
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'docs/policy/**'
|
||||
- 'docs/examples/policies/**'
|
||||
- 'src/Cli/**'
|
||||
- '.gitea/workflows/policy-lint.yml'
|
||||
|
||||
jobs:
|
||||
policy-lint:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
|
||||
TZ: UTC
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET 10 RC
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.nuget/packages
|
||||
local-nugets/packages
|
||||
key: policy-lint-nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
|
||||
|
||||
- name: Restore CLI
|
||||
run: |
|
||||
dotnet restore src/Cli/StellaOps.Cli/StellaOps.Cli.csproj --configfile nuget.config
|
||||
|
||||
- name: Lint policies (deterministic)
|
||||
run: |
|
||||
mkdir -p out/policy-lint
|
||||
dotnet run --project src/Cli/StellaOps.Cli/StellaOps.Cli.csproj -- \
|
||||
policy lint docs/examples/policies/*.stella \
|
||||
--format json --no-color \
|
||||
> out/policy-lint/lint.json
|
||||
|
||||
- name: Smoke simulate entrypoint
|
||||
run: |
|
||||
dotnet run --project src/Cli/StellaOps.Cli/StellaOps.Cli.csproj -- policy simulate --help > out/policy-lint/simulate-help.txt
|
||||
|
||||
- name: Upload lint artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: policy-lint
|
||||
path: out/policy-lint
|
||||
retention-days: 7
|
||||
89
.gitea/workflows/policy-simulate.yml
Normal file
89
.gitea/workflows/policy-simulate.yml
Normal file
@@ -0,0 +1,89 @@
|
||||
name: Policy Simulation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'docs/policy/**'
|
||||
- 'docs/examples/policies/**'
|
||||
- 'scripts/policy/**'
|
||||
- '.gitea/workflows/policy-simulate.yml'
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'docs/policy/**'
|
||||
- 'docs/examples/policies/**'
|
||||
- 'scripts/policy/**'
|
||||
- '.gitea/workflows/policy-simulate.yml'
|
||||
|
||||
jobs:
|
||||
policy-simulate:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
|
||||
TZ: UTC
|
||||
THRESHOLD: 0
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET 10 RC
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3.4.0
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.nuget/packages
|
||||
local-nugets/packages
|
||||
key: policy-sim-nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
|
||||
|
||||
- name: Restore CLI
|
||||
run: |
|
||||
dotnet restore src/Cli/StellaOps.Cli/StellaOps.Cli.csproj --configfile nuget.config
|
||||
|
||||
- name: Generate policy signing key (ephemeral)
|
||||
run: |
|
||||
OUT_DIR=out/policy-sign/keys PREFIX=ci-policy COSIGN_PASSWORD= scripts/policy/rotate-key.sh
|
||||
|
||||
- name: Sign sample policy blob
|
||||
run: |
|
||||
export COSIGN_KEY_B64=$(base64 -w0 out/policy-sign/keys/ci-policy-cosign.key)
|
||||
COSIGN_PASSWORD= \
|
||||
scripts/policy/sign-policy.sh --file docs/examples/policies/baseline.stella --out-dir out/policy-sign
|
||||
|
||||
- name: Attest and verify sample policy blob
|
||||
run: |
|
||||
export COSIGN_KEY_B64=$(base64 -w0 out/policy-sign/keys/ci-policy-cosign.key)
|
||||
COSIGN_PASSWORD= \
|
||||
scripts/policy/attest-verify.sh --file docs/examples/policies/baseline.stella --out-dir out/policy-sign
|
||||
|
||||
- name: Run batch policy simulation
|
||||
run: |
|
||||
scripts/policy/batch-simulate.sh
|
||||
|
||||
- name: Upload simulation artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: policy-simulation
|
||||
path: out/policy-sim
|
||||
retention-days: 7
|
||||
|
||||
- name: Upload signing artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: policy-signing
|
||||
path: out/policy-sign
|
||||
retention-days: 7
|
||||
@@ -1,206 +1,209 @@
|
||||
# .gitea/workflows/promote.yml
|
||||
# Manual promotion workflow to copy staged artefacts to production
|
||||
|
||||
name: Promote Feedser (Manual)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
include_docs:
|
||||
description: 'Also promote the generated documentation bundle'
|
||||
required: false
|
||||
default: 'true'
|
||||
type: boolean
|
||||
tag:
|
||||
description: 'Optional build identifier to record in the summary'
|
||||
required: false
|
||||
default: 'latest'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
promote:
|
||||
runs-on: ubuntu-22.04
|
||||
environment: production
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Resolve staging credentials
|
||||
id: staging
|
||||
run: |
|
||||
missing=()
|
||||
|
||||
host="${{ secrets.STAGING_DEPLOYMENT_HOST }}"
|
||||
if [ -z "$host" ]; then host="${{ vars.STAGING_DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ secrets.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ vars.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then missing+=("STAGING_DEPLOYMENT_HOST"); fi
|
||||
|
||||
user="${{ secrets.STAGING_DEPLOYMENT_USERNAME }}"
|
||||
if [ -z "$user" ]; then user="${{ vars.STAGING_DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ secrets.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ vars.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then missing+=("STAGING_DEPLOYMENT_USERNAME"); fi
|
||||
|
||||
path="${{ secrets.STAGING_DEPLOYMENT_PATH }}"
|
||||
if [ -z "$path" ]; then path="${{ vars.STAGING_DEPLOYMENT_PATH }}"; fi
|
||||
if [ -z "$path" ]; then missing+=("STAGING_DEPLOYMENT_PATH")
|
||||
fi
|
||||
|
||||
docs_path="${{ secrets.STAGING_DOCS_PATH }}"
|
||||
if [ -z "$docs_path" ]; then docs_path="${{ vars.STAGING_DOCS_PATH }}"; fi
|
||||
|
||||
key="${{ secrets.STAGING_DEPLOYMENT_KEY }}"
|
||||
if [ -z "$key" ]; then key="${{ secrets.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.STAGING_DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then missing+=("STAGING_DEPLOYMENT_KEY"); fi
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
echo "❌ Missing staging configuration: ${missing[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
key_file="$RUNNER_TEMP/staging_key"
|
||||
printf '%s\n' "$key" > "$key_file"
|
||||
chmod 600 "$key_file"
|
||||
|
||||
echo "host=$host" >> $GITHUB_OUTPUT
|
||||
echo "user=$user" >> $GITHUB_OUTPUT
|
||||
echo "path=$path" >> $GITHUB_OUTPUT
|
||||
echo "docs-path=$docs_path" >> $GITHUB_OUTPUT
|
||||
echo "key-file=$key_file" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Resolve production credentials
|
||||
id: production
|
||||
run: |
|
||||
missing=()
|
||||
|
||||
host="${{ secrets.PRODUCTION_DEPLOYMENT_HOST }}"
|
||||
if [ -z "$host" ]; then host="${{ vars.PRODUCTION_DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ secrets.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ vars.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then missing+=("PRODUCTION_DEPLOYMENT_HOST"); fi
|
||||
|
||||
user="${{ secrets.PRODUCTION_DEPLOYMENT_USERNAME }}"
|
||||
if [ -z "$user" ]; then user="${{ vars.PRODUCTION_DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ secrets.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ vars.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then missing+=("PRODUCTION_DEPLOYMENT_USERNAME"); fi
|
||||
|
||||
path="${{ secrets.PRODUCTION_DEPLOYMENT_PATH }}"
|
||||
if [ -z "$path" ]; then path="${{ vars.PRODUCTION_DEPLOYMENT_PATH }}"; fi
|
||||
if [ -z "$path" ]; then missing+=("PRODUCTION_DEPLOYMENT_PATH")
|
||||
fi
|
||||
|
||||
docs_path="${{ secrets.PRODUCTION_DOCS_PATH }}"
|
||||
if [ -z "$docs_path" ]; then docs_path="${{ vars.PRODUCTION_DOCS_PATH }}"; fi
|
||||
|
||||
key="${{ secrets.PRODUCTION_DEPLOYMENT_KEY }}"
|
||||
if [ -z "$key" ]; then key="${{ secrets.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.PRODUCTION_DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then missing+=("PRODUCTION_DEPLOYMENT_KEY"); fi
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
echo "❌ Missing production configuration: ${missing[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
key_file="$RUNNER_TEMP/production_key"
|
||||
printf '%s\n' "$key" > "$key_file"
|
||||
chmod 600 "$key_file"
|
||||
|
||||
echo "host=$host" >> $GITHUB_OUTPUT
|
||||
echo "user=$user" >> $GITHUB_OUTPUT
|
||||
echo "path=$path" >> $GITHUB_OUTPUT
|
||||
echo "docs-path=$docs_path" >> $GITHUB_OUTPUT
|
||||
echo "key-file=$key_file" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install rsync
|
||||
run: |
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
CACHE_DIR="${CI_CACHE_ROOT:-/tmp}/apt"
|
||||
mkdir -p "$CACHE_DIR"
|
||||
KEY="rsync-$(lsb_release -rs 2>/dev/null || echo unknown)"
|
||||
DEB_DIR="$CACHE_DIR/$KEY"
|
||||
mkdir -p "$DEB_DIR"
|
||||
if ls "$DEB_DIR"/rsync*.deb >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends "$DEB_DIR"/libpopt0*.deb "$DEB_DIR"/rsync*.deb
|
||||
else
|
||||
apt-get update
|
||||
apt-get download rsync libpopt0
|
||||
mv rsync*.deb libpopt0*.deb "$DEB_DIR"/
|
||||
dpkg -i "$DEB_DIR"/libpopt0*.deb "$DEB_DIR"/rsync*.deb || apt-get install -f -y
|
||||
fi
|
||||
|
||||
- name: Fetch staging artefacts
|
||||
id: fetch
|
||||
run: |
|
||||
staging_root="${{ runner.temp }}/staging"
|
||||
mkdir -p "$staging_root/service" "$staging_root/docs"
|
||||
|
||||
echo "📥 Copying service bundle from staging"
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.staging.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.staging.outputs.user }}@${{ steps.staging.outputs.host }}:${{ steps.staging.outputs.path }}/" \
|
||||
"$staging_root/service/"
|
||||
|
||||
if [ "${{ github.event.inputs.include_docs }}" = "true" ] && [ -n "${{ steps.staging.outputs['docs-path'] }}" ]; then
|
||||
echo "📥 Copying documentation bundle from staging"
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.staging.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.staging.outputs.user }}@${{ steps.staging.outputs.host }}:${{ steps.staging.outputs['docs-path'] }}/" \
|
||||
"$staging_root/docs/"
|
||||
else
|
||||
echo "ℹ️ Documentation promotion skipped"
|
||||
fi
|
||||
|
||||
echo "service-dir=$staging_root/service" >> $GITHUB_OUTPUT
|
||||
echo "docs-dir=$staging_root/docs" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Backup production service content
|
||||
run: |
|
||||
ssh -o StrictHostKeyChecking=no -i "${{ steps.production.outputs['key-file'] }}" \
|
||||
"${{ steps.production.outputs.user }}@${{ steps.production.outputs.host }}" \
|
||||
"set -e; TARGET='${{ steps.production.outputs.path }}'; \
|
||||
if [ -d \"$TARGET\" ]; then \
|
||||
parent=\$(dirname \"$TARGET\"); \
|
||||
base=\$(basename \"$TARGET\"); \
|
||||
backup=\"\$parent/\${base}.backup.\$(date +%Y%m%d_%H%M%S)\"; \
|
||||
mkdir -p \"\$backup\"; \
|
||||
rsync -a --delete \"$TARGET/\" \"\$backup/\"; \
|
||||
ls -dt \"\$parent/\${base}.backup.*\" 2>/dev/null | tail -n +6 | xargs rm -rf || true; \
|
||||
echo 'Backup created at ' \"\$backup\"; \
|
||||
else \
|
||||
echo 'Production service path missing; skipping backup'; \
|
||||
fi"
|
||||
|
||||
- name: Publish service to production
|
||||
run: |
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.production.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.fetch.outputs['service-dir'] }}/" \
|
||||
"${{ steps.production.outputs.user }}@${{ steps.production.outputs.host }}:${{ steps.production.outputs.path }}/"
|
||||
|
||||
- name: Promote documentation bundle
|
||||
if: github.event.inputs.include_docs == 'true' && steps.production.outputs['docs-path'] != ''
|
||||
run: |
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.production.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.fetch.outputs['docs-dir'] }}/" \
|
||||
"${{ steps.production.outputs.user }}@${{ steps.production.outputs.host }}:${{ steps.production.outputs['docs-path'] }}/"
|
||||
|
||||
- name: Promotion summary
|
||||
run: |
|
||||
echo "✅ Promotion completed"
|
||||
echo " Tag: ${{ github.event.inputs.tag }}"
|
||||
echo " Service: ${{ steps.staging.outputs.host }} → ${{ steps.production.outputs.host }}"
|
||||
if [ "${{ github.event.inputs.include_docs }}" = "true" ]; then
|
||||
echo " Docs: included"
|
||||
else
|
||||
echo " Docs: skipped"
|
||||
fi
|
||||
# .gitea/workflows/promote.yml
|
||||
# Manual promotion workflow to copy staged artefacts to production
|
||||
|
||||
name: Promote Feedser (Manual)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
include_docs:
|
||||
description: 'Also promote the generated documentation bundle'
|
||||
required: false
|
||||
default: 'true'
|
||||
type: boolean
|
||||
tag:
|
||||
description: 'Optional build identifier to record in the summary'
|
||||
required: false
|
||||
default: 'latest'
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
promote:
|
||||
runs-on: ubuntu-22.04
|
||||
environment: production
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Resolve staging credentials
|
||||
id: staging
|
||||
run: |
|
||||
missing=()
|
||||
|
||||
host="${{ secrets.STAGING_DEPLOYMENT_HOST }}"
|
||||
if [ -z "$host" ]; then host="${{ vars.STAGING_DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ secrets.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ vars.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then missing+=("STAGING_DEPLOYMENT_HOST"); fi
|
||||
|
||||
user="${{ secrets.STAGING_DEPLOYMENT_USERNAME }}"
|
||||
if [ -z "$user" ]; then user="${{ vars.STAGING_DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ secrets.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ vars.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then missing+=("STAGING_DEPLOYMENT_USERNAME"); fi
|
||||
|
||||
path="${{ secrets.STAGING_DEPLOYMENT_PATH }}"
|
||||
if [ -z "$path" ]; then path="${{ vars.STAGING_DEPLOYMENT_PATH }}"; fi
|
||||
if [ -z "$path" ]; then missing+=("STAGING_DEPLOYMENT_PATH")
|
||||
fi
|
||||
|
||||
docs_path="${{ secrets.STAGING_DOCS_PATH }}"
|
||||
if [ -z "$docs_path" ]; then docs_path="${{ vars.STAGING_DOCS_PATH }}"; fi
|
||||
|
||||
key="${{ secrets.STAGING_DEPLOYMENT_KEY }}"
|
||||
if [ -z "$key" ]; then key="${{ secrets.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.STAGING_DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then missing+=("STAGING_DEPLOYMENT_KEY"); fi
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
echo "❌ Missing staging configuration: ${missing[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
key_file="$RUNNER_TEMP/staging_key"
|
||||
printf '%s\n' "$key" > "$key_file"
|
||||
chmod 600 "$key_file"
|
||||
|
||||
echo "host=$host" >> $GITHUB_OUTPUT
|
||||
echo "user=$user" >> $GITHUB_OUTPUT
|
||||
echo "path=$path" >> $GITHUB_OUTPUT
|
||||
echo "docs-path=$docs_path" >> $GITHUB_OUTPUT
|
||||
echo "key-file=$key_file" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Resolve production credentials
|
||||
id: production
|
||||
run: |
|
||||
missing=()
|
||||
|
||||
host="${{ secrets.PRODUCTION_DEPLOYMENT_HOST }}"
|
||||
if [ -z "$host" ]; then host="${{ vars.PRODUCTION_DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ secrets.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then host="${{ vars.DEPLOYMENT_HOST }}"; fi
|
||||
if [ -z "$host" ]; then missing+=("PRODUCTION_DEPLOYMENT_HOST"); fi
|
||||
|
||||
user="${{ secrets.PRODUCTION_DEPLOYMENT_USERNAME }}"
|
||||
if [ -z "$user" ]; then user="${{ vars.PRODUCTION_DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ secrets.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then user="${{ vars.DEPLOYMENT_USERNAME }}"; fi
|
||||
if [ -z "$user" ]; then missing+=("PRODUCTION_DEPLOYMENT_USERNAME"); fi
|
||||
|
||||
path="${{ secrets.PRODUCTION_DEPLOYMENT_PATH }}"
|
||||
if [ -z "$path" ]; then path="${{ vars.PRODUCTION_DEPLOYMENT_PATH }}"; fi
|
||||
if [ -z "$path" ]; then missing+=("PRODUCTION_DEPLOYMENT_PATH")
|
||||
fi
|
||||
|
||||
docs_path="${{ secrets.PRODUCTION_DOCS_PATH }}"
|
||||
if [ -z "$docs_path" ]; then docs_path="${{ vars.PRODUCTION_DOCS_PATH }}"; fi
|
||||
|
||||
key="${{ secrets.PRODUCTION_DEPLOYMENT_KEY }}"
|
||||
if [ -z "$key" ]; then key="${{ secrets.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.PRODUCTION_DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then key="${{ vars.DEPLOYMENT_KEY }}"; fi
|
||||
if [ -z "$key" ]; then missing+=("PRODUCTION_DEPLOYMENT_KEY"); fi
|
||||
|
||||
if [ ${#missing[@]} -gt 0 ]; then
|
||||
echo "❌ Missing production configuration: ${missing[*]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
key_file="$RUNNER_TEMP/production_key"
|
||||
printf '%s\n' "$key" > "$key_file"
|
||||
chmod 600 "$key_file"
|
||||
|
||||
echo "host=$host" >> $GITHUB_OUTPUT
|
||||
echo "user=$user" >> $GITHUB_OUTPUT
|
||||
echo "path=$path" >> $GITHUB_OUTPUT
|
||||
echo "docs-path=$docs_path" >> $GITHUB_OUTPUT
|
||||
echo "key-file=$key_file" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install rsync
|
||||
run: |
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
exit 0
|
||||
fi
|
||||
CACHE_DIR="${CI_CACHE_ROOT:-/tmp}/apt"
|
||||
mkdir -p "$CACHE_DIR"
|
||||
KEY="rsync-$(lsb_release -rs 2>/dev/null || echo unknown)"
|
||||
DEB_DIR="$CACHE_DIR/$KEY"
|
||||
mkdir -p "$DEB_DIR"
|
||||
if ls "$DEB_DIR"/rsync*.deb >/dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends "$DEB_DIR"/libpopt0*.deb "$DEB_DIR"/rsync*.deb
|
||||
else
|
||||
apt-get update
|
||||
apt-get download rsync libpopt0
|
||||
mv rsync*.deb libpopt0*.deb "$DEB_DIR"/
|
||||
dpkg -i "$DEB_DIR"/libpopt0*.deb "$DEB_DIR"/rsync*.deb || apt-get install -f -y
|
||||
fi
|
||||
|
||||
- name: Fetch staging artefacts
|
||||
id: fetch
|
||||
run: |
|
||||
staging_root="${{ runner.temp }}/staging"
|
||||
mkdir -p "$staging_root/service" "$staging_root/docs"
|
||||
|
||||
echo "📥 Copying service bundle from staging"
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.staging.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.staging.outputs.user }}@${{ steps.staging.outputs.host }}:${{ steps.staging.outputs.path }}/" \
|
||||
"$staging_root/service/"
|
||||
|
||||
if [ "${{ github.event.inputs.include_docs }}" = "true" ] && [ -n "${{ steps.staging.outputs['docs-path'] }}" ]; then
|
||||
echo "📥 Copying documentation bundle from staging"
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.staging.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.staging.outputs.user }}@${{ steps.staging.outputs.host }}:${{ steps.staging.outputs['docs-path'] }}/" \
|
||||
"$staging_root/docs/"
|
||||
else
|
||||
echo "ℹ️ Documentation promotion skipped"
|
||||
fi
|
||||
|
||||
echo "service-dir=$staging_root/service" >> $GITHUB_OUTPUT
|
||||
echo "docs-dir=$staging_root/docs" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Backup production service content
|
||||
run: |
|
||||
ssh -o StrictHostKeyChecking=no -i "${{ steps.production.outputs['key-file'] }}" \
|
||||
"${{ steps.production.outputs.user }}@${{ steps.production.outputs.host }}" \
|
||||
"set -e; TARGET='${{ steps.production.outputs.path }}'; \
|
||||
if [ -d \"$TARGET\" ]; then \
|
||||
parent=\$(dirname \"$TARGET\"); \
|
||||
base=\$(basename \"$TARGET\"); \
|
||||
backup=\"\$parent/\${base}.backup.\$(date +%Y%m%d_%H%M%S)\"; \
|
||||
mkdir -p \"\$backup\"; \
|
||||
rsync -a --delete \"$TARGET/\" \"\$backup/\"; \
|
||||
ls -dt \"\$parent/\${base}.backup.*\" 2>/dev/null | tail -n +6 | xargs rm -rf || true; \
|
||||
echo 'Backup created at ' \"\$backup\"; \
|
||||
else \
|
||||
echo 'Production service path missing; skipping backup'; \
|
||||
fi"
|
||||
|
||||
- name: Publish service to production
|
||||
run: |
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.production.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.fetch.outputs['service-dir'] }}/" \
|
||||
"${{ steps.production.outputs.user }}@${{ steps.production.outputs.host }}:${{ steps.production.outputs.path }}/"
|
||||
|
||||
- name: Promote documentation bundle
|
||||
if: github.event.inputs.include_docs == 'true' && steps.production.outputs['docs-path'] != ''
|
||||
run: |
|
||||
rsync -az --delete \
|
||||
-e "ssh -i ${{ steps.production.outputs['key-file'] }} -o StrictHostKeyChecking=no" \
|
||||
"${{ steps.fetch.outputs['docs-dir'] }}/" \
|
||||
"${{ steps.production.outputs.user }}@${{ steps.production.outputs.host }}:${{ steps.production.outputs['docs-path'] }}/"
|
||||
|
||||
- name: Promotion summary
|
||||
run: |
|
||||
echo "✅ Promotion completed"
|
||||
echo " Tag: ${{ github.event.inputs.tag }}"
|
||||
echo " Service: ${{ steps.staging.outputs.host }} → ${{ steps.production.outputs.host }}"
|
||||
if [ "${{ github.event.inputs.include_docs }}" = "true" ]; then
|
||||
echo " Docs: included"
|
||||
else
|
||||
echo " Docs: skipped"
|
||||
fi
|
||||
|
||||
24
.gitea/workflows/provenance-check.yml
Normal file
24
.gitea/workflows/provenance-check.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
name: provenance-check
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Emit provenance summary
|
||||
run: |
|
||||
mkdir -p out/provenance
|
||||
echo "run_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" > out/provenance/summary.txt
|
||||
|
||||
- name: Upload provenance summary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: provenance-summary
|
||||
path: out/provenance/**
|
||||
267
.gitea/workflows/reachability-corpus-ci.yml
Normal file
267
.gitea/workflows/reachability-corpus-ci.yml
Normal file
@@ -0,0 +1,267 @@
|
||||
name: Reachability Corpus Validation
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'tests/reachability/corpus/**'
|
||||
- 'tests/reachability/fixtures/**'
|
||||
- 'tests/reachability/StellaOps.Reachability.FixtureTests/**'
|
||||
- 'scripts/reachability/**'
|
||||
- '.gitea/workflows/reachability-corpus-ci.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'tests/reachability/corpus/**'
|
||||
- 'tests/reachability/fixtures/**'
|
||||
- 'tests/reachability/StellaOps.Reachability.FixtureTests/**'
|
||||
- 'scripts/reachability/**'
|
||||
- '.gitea/workflows/reachability-corpus-ci.yml'
|
||||
|
||||
jobs:
|
||||
validate-corpus:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
|
||||
TZ: UTC
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET 10 RC
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Verify corpus manifest integrity
|
||||
run: |
|
||||
echo "Verifying corpus manifest..."
|
||||
cd tests/reachability/corpus
|
||||
if [ ! -f manifest.json ]; then
|
||||
echo "::error::Corpus manifest.json not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "Manifest exists, checking JSON validity..."
|
||||
python3 -c "import json; json.load(open('manifest.json'))"
|
||||
echo "Manifest is valid JSON"
|
||||
|
||||
- name: Verify reachbench index integrity
|
||||
run: |
|
||||
echo "Verifying reachbench fixtures..."
|
||||
cd tests/reachability/fixtures/reachbench-2025-expanded
|
||||
if [ ! -f INDEX.json ]; then
|
||||
echo "::error::Reachbench INDEX.json not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "INDEX exists, checking JSON validity..."
|
||||
python3 -c "import json; json.load(open('INDEX.json'))"
|
||||
echo "INDEX is valid JSON"
|
||||
|
||||
- name: Restore test project
|
||||
run: dotnet restore tests/reachability/StellaOps.Reachability.FixtureTests/StellaOps.Reachability.FixtureTests.csproj --configfile nuget.config
|
||||
|
||||
- name: Build test project
|
||||
run: dotnet build tests/reachability/StellaOps.Reachability.FixtureTests/StellaOps.Reachability.FixtureTests.csproj -c Release --no-restore
|
||||
|
||||
- name: Run corpus fixture tests
|
||||
run: |
|
||||
dotnet test tests/reachability/StellaOps.Reachability.FixtureTests/StellaOps.Reachability.FixtureTests.csproj \
|
||||
-c Release \
|
||||
--no-build \
|
||||
--logger "trx;LogFileName=corpus-results.trx" \
|
||||
--results-directory ./TestResults \
|
||||
--filter "FullyQualifiedName~CorpusFixtureTests"
|
||||
|
||||
- name: Run reachbench fixture tests
|
||||
run: |
|
||||
dotnet test tests/reachability/StellaOps.Reachability.FixtureTests/StellaOps.Reachability.FixtureTests.csproj \
|
||||
-c Release \
|
||||
--no-build \
|
||||
--logger "trx;LogFileName=reachbench-results.trx" \
|
||||
--results-directory ./TestResults \
|
||||
--filter "FullyQualifiedName~ReachbenchFixtureTests"
|
||||
|
||||
- name: Verify deterministic hashes
|
||||
run: |
|
||||
echo "Verifying SHA-256 hashes in corpus manifest..."
|
||||
chmod +x scripts/reachability/verify_corpus_hashes.sh || true
|
||||
if [ -f scripts/reachability/verify_corpus_hashes.sh ]; then
|
||||
scripts/reachability/verify_corpus_hashes.sh
|
||||
else
|
||||
echo "Hash verification script not found, using inline verification..."
|
||||
cd tests/reachability/corpus
|
||||
python3 << 'EOF'
|
||||
import json
|
||||
import hashlib
|
||||
import sys
|
||||
import os
|
||||
|
||||
with open('manifest.json') as f:
|
||||
manifest = json.load(f)
|
||||
|
||||
errors = []
|
||||
for entry in manifest:
|
||||
case_id = entry['id']
|
||||
lang = entry['language']
|
||||
case_dir = os.path.join(lang, case_id)
|
||||
for filename, expected_hash in entry['files'].items():
|
||||
filepath = os.path.join(case_dir, filename)
|
||||
if not os.path.exists(filepath):
|
||||
errors.append(f"{case_id}: missing {filename}")
|
||||
continue
|
||||
with open(filepath, 'rb') as f:
|
||||
actual_hash = hashlib.sha256(f.read()).hexdigest()
|
||||
if actual_hash != expected_hash:
|
||||
errors.append(f"{case_id}: {filename} hash mismatch (expected {expected_hash}, got {actual_hash})")
|
||||
|
||||
if errors:
|
||||
for err in errors:
|
||||
print(f"::error::{err}")
|
||||
sys.exit(1)
|
||||
print(f"All {len(manifest)} corpus entries verified")
|
||||
EOF
|
||||
fi
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: corpus-test-results-${{ github.run_number }}
|
||||
path: ./TestResults/*.trx
|
||||
retention-days: 14
|
||||
|
||||
validate-ground-truths:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
TZ: UTC
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate ground-truth schema version
|
||||
run: |
|
||||
echo "Validating ground-truth files..."
|
||||
cd tests/reachability
|
||||
python3 << 'EOF'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
EXPECTED_SCHEMA = "reachbench.reachgraph.truth/v1"
|
||||
ALLOWED_VARIANTS = {"reachable", "unreachable"}
|
||||
errors = []
|
||||
|
||||
# Validate corpus ground-truths
|
||||
corpus_manifest = 'corpus/manifest.json'
|
||||
if os.path.exists(corpus_manifest):
|
||||
with open(corpus_manifest) as f:
|
||||
manifest = json.load(f)
|
||||
for entry in manifest:
|
||||
case_id = entry['id']
|
||||
lang = entry['language']
|
||||
truth_path = os.path.join('corpus', lang, case_id, 'ground-truth.json')
|
||||
if not os.path.exists(truth_path):
|
||||
errors.append(f"corpus/{case_id}: missing ground-truth.json")
|
||||
continue
|
||||
with open(truth_path) as f:
|
||||
truth = json.load(f)
|
||||
if truth.get('schema_version') != EXPECTED_SCHEMA:
|
||||
errors.append(f"corpus/{case_id}: wrong schema_version")
|
||||
if truth.get('variant') not in ALLOWED_VARIANTS:
|
||||
errors.append(f"corpus/{case_id}: invalid variant '{truth.get('variant')}'")
|
||||
if not isinstance(truth.get('paths'), list):
|
||||
errors.append(f"corpus/{case_id}: paths must be an array")
|
||||
|
||||
# Validate reachbench ground-truths
|
||||
reachbench_index = 'fixtures/reachbench-2025-expanded/INDEX.json'
|
||||
if os.path.exists(reachbench_index):
|
||||
with open(reachbench_index) as f:
|
||||
index = json.load(f)
|
||||
for case in index.get('cases', []):
|
||||
case_id = case['id']
|
||||
case_path = case.get('path', os.path.join('cases', case_id))
|
||||
for variant in ['reachable', 'unreachable']:
|
||||
truth_path = os.path.join('fixtures/reachbench-2025-expanded', case_path, 'images', variant, 'reachgraph.truth.json')
|
||||
if not os.path.exists(truth_path):
|
||||
errors.append(f"reachbench/{case_id}/{variant}: missing reachgraph.truth.json")
|
||||
continue
|
||||
with open(truth_path) as f:
|
||||
truth = json.load(f)
|
||||
if not truth.get('schema_version'):
|
||||
errors.append(f"reachbench/{case_id}/{variant}: missing schema_version")
|
||||
if not isinstance(truth.get('paths'), list):
|
||||
errors.append(f"reachbench/{case_id}/{variant}: paths must be an array")
|
||||
|
||||
if errors:
|
||||
for err in errors:
|
||||
print(f"::error::{err}")
|
||||
sys.exit(1)
|
||||
print("All ground-truth files validated successfully")
|
||||
EOF
|
||||
|
||||
determinism-check:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
TZ: UTC
|
||||
needs: validate-corpus
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Verify JSON determinism (sorted keys, no trailing whitespace)
|
||||
run: |
|
||||
echo "Checking JSON determinism..."
|
||||
cd tests/reachability
|
||||
python3 << 'EOF'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
def check_json_sorted(filepath):
|
||||
"""Check if JSON has sorted keys (deterministic)."""
|
||||
with open(filepath) as f:
|
||||
content = f.read()
|
||||
parsed = json.loads(content)
|
||||
reserialized = json.dumps(parsed, sort_keys=True, indent=2)
|
||||
# Normalize line endings
|
||||
content_normalized = content.replace('\r\n', '\n').strip()
|
||||
reserialized_normalized = reserialized.strip()
|
||||
return content_normalized == reserialized_normalized
|
||||
|
||||
errors = []
|
||||
json_files = []
|
||||
|
||||
# Collect JSON files from corpus
|
||||
for root, dirs, files in os.walk('corpus'):
|
||||
for f in files:
|
||||
if f.endswith('.json'):
|
||||
json_files.append(os.path.join(root, f))
|
||||
|
||||
# Check determinism
|
||||
non_deterministic = []
|
||||
for filepath in json_files:
|
||||
try:
|
||||
if not check_json_sorted(filepath):
|
||||
non_deterministic.append(filepath)
|
||||
except json.JSONDecodeError as e:
|
||||
errors.append(f"{filepath}: invalid JSON - {e}")
|
||||
|
||||
if non_deterministic:
|
||||
print(f"::warning::Found {len(non_deterministic)} non-deterministic JSON files (keys not sorted or whitespace differs)")
|
||||
for f in non_deterministic[:10]:
|
||||
print(f" - {f}")
|
||||
if len(non_deterministic) > 10:
|
||||
print(f" ... and {len(non_deterministic) - 10} more")
|
||||
|
||||
if errors:
|
||||
for err in errors:
|
||||
print(f"::error::{err}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Checked {len(json_files)} JSON files")
|
||||
EOF
|
||||
19
.gitea/workflows/release-manifest-verify.yml
Normal file
19
.gitea/workflows/release-manifest-verify.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
name: release-manifest-verify
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- deploy/releases/2025.09-stable.yaml
|
||||
- deploy/releases/2025.09-airgap.yaml
|
||||
- deploy/downloads/manifest.json
|
||||
- ops/devops/release/check_release_manifest.py
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Validate release & downloads manifests
|
||||
run: |
|
||||
python ops/devops/release/check_release_manifest.py
|
||||
120
.gitea/workflows/release-validation.yml
Normal file
120
.gitea/workflows/release-validation.yml
Normal file
@@ -0,0 +1,120 @@
|
||||
name: Release Validation
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'deploy/**'
|
||||
- 'scripts/release/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.x'
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_PREFIX: stellaops
|
||||
|
||||
jobs:
|
||||
validate-manifests:
|
||||
name: Validate Release Manifests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate Helm charts
|
||||
run: |
|
||||
helm lint deploy/helm/stellaops
|
||||
helm template stellaops deploy/helm/stellaops --dry-run
|
||||
|
||||
- name: Validate Kubernetes manifests
|
||||
run: |
|
||||
for f in deploy/k8s/*.yaml; do
|
||||
kubectl apply --dry-run=client -f "$f" || exit 1
|
||||
done
|
||||
|
||||
- name: Check required images exist
|
||||
run: |
|
||||
REQUIRED_IMAGES=(
|
||||
"concelier"
|
||||
"scanner"
|
||||
"authority"
|
||||
"signer"
|
||||
"attestor"
|
||||
"excititor"
|
||||
"policy"
|
||||
"scheduler"
|
||||
"notify"
|
||||
)
|
||||
for img in "${REQUIRED_IMAGES[@]}"; do
|
||||
echo "Checking $img..."
|
||||
# Validate Dockerfile exists
|
||||
if [ ! -f "src/${img^}/Dockerfile" ] && [ ! -f "deploy/docker/${img}/Dockerfile" ]; then
|
||||
echo "Warning: Dockerfile not found for $img"
|
||||
fi
|
||||
done
|
||||
|
||||
validate-checksums:
|
||||
name: Validate Artifact Checksums
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Verify SHA256SUMS files
|
||||
run: |
|
||||
find . -name "SHA256SUMS" -type f | while read f; do
|
||||
dir=$(dirname "$f")
|
||||
echo "Validating $f..."
|
||||
cd "$dir"
|
||||
if ! sha256sum -c SHA256SUMS --quiet 2>/dev/null; then
|
||||
echo "Warning: Checksum mismatch in $dir"
|
||||
fi
|
||||
cd - > /dev/null
|
||||
done
|
||||
|
||||
validate-schemas:
|
||||
name: Validate Schema Integrity
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install ajv-cli
|
||||
run: npm install -g ajv-cli ajv-formats
|
||||
|
||||
- name: Validate JSON schemas
|
||||
run: |
|
||||
for schema in docs/schemas/*.schema.json; do
|
||||
echo "Validating $schema..."
|
||||
ajv compile -s "$schema" --spec=draft2020 || echo "Warning: $schema validation issue"
|
||||
done
|
||||
|
||||
release-notes:
|
||||
name: Generate Release Notes
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [validate-manifests, validate-checksums, validate-schemas]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Generate changelog
|
||||
run: |
|
||||
PREV_TAG=$(git describe --abbrev=0 --tags HEAD^ 2>/dev/null || echo "")
|
||||
if [ -n "$PREV_TAG" ]; then
|
||||
echo "## Changes since $PREV_TAG" > RELEASE_NOTES.md
|
||||
git log --pretty=format:"- %s (%h)" "$PREV_TAG"..HEAD >> RELEASE_NOTES.md
|
||||
else
|
||||
echo "## Initial Release" > RELEASE_NOTES.md
|
||||
fi
|
||||
|
||||
- name: Upload release notes
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-notes
|
||||
path: RELEASE_NOTES.md
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
build-release:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100-rc.1.25451.107'
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
REGISTRY: registry.stella-ops.org
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -44,6 +44,9 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Validate NuGet restore source ordering
|
||||
run: python3 ops/devops/validate_restore_sources.py
|
||||
|
||||
@@ -239,3 +242,10 @@ jobs:
|
||||
name: stellaops-release-${{ steps.meta.outputs.version }}
|
||||
path: out/release
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload debug artefacts (build-id store)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: stellaops-debug-${{ steps.meta.outputs.version }}
|
||||
path: out/release/debug
|
||||
if-no-files-found: error
|
||||
|
||||
198
.gitea/workflows/risk-bundle-ci.yml
Normal file
198
.gitea/workflows/risk-bundle-ci.yml
Normal file
@@ -0,0 +1,198 @@
|
||||
name: Risk Bundle CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/ExportCenter/StellaOps.ExportCenter.RiskBundles/**'
|
||||
- 'src/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.Worker/**'
|
||||
- 'ops/devops/risk-bundle/**'
|
||||
- '.gitea/workflows/risk-bundle-ci.yml'
|
||||
- 'docs/modules/export-center/operations/risk-bundle-*.md'
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
paths:
|
||||
- 'src/ExportCenter/StellaOps.ExportCenter.RiskBundles/**'
|
||||
- 'src/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.Worker/**'
|
||||
- 'ops/devops/risk-bundle/**'
|
||||
- '.gitea/workflows/risk-bundle-ci.yml'
|
||||
- 'docs/modules/export-center/operations/risk-bundle-*.md'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
include_osv:
|
||||
description: 'Include OSV providers (larger bundle)'
|
||||
type: boolean
|
||||
default: false
|
||||
publish_checksums:
|
||||
description: 'Publish checksums to artifact store'
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
risk-bundle-build:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.100'
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
BUNDLE_OUTPUT: ${{ github.workspace }}/.artifacts/risk-bundle
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Set up .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
include-prerelease: true
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/ExportCenter/StellaOps.ExportCenter.RiskBundles/StellaOps.ExportCenter.RiskBundles.csproj
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/ExportCenter/StellaOps.ExportCenter.RiskBundles/StellaOps.ExportCenter.RiskBundles.csproj -c Release /p:ContinuousIntegrationBuild=true
|
||||
|
||||
- name: Test RiskBundle unit tests
|
||||
run: |
|
||||
mkdir -p $ARTIFACT_DIR
|
||||
dotnet test src/ExportCenter/StellaOps.ExportCenter/StellaOps.ExportCenter.Tests/StellaOps.ExportCenter.Tests.csproj \
|
||||
-c Release \
|
||||
--filter "FullyQualifiedName~RiskBundle" \
|
||||
--logger "trx;LogFileName=risk-bundle-tests.trx" \
|
||||
--results-directory $ARTIFACT_DIR
|
||||
|
||||
- name: Build risk bundle (fixtures)
|
||||
run: |
|
||||
mkdir -p $BUNDLE_OUTPUT
|
||||
ops/devops/risk-bundle/build-bundle.sh --output "$BUNDLE_OUTPUT" --fixtures-only
|
||||
|
||||
- name: Verify bundle integrity
|
||||
run: ops/devops/risk-bundle/verify-bundle.sh "$BUNDLE_OUTPUT/risk-bundle.tar.gz"
|
||||
|
||||
- name: Generate checksums
|
||||
run: |
|
||||
cd $BUNDLE_OUTPUT
|
||||
sha256sum risk-bundle.tar.gz > risk-bundle.tar.gz.sha256
|
||||
sha256sum manifest.json > manifest.json.sha256
|
||||
cat risk-bundle.tar.gz.sha256 manifest.json.sha256 > checksums.txt
|
||||
echo "Bundle checksums:"
|
||||
cat checksums.txt
|
||||
|
||||
- name: Upload risk bundle artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: risk-bundle-artifacts
|
||||
path: |
|
||||
${{ env.BUNDLE_OUTPUT }}/risk-bundle.tar.gz
|
||||
${{ env.BUNDLE_OUTPUT }}/risk-bundle.tar.gz.sig
|
||||
${{ env.BUNDLE_OUTPUT }}/manifest.json
|
||||
${{ env.BUNDLE_OUTPUT }}/checksums.txt
|
||||
${{ env.ARTIFACT_DIR }}/*.trx
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: risk-bundle-test-results
|
||||
path: ${{ env.ARTIFACT_DIR }}/*.trx
|
||||
|
||||
risk-bundle-offline-kit:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: risk-bundle-build
|
||||
env:
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
OFFLINE_KIT_DIR: ${{ github.workspace }}/.artifacts/offline-kit
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download risk bundle artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: risk-bundle-artifacts
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
|
||||
- name: Package for offline kit
|
||||
run: |
|
||||
mkdir -p $OFFLINE_KIT_DIR/risk-bundles
|
||||
cp $ARTIFACT_DIR/risk-bundle.tar.gz $OFFLINE_KIT_DIR/risk-bundles/
|
||||
cp $ARTIFACT_DIR/risk-bundle.tar.gz.sig $OFFLINE_KIT_DIR/risk-bundles/ 2>/dev/null || true
|
||||
cp $ARTIFACT_DIR/manifest.json $OFFLINE_KIT_DIR/risk-bundles/
|
||||
cp $ARTIFACT_DIR/checksums.txt $OFFLINE_KIT_DIR/risk-bundles/
|
||||
|
||||
# Create offline kit manifest entry
|
||||
cat > $OFFLINE_KIT_DIR/risk-bundles/kit-manifest.json <<EOF
|
||||
{
|
||||
"component": "risk-bundle",
|
||||
"version": "$(date -u +%Y%m%d-%H%M%S)",
|
||||
"files": [
|
||||
{"path": "risk-bundle.tar.gz", "checksum_file": "risk-bundle.tar.gz.sha256"},
|
||||
{"path": "manifest.json", "checksum_file": "manifest.json.sha256"}
|
||||
],
|
||||
"verification": {
|
||||
"checksums": "checksums.txt",
|
||||
"signature": "risk-bundle.tar.gz.sig"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Verify offline kit structure
|
||||
run: |
|
||||
echo "Offline kit structure:"
|
||||
find $OFFLINE_KIT_DIR -type f
|
||||
echo ""
|
||||
echo "Checksum verification:"
|
||||
cd $OFFLINE_KIT_DIR/risk-bundles
|
||||
sha256sum -c checksums.txt
|
||||
|
||||
- name: Upload offline kit
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: risk-bundle-offline-kit
|
||||
path: ${{ env.OFFLINE_KIT_DIR }}
|
||||
|
||||
publish-checksums:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: risk-bundle-build
|
||||
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event.inputs.publish_checksums == 'true')
|
||||
env:
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/.artifacts
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download risk bundle artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: risk-bundle-artifacts
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
|
||||
- name: Publish checksums
|
||||
run: |
|
||||
echo "Publishing checksums for risk bundle..."
|
||||
CHECKSUM_DIR=out/checksums/risk-bundle/$(date -u +%Y-%m-%d)
|
||||
mkdir -p $CHECKSUM_DIR
|
||||
cp $ARTIFACT_DIR/checksums.txt $CHECKSUM_DIR/
|
||||
cp $ARTIFACT_DIR/manifest.json $CHECKSUM_DIR/
|
||||
|
||||
# Create latest symlink manifest
|
||||
cat > out/checksums/risk-bundle/latest.json <<EOF
|
||||
{
|
||||
"date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"path": "$(date -u +%Y-%m-%d)/checksums.txt",
|
||||
"manifest": "$(date -u +%Y-%m-%d)/manifest.json"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "Checksums published to $CHECKSUM_DIR"
|
||||
cat $CHECKSUM_DIR/checksums.txt
|
||||
|
||||
- name: Upload published checksums
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: risk-bundle-published-checksums
|
||||
path: out/checksums/risk-bundle/
|
||||
57
.gitea/workflows/scanner-analyzers-release.yml
Normal file
57
.gitea/workflows/scanner-analyzers-release.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
name: scanner-analyzers-release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
rid:
|
||||
description: "RID (e.g., linux-x64)"
|
||||
required: false
|
||||
default: "linux-x64"
|
||||
|
||||
jobs:
|
||||
build-analyzers:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.100"
|
||||
|
||||
- name: Install syft (SBOM)
|
||||
uses: anchore/sbom-action/download-syft@v0
|
||||
|
||||
- name: Package PHP analyzer
|
||||
run: |
|
||||
chmod +x scripts/scanner/package-analyzer.sh
|
||||
RID="${{ github.event.inputs.rid }}" scripts/scanner/package-analyzer.sh src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Php/StellaOps.Scanner.Analyzers.Lang.Php.csproj php-analyzer
|
||||
|
||||
- name: Package Ruby analyzer
|
||||
run: |
|
||||
RID="${{ github.event.inputs.rid }}" scripts/scanner/package-analyzer.sh src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Ruby/StellaOps.Scanner.Analyzers.Lang.Ruby.csproj ruby-analyzer
|
||||
|
||||
- name: Package Native analyzer
|
||||
run: |
|
||||
RID="${{ github.event.inputs.rid }}" scripts/scanner/package-analyzer.sh src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Native/StellaOps.Scanner.Analyzers.Native.csproj native-analyzer
|
||||
|
||||
- name: Package Java analyzer
|
||||
run: |
|
||||
RID="${{ github.event.inputs.rid }}" scripts/scanner/package-analyzer.sh src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Java/StellaOps.Scanner.Analyzers.Lang.Java.csproj java-analyzer
|
||||
|
||||
- name: Package DotNet analyzer
|
||||
run: |
|
||||
RID="${{ github.event.inputs.rid }}" scripts/scanner/package-analyzer.sh src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/StellaOps.Scanner.Analyzers.Lang.DotNet.csproj dotnet-analyzer
|
||||
|
||||
- name: Package Node analyzer
|
||||
run: |
|
||||
RID="${{ github.event.inputs.rid }}" scripts/scanner/package-analyzer.sh src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.Lang.Node/StellaOps.Scanner.Analyzers.Lang.Node.csproj node-analyzer
|
||||
|
||||
- name: Upload analyzer artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: scanner-analyzers-${{ github.event.inputs.rid }}
|
||||
path: out/scanner-analyzers/**
|
||||
133
.gitea/workflows/scanner-analyzers.yml
Normal file
133
.gitea/workflows/scanner-analyzers.yml
Normal file
@@ -0,0 +1,133 @@
|
||||
name: Scanner Analyzers
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.*/**'
|
||||
- 'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.*/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/Scanner/__Libraries/StellaOps.Scanner.Analyzers.*/**'
|
||||
- 'src/Scanner/__Tests/StellaOps.Scanner.Analyzers.*/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
DOTNET_VERSION: '10.0.x'
|
||||
|
||||
jobs:
|
||||
discover-analyzers:
|
||||
name: Discover Analyzers
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
analyzers: ${{ steps.find.outputs.analyzers }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Find analyzer projects
|
||||
id: find
|
||||
run: |
|
||||
ANALYZERS=$(find src/Scanner/__Libraries -name "StellaOps.Scanner.Analyzers.*.csproj" -exec dirname {} \; | xargs -I {} basename {} | sort -u | jq -R -s -c 'split("\n")[:-1]')
|
||||
echo "analyzers=$ANALYZERS" >> $GITHUB_OUTPUT
|
||||
|
||||
build-analyzers:
|
||||
name: Build Analyzers
|
||||
runs-on: ubuntu-latest
|
||||
needs: discover-analyzers
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
analyzer: ${{ fromJson(needs.discover-analyzers.outputs.analyzers) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/Scanner/__Libraries/${{ matrix.analyzer }}/
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/Scanner/__Libraries/${{ matrix.analyzer }}/ --no-restore
|
||||
|
||||
test-lang-analyzers:
|
||||
name: Test Language Analyzers
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-analyzers
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Run Bun analyzer tests
|
||||
run: |
|
||||
if [ -d "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Bun.Tests" ]; then
|
||||
dotnet test src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Bun.Tests/ --verbosity normal
|
||||
fi
|
||||
|
||||
- name: Run Node analyzer tests
|
||||
run: |
|
||||
if [ -d "src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Node.Tests" ]; then
|
||||
dotnet test src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Node.Tests/ --verbosity normal
|
||||
fi
|
||||
|
||||
fixture-validation:
|
||||
name: Validate Test Fixtures
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Validate fixture structure
|
||||
run: |
|
||||
find src/Scanner/__Tests -name "expected.json" | while read f; do
|
||||
echo "Validating $f..."
|
||||
if ! jq empty "$f" 2>/dev/null; then
|
||||
echo "Error: Invalid JSON in $f"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Check fixture completeness
|
||||
run: |
|
||||
find src/Scanner/__Tests -type d -name "Fixtures" | while read fixtures_dir; do
|
||||
echo "Checking $fixtures_dir..."
|
||||
find "$fixtures_dir" -mindepth 1 -maxdepth 1 -type d | while read test_case; do
|
||||
if [ ! -f "$test_case/expected.json" ]; then
|
||||
echo "Warning: $test_case missing expected.json"
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
determinism-check:
|
||||
name: Verify Deterministic Output
|
||||
runs-on: ubuntu-latest
|
||||
needs: test-lang-analyzers
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: ${{ env.DOTNET_VERSION }}
|
||||
|
||||
- name: Run determinism tests
|
||||
run: |
|
||||
# Run scanner on same input twice, compare outputs
|
||||
if [ -d "tests/fixtures/determinism" ]; then
|
||||
dotnet test --filter "Category=Determinism" --verbosity normal
|
||||
fi
|
||||
29
.gitea/workflows/scanner-determinism.yml
Normal file
29
.gitea/workflows/scanner-determinism.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
name: scanner-determinism
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
determinism:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "10.0.100"
|
||||
|
||||
- name: Run determinism harness
|
||||
run: |
|
||||
chmod +x scripts/scanner/determinism-run.sh
|
||||
scripts/scanner/determinism-run.sh
|
||||
|
||||
- name: Upload determinism artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: scanner-determinism
|
||||
path: out/scanner-determinism/**
|
||||
38
.gitea/workflows/sdk-generator.yml
Normal file
38
.gitea/workflows/sdk-generator.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
name: sdk-generator-smoke
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "src/Sdk/StellaOps.Sdk.Generator/**"
|
||||
- "package.json"
|
||||
pull_request:
|
||||
paths:
|
||||
- "src/Sdk/StellaOps.Sdk.Generator/**"
|
||||
- "package.json"
|
||||
|
||||
jobs:
|
||||
sdk-smoke:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "18"
|
||||
|
||||
- name: Setup Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "21"
|
||||
|
||||
- name: Install npm deps (scripts only)
|
||||
run: npm install --ignore-scripts --no-progress --no-audit --no-fund
|
||||
|
||||
- name: Run SDK smoke suite (TS/Python/Go/Java)
|
||||
run: npm run sdk:smoke
|
||||
92
.gitea/workflows/sdk-publish.yml
Normal file
92
.gitea/workflows/sdk-publish.yml
Normal file
@@ -0,0 +1,92 @@
|
||||
name: SDK Publish & Sign
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/Sdk/**'
|
||||
- 'ops/devops/sdk/**'
|
||||
- 'scripts/sdk/**'
|
||||
- '.gitea/workflows/sdk-publish.yml'
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/Sdk/**'
|
||||
- 'ops/devops/sdk/**'
|
||||
- 'scripts/sdk/**'
|
||||
- '.gitea/workflows/sdk-publish.yml'
|
||||
|
||||
jobs:
|
||||
sdk-publish:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
|
||||
TZ: UTC
|
||||
SDK_NUGET_SOURCE: ${{ secrets.SDK_NUGET_SOURCE || 'local-nugets/packages' }}
|
||||
SDK_NUGET_API_KEY: ${{ secrets.SDK_NUGET_API_KEY }}
|
||||
SDK_SIGNING_CERT_B64: ${{ secrets.SDK_SIGNING_CERT_B64 }}
|
||||
SDK_SIGNING_CERT_PASSWORD: ${{ secrets.SDK_SIGNING_CERT_PASSWORD }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET 10 RC
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.nuget/packages
|
||||
local-nugets/packages
|
||||
key: sdk-nuget-${{ runner.os }}-${{ hashFiles('src/Sdk/**/*.csproj') }}
|
||||
|
||||
- name: Restore (best effort; skipped if no csproj)
|
||||
run: |
|
||||
set -e
|
||||
if compgen -G "src/Sdk/**/*.csproj" > /dev/null; then
|
||||
dotnet restore --configfile nuget.config src/Sdk/StellaOps.Sdk.Release/StellaOps.Sdk.Release.csproj || true
|
||||
else
|
||||
echo "No SDK csproj present; skipping restore."
|
||||
fi
|
||||
|
||||
- name: Build & Test (best effort)
|
||||
run: |
|
||||
set -e
|
||||
if compgen -G "src/Sdk/**/*.csproj" > /dev/null; then
|
||||
dotnet build src/Sdk/StellaOps.Sdk.Release/StellaOps.Sdk.Release.csproj -c Release --no-restore || true
|
||||
if compgen -G "src/Sdk/**/__Tests/**/*.csproj" > /dev/null; then
|
||||
dotnet test src/Sdk/**/__Tests/**/*.csproj -c Release --no-build --logger "trx;LogFileName=sdk-tests.trx" || true
|
||||
fi
|
||||
else
|
||||
echo "No SDK csproj present; skipping build/test."
|
||||
fi
|
||||
|
||||
- name: Sign packages (if present)
|
||||
run: |
|
||||
chmod +x scripts/sdk/sign-packages.sh
|
||||
scripts/sdk/sign-packages.sh
|
||||
|
||||
- name: Publish packages (if present)
|
||||
run: |
|
||||
chmod +x scripts/sdk/publish.sh
|
||||
scripts/sdk/publish.sh
|
||||
|
||||
- name: Upload SDK artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sdk-artifacts
|
||||
path: |
|
||||
out/sdk
|
||||
local-nugets/packages/*.nupkg
|
||||
if-no-files-found: warn
|
||||
retention-days: 7
|
||||
75
.gitea/workflows/signals-ci.yml
Normal file
75
.gitea/workflows/signals-ci.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
name: Signals CI & Image
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/Signals/**'
|
||||
- '.gitea/workflows/signals-ci.yml'
|
||||
- 'ops/devops/signals/**'
|
||||
- 'helm/signals/**'
|
||||
- 'scripts/signals/**'
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/Signals/**'
|
||||
- '.gitea/workflows/signals-ci.yml'
|
||||
- 'ops/devops/signals/**'
|
||||
- 'helm/signals/**'
|
||||
- 'scripts/signals/**'
|
||||
|
||||
jobs:
|
||||
signals-ci:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
|
||||
TZ: UTC
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET 10 RC
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.nuget/packages
|
||||
local-nugets/packages
|
||||
key: signals-nuget-${{ runner.os }}-${{ hashFiles('src/Signals/**/*.csproj') }}
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/Signals/StellaOps.Signals.sln --configfile nuget.config
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/Signals/StellaOps.Signals.sln -c Release --no-restore
|
||||
|
||||
- name: Test
|
||||
run: dotnet test src/Signals/__Tests/StellaOps.Signals.Tests/StellaOps.Signals.Tests.csproj -c Release --no-build --logger "trx;LogFileName=signals-tests.trx"
|
||||
|
||||
- name: Publish service
|
||||
run: dotnet publish src/Signals/StellaOps.Signals/StellaOps.Signals.csproj -c Release -o out/signals/publish --no-build
|
||||
|
||||
- name: Build container image
|
||||
run: |
|
||||
chmod +x scripts/signals/build.sh
|
||||
scripts/signals/build.sh
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signals-offline-kit
|
||||
path: |
|
||||
out/signals
|
||||
out/signals/signals-image.tar
|
||||
retention-days: 7
|
||||
183
.gitea/workflows/signals-dsse-sign.yml
Normal file
183
.gitea/workflows/signals-dsse-sign.yml
Normal file
@@ -0,0 +1,183 @@
|
||||
name: Signals DSSE Sign & Evidence Locker
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
out_dir:
|
||||
description: "Output directory for signed artifacts"
|
||||
required: false
|
||||
default: "evidence-locker/signals/2025-12-01"
|
||||
allow_dev_key:
|
||||
description: "Allow dev key for testing (1=yes, 0=no)"
|
||||
required: false
|
||||
default: "0"
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'docs/modules/signals/decay/**'
|
||||
- 'docs/modules/signals/unknowns/**'
|
||||
- 'docs/modules/signals/heuristics/**'
|
||||
- 'docs/modules/signals/SHA256SUMS'
|
||||
- 'tools/cosign/sign-signals.sh'
|
||||
|
||||
jobs:
|
||||
sign-signals-artifacts:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
OUT_DIR: ${{ github.event.inputs.out_dir || 'evidence-locker/signals/2025-12-01' }}
|
||||
COSIGN_ALLOW_DEV_KEY: ${{ github.event.inputs.allow_dev_key || '0' }}
|
||||
CI_EVIDENCE_LOCKER_TOKEN: ${{ secrets.CI_EVIDENCE_LOCKER_TOKEN || vars.CI_EVIDENCE_LOCKER_TOKEN }}
|
||||
EVIDENCE_LOCKER_URL: ${{ secrets.EVIDENCE_LOCKER_URL || vars.EVIDENCE_LOCKER_URL }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.2.4'
|
||||
|
||||
- name: Check signing key configured
|
||||
run: |
|
||||
if [[ -z "$COSIGN_PRIVATE_KEY_B64" && "$COSIGN_ALLOW_DEV_KEY" != "1" ]]; then
|
||||
echo "::error::COSIGN_PRIVATE_KEY_B64 is missing and dev key fallback is disabled. Set COSIGN_PRIVATE_KEY_B64 (and COSIGN_PASSWORD if needed) or rerun with allow_dev_key=1 for smoke only."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$COSIGN_ALLOW_DEV_KEY" == "1" ]]; then
|
||||
echo "::notice::Using dev key for signing (allow_dev_key=1) - not suitable for production uploads."
|
||||
fi
|
||||
|
||||
- name: Verify artifacts exist
|
||||
run: |
|
||||
cd docs/modules/signals
|
||||
sha256sum -c SHA256SUMS
|
||||
echo "All artifacts verified against SHA256SUMS"
|
||||
|
||||
- name: Check signing key availability
|
||||
id: check-key
|
||||
run: |
|
||||
if [[ -n "$COSIGN_PRIVATE_KEY_B64" ]]; then
|
||||
echo "key_source=ci_secret" >> "$GITHUB_OUTPUT"
|
||||
echo "Signing key available via CI secret"
|
||||
elif [[ "$COSIGN_ALLOW_DEV_KEY" == "1" ]]; then
|
||||
echo "key_source=dev_key" >> "$GITHUB_OUTPUT"
|
||||
echo "[warn] Using development key - NOT for production Evidence Locker"
|
||||
else
|
||||
echo "key_source=none" >> "$GITHUB_OUTPUT"
|
||||
echo "::error::No signing key available. Set COSIGN_PRIVATE_KEY_B64 secret or enable dev key."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Sign signals artifacts
|
||||
run: |
|
||||
chmod +x tools/cosign/sign-signals.sh
|
||||
OUT_DIR="${OUT_DIR}" tools/cosign/sign-signals.sh
|
||||
|
||||
- name: Verify signatures
|
||||
run: |
|
||||
cd "$OUT_DIR"
|
||||
# List generated artifacts
|
||||
echo "=== Generated Artifacts ==="
|
||||
ls -la
|
||||
echo ""
|
||||
echo "=== SHA256SUMS ==="
|
||||
cat SHA256SUMS
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signals-dsse-signed-${{ github.run_number }}
|
||||
path: |
|
||||
${{ env.OUT_DIR }}/*.sigstore.json
|
||||
${{ env.OUT_DIR }}/*.dsse
|
||||
${{ env.OUT_DIR }}/SHA256SUMS
|
||||
if-no-files-found: error
|
||||
retention-days: 90
|
||||
|
||||
- name: Push to Evidence Locker
|
||||
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN != '' && env.EVIDENCE_LOCKER_URL != '' }}
|
||||
env:
|
||||
TOKEN: ${{ env.CI_EVIDENCE_LOCKER_TOKEN }}
|
||||
URL: ${{ env.EVIDENCE_LOCKER_URL }}
|
||||
run: |
|
||||
tar -cf /tmp/signals-dsse.tar -C "$OUT_DIR" .
|
||||
curl -f -X PUT "$URL/signals/dsse/$(date -u +%Y-%m-%d)/signals-dsse.tar" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
--data-binary @/tmp/signals-dsse.tar
|
||||
echo "Pushed to Evidence Locker"
|
||||
|
||||
- name: Evidence Locker skip notice
|
||||
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN == '' || env.EVIDENCE_LOCKER_URL == '' }}
|
||||
run: |
|
||||
echo "::notice::Evidence Locker push skipped (CI_EVIDENCE_LOCKER_TOKEN or EVIDENCE_LOCKER_URL not set)"
|
||||
echo "Artifacts available as workflow artifact for manual ingestion"
|
||||
|
||||
verify-signatures:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: sign-signals-artifacts
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download signed artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: signals-dsse-signed-${{ github.run_number }}
|
||||
path: signed-artifacts/
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.2.4'
|
||||
|
||||
- name: Verify decay config signature
|
||||
run: |
|
||||
if [[ -f signed-artifacts/confidence_decay_config.sigstore.json ]]; then
|
||||
cosign verify-blob \
|
||||
--key tools/cosign/cosign.dev.pub \
|
||||
--bundle signed-artifacts/confidence_decay_config.sigstore.json \
|
||||
docs/modules/signals/decay/confidence_decay_config.yaml \
|
||||
&& echo "✓ decay config signature verified" \
|
||||
|| echo "::warning::Signature verification failed (may need production public key)"
|
||||
fi
|
||||
|
||||
- name: Verify unknowns manifest signature
|
||||
run: |
|
||||
if [[ -f signed-artifacts/unknowns_scoring_manifest.sigstore.json ]]; then
|
||||
cosign verify-blob \
|
||||
--key tools/cosign/cosign.dev.pub \
|
||||
--bundle signed-artifacts/unknowns_scoring_manifest.sigstore.json \
|
||||
docs/modules/signals/unknowns/unknowns_scoring_manifest.json \
|
||||
&& echo "✓ unknowns manifest signature verified" \
|
||||
|| echo "::warning::Signature verification failed (may need production public key)"
|
||||
fi
|
||||
|
||||
- name: Verify heuristics catalog signature
|
||||
run: |
|
||||
if [[ -f signed-artifacts/heuristics_catalog.sigstore.json ]]; then
|
||||
cosign verify-blob \
|
||||
--key tools/cosign/cosign.dev.pub \
|
||||
--bundle signed-artifacts/heuristics_catalog.sigstore.json \
|
||||
docs/modules/signals/heuristics/heuristics.catalog.json \
|
||||
&& echo "✓ heuristics catalog signature verified" \
|
||||
|| echo "::warning::Signature verification failed (may need production public key)"
|
||||
fi
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "## Signals DSSE Signing Summary" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "| Artifact | Status |" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "|----------|--------|" >> "$GITHUB_STEP_SUMMARY"
|
||||
for f in signed-artifacts/*.sigstore.json signed-artifacts/*.dsse; do
|
||||
[[ -f "$f" ]] && echo "| $(basename $f) | ✓ Signed |" >> "$GITHUB_STEP_SUMMARY"
|
||||
done
|
||||
echo "" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "Run ID: ${{ github.run_number }}" >> "$GITHUB_STEP_SUMMARY"
|
||||
106
.gitea/workflows/signals-evidence-locker.yml
Normal file
106
.gitea/workflows/signals-evidence-locker.yml
Normal file
@@ -0,0 +1,106 @@
|
||||
name: signals-evidence-locker
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
out_dir:
|
||||
description: "Output directory containing signed artifacts"
|
||||
required: false
|
||||
default: "evidence-locker/signals/2025-12-05"
|
||||
allow_dev_key:
|
||||
description: "Allow dev key fallback (1=yes, 0=no)"
|
||||
required: false
|
||||
default: "0"
|
||||
retention_target:
|
||||
description: "Retention days target"
|
||||
required: false
|
||||
default: "180"
|
||||
|
||||
jobs:
|
||||
prepare-signals-evidence:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MODULE_ROOT: docs/modules/signals
|
||||
OUT_DIR: ${{ github.event.inputs.out_dir || 'evidence-locker/signals/2025-12-05' }}
|
||||
COSIGN_ALLOW_DEV_KEY: ${{ github.event.inputs.allow_dev_key || '0' }}
|
||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
EVIDENCE_LOCKER_URL: ${{ secrets.EVIDENCE_LOCKER_URL || vars.EVIDENCE_LOCKER_URL }}
|
||||
CI_EVIDENCE_LOCKER_TOKEN: ${{ secrets.CI_EVIDENCE_LOCKER_TOKEN || vars.CI_EVIDENCE_LOCKER_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.2.4'
|
||||
|
||||
- name: Check signing key configured
|
||||
run: |
|
||||
if [[ -z "$COSIGN_PRIVATE_KEY_B64" && "$COSIGN_ALLOW_DEV_KEY" != "1" ]]; then
|
||||
echo "::error::COSIGN_PRIVATE_KEY_B64 is missing and dev key fallback is disabled. Set COSIGN_PRIVATE_KEY_B64 (and COSIGN_PASSWORD if needed) or rerun with allow_dev_key=1 for smoke only."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$COSIGN_ALLOW_DEV_KEY" == "1" ]]; then
|
||||
echo "::notice::Using dev key for signing (allow_dev_key=1) - not suitable for production uploads."
|
||||
fi
|
||||
|
||||
- name: Verify artifacts exist
|
||||
run: |
|
||||
cd "$MODULE_ROOT"
|
||||
sha256sum -c SHA256SUMS
|
||||
|
||||
- name: Sign signals artifacts
|
||||
run: |
|
||||
chmod +x tools/cosign/sign-signals.sh
|
||||
OUT_DIR="${OUT_DIR}" tools/cosign/sign-signals.sh
|
||||
|
||||
- name: Build deterministic signals evidence tar
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test -d "$MODULE_ROOT" || { echo "missing $MODULE_ROOT" >&2; exit 1; }
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
rsync -a --relative \
|
||||
"$OUT_DIR/SHA256SUMS" \
|
||||
"$OUT_DIR/confidence_decay_config.sigstore.json" \
|
||||
"$OUT_DIR/unknowns_scoring_manifest.sigstore.json" \
|
||||
"$OUT_DIR/heuristics_catalog.sigstore.json" \
|
||||
"$MODULE_ROOT/decay/confidence_decay_config.yaml" \
|
||||
"$MODULE_ROOT/unknowns/unknowns_scoring_manifest.json" \
|
||||
"$MODULE_ROOT/heuristics/heuristics.catalog.json" \
|
||||
"$tmpdir/"
|
||||
|
||||
(cd "$tmpdir/$OUT_DIR" && sha256sum --check SHA256SUMS)
|
||||
|
||||
tar --sort=name --mtime="UTC 1970-01-01" --owner=0 --group=0 --numeric-owner \
|
||||
-cf /tmp/signals-evidence.tar -C "$tmpdir" .
|
||||
|
||||
sha256sum /tmp/signals-evidence.tar > /tmp/signals-evidence.tar.sha256
|
||||
|
||||
- name: Upload artifact (fallback)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signals-evidence-2025-12-05
|
||||
path: |
|
||||
/tmp/signals-evidence.tar
|
||||
/tmp/signals-evidence.tar.sha256
|
||||
|
||||
- name: Push to Evidence Locker
|
||||
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN != '' && env.EVIDENCE_LOCKER_URL != '' }}
|
||||
env:
|
||||
TOKEN: ${{ env.CI_EVIDENCE_LOCKER_TOKEN }}
|
||||
URL: ${{ env.EVIDENCE_LOCKER_URL }}
|
||||
run: |
|
||||
upload_path="${OUT_DIR#evidence-locker/}"
|
||||
curl -f -X PUT "$URL/${upload_path}/signals-evidence.tar" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
--data-binary @/tmp/signals-evidence.tar
|
||||
|
||||
- name: Skip push (missing secret or URL)
|
||||
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN == '' || env.EVIDENCE_LOCKER_URL == '' }}
|
||||
run: |
|
||||
echo "Locker push skipped: set CI_EVIDENCE_LOCKER_TOKEN and EVIDENCE_LOCKER_URL to enable." >&2
|
||||
127
.gitea/workflows/signals-reachability.yml
Normal file
127
.gitea/workflows/signals-reachability.yml
Normal file
@@ -0,0 +1,127 @@
|
||||
name: Signals Reachability Scoring & Events
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
allow_dev_key:
|
||||
description: "Allow dev signing key fallback (1=yes, 0=no)"
|
||||
required: false
|
||||
default: "0"
|
||||
evidence_out_dir:
|
||||
description: "Evidence output dir for signing/upload"
|
||||
required: false
|
||||
default: "evidence-locker/signals/2025-12-05"
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'src/Signals/**'
|
||||
- 'scripts/signals/reachability-smoke.sh'
|
||||
- '.gitea/workflows/signals-reachability.yml'
|
||||
- 'tools/cosign/sign-signals.sh'
|
||||
|
||||
jobs:
|
||||
reachability-smoke:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
DOTNET_NOLOGO: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: 1
|
||||
TZ: UTC
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup .NET 10 RC
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.100
|
||||
include-prerelease: true
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore src/Signals/StellaOps.Signals.sln --configfile nuget.config
|
||||
|
||||
- name: Build
|
||||
run: dotnet build src/Signals/StellaOps.Signals.sln -c Release --no-restore
|
||||
|
||||
- name: Reachability scoring + cache/events smoke
|
||||
run: |
|
||||
chmod +x scripts/signals/reachability-smoke.sh
|
||||
scripts/signals/reachability-smoke.sh
|
||||
|
||||
sign-and-upload:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: reachability-smoke
|
||||
env:
|
||||
COSIGN_PRIVATE_KEY_B64: ${{ secrets.COSIGN_PRIVATE_KEY_B64 }}
|
||||
COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
|
||||
COSIGN_ALLOW_DEV_KEY: ${{ github.event.inputs.allow_dev_key || '0' }}
|
||||
OUT_DIR: ${{ github.event.inputs.evidence_out_dir || 'evidence-locker/signals/2025-12-05' }}
|
||||
CI_EVIDENCE_LOCKER_TOKEN: ${{ secrets.CI_EVIDENCE_LOCKER_TOKEN || vars.CI_EVIDENCE_LOCKER_TOKEN }}
|
||||
EVIDENCE_LOCKER_URL: ${{ secrets.EVIDENCE_LOCKER_URL || vars.EVIDENCE_LOCKER_URL }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@v3
|
||||
with:
|
||||
cosign-release: 'v2.2.4'
|
||||
|
||||
- name: Check signing key configured
|
||||
run: |
|
||||
if [[ -z "$COSIGN_PRIVATE_KEY_B64" && "$COSIGN_ALLOW_DEV_KEY" != "1" ]]; then
|
||||
echo "::error::COSIGN_PRIVATE_KEY_B64 is missing and dev key fallback is disabled. Set COSIGN_PRIVATE_KEY_B64 (and COSIGN_PASSWORD if needed) or rerun with allow_dev_key=1 for smoke only."
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$COSIGN_ALLOW_DEV_KEY" == "1" ]]; then
|
||||
echo "::notice::Using dev key for signing (allow_dev_key=1) - not suitable for production uploads."
|
||||
fi
|
||||
|
||||
- name: Verify artifacts exist
|
||||
run: |
|
||||
cd docs/modules/signals
|
||||
sha256sum -c SHA256SUMS
|
||||
|
||||
- name: Sign signals artifacts
|
||||
run: |
|
||||
chmod +x tools/cosign/sign-signals.sh
|
||||
OUT_DIR="${OUT_DIR}" tools/cosign/sign-signals.sh
|
||||
|
||||
- name: Upload signed artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signals-evidence-${{ github.run_number }}
|
||||
path: |
|
||||
${{ env.OUT_DIR }}/*.sigstore.json
|
||||
${{ env.OUT_DIR }}/*.dsse
|
||||
${{ env.OUT_DIR }}/SHA256SUMS
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
|
||||
- name: Push to Evidence Locker
|
||||
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN != '' && env.EVIDENCE_LOCKER_URL != '' }}
|
||||
env:
|
||||
TOKEN: ${{ env.CI_EVIDENCE_LOCKER_TOKEN }}
|
||||
URL: ${{ env.EVIDENCE_LOCKER_URL }}
|
||||
run: |
|
||||
tar -cf /tmp/signals-evidence.tar -C "$OUT_DIR" .
|
||||
sha256sum /tmp/signals-evidence.tar
|
||||
curl -f -X PUT "$URL/signals/$(date -u +%Y-%m-%d)/signals-evidence.tar" \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
--data-binary @/tmp/signals-evidence.tar
|
||||
echo "Uploaded to Evidence Locker"
|
||||
|
||||
- name: Evidence Locker skip notice
|
||||
if: ${{ env.CI_EVIDENCE_LOCKER_TOKEN == '' || env.EVIDENCE_LOCKER_URL == '' }}
|
||||
run: |
|
||||
echo "::notice::Evidence Locker upload skipped (CI_EVIDENCE_LOCKER_TOKEN or EVIDENCE_LOCKER_URL not set)"
|
||||
33
.gitea/workflows/sm-remote-ci.yml
Normal file
33
.gitea/workflows/sm-remote-ci.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
name: sm-remote-ci
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "src/SmRemote/**"
|
||||
- "src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/**"
|
||||
- "src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/**"
|
||||
- "ops/sm-remote/**"
|
||||
- ".gitea/workflows/sm-remote-ci.yml"
|
||||
pull_request:
|
||||
paths:
|
||||
- "src/SmRemote/**"
|
||||
- "src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote/**"
|
||||
- "src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/**"
|
||||
- "ops/sm-remote/**"
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
- name: Restore
|
||||
run: dotnet restore src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj
|
||||
- name: Test
|
||||
run: dotnet test src/__Libraries/StellaOps.Cryptography.Plugin.SmRemote.Tests/StellaOps.Cryptography.Plugin.SmRemote.Tests.csproj --no-build --verbosity normal
|
||||
- name: Publish service
|
||||
run: dotnet publish src/SmRemote/StellaOps.SmRemote.Service/StellaOps.SmRemote.Service.csproj -c Release -o out/sm-remote
|
||||
47
.gitea/workflows/symbols-ci.yml
Normal file
47
.gitea/workflows/symbols-ci.yml
Normal file
@@ -0,0 +1,47 @@
|
||||
name: Symbols Server CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'ops/devops/symbols/**'
|
||||
- 'scripts/symbols/**'
|
||||
- '.gitea/workflows/symbols-ci.yml'
|
||||
pull_request:
|
||||
branches: [ main, develop ]
|
||||
paths:
|
||||
- 'ops/devops/symbols/**'
|
||||
- 'scripts/symbols/**'
|
||||
- '.gitea/workflows/symbols-ci.yml'
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
symbols-smoke:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/artifacts/symbols-ci
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Run Symbols.Server smoke
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "$ARTIFACT_DIR"
|
||||
PROJECT_NAME=symbolsci ARTIFACT_DIR="$ARTIFACT_DIR" scripts/symbols/smoke.sh
|
||||
|
||||
- name: Upload artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: symbols-ci
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
retention-days: 7
|
||||
41
.gitea/workflows/symbols-release.yml
Normal file
41
.gitea/workflows/symbols-release.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Symbols Release Smoke
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
symbols-release-smoke:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
ARTIFACT_DIR: ${{ github.workspace }}/artifacts/symbols-release
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Export OpenSSL 1.1 shim for Mongo2Go
|
||||
run: scripts/enable-openssl11-shim.sh
|
||||
|
||||
- name: Run Symbols.Server smoke
|
||||
env:
|
||||
PROJECT_NAME: symbolsrelease
|
||||
ARTIFACT_DIR: ${{ env.ARTIFACT_DIR }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p "$ARTIFACT_DIR"
|
||||
PROJECT_NAME="${PROJECT_NAME:-symbolsrelease}" ARTIFACT_DIR="$ARTIFACT_DIR" scripts/symbols/smoke.sh
|
||||
|
||||
- name: Upload artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: symbols-release
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
retention-days: 14
|
||||
40
.gitea/workflows/vex-proof-bundles.yml
Normal file
40
.gitea/workflows/vex-proof-bundles.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
name: VEX Proof Bundles
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'scripts/vex/**'
|
||||
- 'tests/Vex/ProofBundles/**'
|
||||
- 'docs/benchmarks/vex-evidence-playbook*'
|
||||
- '.gitea/workflows/vex-proof-bundles.yml'
|
||||
push:
|
||||
branches: [ main ]
|
||||
paths:
|
||||
- 'scripts/vex/**'
|
||||
- 'tests/Vex/ProofBundles/**'
|
||||
- 'docs/benchmarks/vex-evidence-playbook*'
|
||||
- '.gitea/workflows/vex-proof-bundles.yml'
|
||||
|
||||
jobs:
|
||||
verify-bundles:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Task Pack offline bundle fixtures
|
||||
run: python3 scripts/packs/run-fixtures-check.sh
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install deps
|
||||
run: pip install --disable-pip-version-check --no-cache-dir -r scripts/vex/requirements.txt
|
||||
|
||||
- name: Verify proof bundles (offline)
|
||||
env:
|
||||
PYTHONHASHSEED: "0"
|
||||
run: |
|
||||
chmod +x tests/Vex/ProofBundles/test_verify_sample.sh
|
||||
tests/Vex/ProofBundles/test_verify_sample.sh
|
||||
36
.gitignore
vendored
36
.gitignore
vendored
@@ -17,6 +17,8 @@ obj/
|
||||
# Packages and logs
|
||||
*.log
|
||||
TestResults/
|
||||
local-nuget/
|
||||
local-nugets/packages/
|
||||
|
||||
.dotnet
|
||||
.DS_Store
|
||||
@@ -32,4 +34,36 @@ out/offline-kit/web/**/*
|
||||
**/.cache/**/*
|
||||
**/dist/**/*
|
||||
tmp/**/*
|
||||
build/
|
||||
build/
|
||||
/out/cli/**
|
||||
/src/Sdk/StellaOps.Sdk.Release/out/**
|
||||
/src/Sdk/StellaOps.Sdk.Generator/out/**
|
||||
/out/scanner-analyzers/**
|
||||
|
||||
# Node / frontend
|
||||
node_modules/
|
||||
dist/
|
||||
.build/
|
||||
.cache/
|
||||
|
||||
# .NET
|
||||
bin/
|
||||
obj/
|
||||
|
||||
# IDEs
|
||||
.vscode/
|
||||
.idea/
|
||||
*.user
|
||||
*.suo
|
||||
|
||||
# Misc
|
||||
logs/
|
||||
tmp/
|
||||
coverage/
|
||||
.nuget/
|
||||
local-nugets/
|
||||
local-nuget/
|
||||
src/Sdk/StellaOps.Sdk.Generator/tools/jdk-21.0.1+12
|
||||
.nuget-cache/
|
||||
.nuget-packages2/
|
||||
.nuget-temp/
|
||||
233
.spectral.yaml
Normal file
233
.spectral.yaml
Normal file
@@ -0,0 +1,233 @@
|
||||
extends:
|
||||
- "spectral:oas"
|
||||
|
||||
formats:
|
||||
- "oas3"
|
||||
|
||||
rules:
|
||||
stella-info-title:
|
||||
description: "OpenAPI info.title must be present"
|
||||
message: "Add a descriptive `info.title`"
|
||||
given: "$.info.title"
|
||||
severity: error
|
||||
then:
|
||||
function: truthy
|
||||
|
||||
stella-info-version:
|
||||
description: "OpenAPI info.version must be present"
|
||||
message: "Set `info.version` (SemVer or release tag)"
|
||||
given: "$.info.version"
|
||||
severity: error
|
||||
then:
|
||||
function: truthy
|
||||
|
||||
stella-servers-https:
|
||||
description: "Servers should use https"
|
||||
given: "$.servers[*].url"
|
||||
severity: warn
|
||||
then:
|
||||
function: pattern
|
||||
functionOptions:
|
||||
match: "^https://"
|
||||
|
||||
operation-operationId-required:
|
||||
description: "Every operation must have an operationId"
|
||||
message: "Add an `operationId` for this operation"
|
||||
given: "$.paths[*][*]"
|
||||
severity: error
|
||||
then:
|
||||
field: operationId
|
||||
function: truthy
|
||||
|
||||
stella-2xx-response-examples:
|
||||
description: "Every 2xx response must include at least one example"
|
||||
message: "Add an example or examples block to 2xx responses"
|
||||
given: "$.paths[*][*].responses[?(@property.match(/^2\\d\\d$/))].content.*"
|
||||
severity: error
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
anyOf:
|
||||
- required: [examples]
|
||||
- required: [example]
|
||||
|
||||
stella-pagination-params:
|
||||
description: "Collection GETs (list/search) must expose limit/cursor parameters"
|
||||
message: "Add limit/cursor parameters for paged collection endpoints"
|
||||
given: "$.paths[*][get]"
|
||||
severity: warn
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
operationId:
|
||||
type: string
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
operationId:
|
||||
pattern: "([Ll]ist|[Ss]earch|[Qq]uery)"
|
||||
then:
|
||||
required: [parameters]
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
allOf:
|
||||
- contains:
|
||||
anyOf:
|
||||
- required: ['$ref']
|
||||
properties:
|
||||
$ref:
|
||||
pattern: 'parameters/LimitParam$'
|
||||
- required: [name, in]
|
||||
properties:
|
||||
name:
|
||||
const: limit
|
||||
in:
|
||||
const: query
|
||||
- contains:
|
||||
anyOf:
|
||||
- required: ['$ref']
|
||||
properties:
|
||||
$ref:
|
||||
pattern: 'parameters/CursorParam$'
|
||||
- required: [name, in]
|
||||
properties:
|
||||
name:
|
||||
const: cursor
|
||||
in:
|
||||
const: query
|
||||
|
||||
stella-idempotency-header:
|
||||
description: "State-changing operations returning 201/202 should accept Idempotency-Key headers"
|
||||
message: "Add Idempotency-Key header parameter for idempotent submissions"
|
||||
given: "$.paths[*][?(@property.match(/^(post|put|patch)$/))]"
|
||||
severity: warn
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
responses:
|
||||
type: object
|
||||
parameters:
|
||||
type: array
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
responses:
|
||||
type: object
|
||||
anyOf:
|
||||
- required: ['201']
|
||||
- required: ['202']
|
||||
then:
|
||||
required: [parameters]
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
contains:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
const: Idempotency-Key
|
||||
in:
|
||||
const: header
|
||||
required: [name, in]
|
||||
|
||||
stella-operationId-style:
|
||||
description: "operationId must be lowerCamelCase"
|
||||
given: "$.paths[*][*].operationId"
|
||||
severity: warn
|
||||
then:
|
||||
function: casing
|
||||
functionOptions:
|
||||
type: camel
|
||||
|
||||
|
||||
stella-jobs-idempotency-key:
|
||||
description: "Orchestrator job submissions must accept Idempotency-Key header"
|
||||
given: "$.paths['/jobs'].post.parameters"
|
||||
severity: warn
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: array
|
||||
contains:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
const: Idempotency-Key
|
||||
in:
|
||||
const: header
|
||||
required: [name, in]
|
||||
|
||||
# --- Deprecation Metadata Rules (per APIGOV-63-001) ---
|
||||
|
||||
stella-deprecated-has-metadata:
|
||||
description: "Deprecated operations must have x-deprecation extension with required fields"
|
||||
message: "Add x-deprecation metadata (deprecatedAt, sunsetAt, successorPath, reason) to deprecated operations"
|
||||
given: "$.paths[*][*][?(@.deprecated == true)]"
|
||||
severity: error
|
||||
then:
|
||||
field: x-deprecation
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- deprecatedAt
|
||||
- sunsetAt
|
||||
- successorPath
|
||||
- reason
|
||||
properties:
|
||||
deprecatedAt:
|
||||
type: string
|
||||
format: date-time
|
||||
sunsetAt:
|
||||
type: string
|
||||
format: date-time
|
||||
successorPath:
|
||||
type: string
|
||||
successorOperationId:
|
||||
type: string
|
||||
reason:
|
||||
type: string
|
||||
migrationGuide:
|
||||
type: string
|
||||
format: uri
|
||||
notificationChannels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum: [slack, teams, email, webhook]
|
||||
|
||||
stella-deprecated-sunset-future:
|
||||
description: "Sunset dates should be in the future (warn if sunset already passed)"
|
||||
message: "x-deprecation.sunsetAt should be a future date"
|
||||
given: "$.paths[*][*].x-deprecation.sunsetAt"
|
||||
severity: warn
|
||||
then:
|
||||
function: truthy
|
||||
|
||||
stella-deprecated-migration-guide:
|
||||
description: "Deprecated operations should include a migration guide URL"
|
||||
message: "Consider adding x-deprecation.migrationGuide for consumer guidance"
|
||||
given: "$.paths[*][*][?(@.deprecated == true)].x-deprecation"
|
||||
severity: hint
|
||||
then:
|
||||
field: migrationGuide
|
||||
function: truthy
|
||||
|
||||
stella-deprecated-notification-channels:
|
||||
description: "Deprecated operations should specify notification channels"
|
||||
message: "Add x-deprecation.notificationChannels to enable deprecation notifications"
|
||||
given: "$.paths[*][*][?(@.deprecated == true)].x-deprecation"
|
||||
severity: hint
|
||||
then:
|
||||
field: notificationChannels
|
||||
function: truthy
|
||||
5
.venv/pyvenv.cfg
Normal file
5
.venv/pyvenv.cfg
Normal file
@@ -0,0 +1,5 @@
|
||||
home = /usr/bin
|
||||
include-system-site-packages = false
|
||||
version = 3.12.3
|
||||
executable = /usr/bin/python3.12
|
||||
command = /usr/bin/python -m venv /mnt/e/dev/git.stella-ops.org/.venv
|
||||
625
AGENTS.md
625
AGENTS.md
@@ -1,226 +1,399 @@
|
||||
# 1) What is StellaOps?
|
||||
|
||||
**StellaOps** an next-gen and sovereign container-security toolkit built for high-speed, offline operation, released under AGPL-3.0-or-later.
|
||||
|
||||
Stella Ops is a self-hostable, sovereign container-security platform that makes proof—not promises—default. It binds every container digest to content-addressed SBOMs (SBOM 3.0.0 and CycloneDX 1.6), in-toto/DSSE attestations, and optional Sigstore Rekor transparency, then layers deterministic, replayable scanning with entry-trace and VEX-first decisioning. “Next-gen” means findings are reproducible and explainable, exploitability is modeled in OpenVEX and merged with lattice logic for stable outcomes, and the same workflow runs online or fully air-gapped. “Sovereign” means cryptographic and operational independence: bring-your-own trust roots, regional crypto readiness (eIDAS/FIPS/GOST/SM), offline bundles, and post-quantum-ready modes—so regulated orgs can comply without phoning home.
|
||||
|
||||
Our principles and goals are simple: authenticity & integrity by default, provenance attached to digests, transparency for tamper-evidence, determinism & replay for audits, explainability engineers can act on, and exploitability over enumeration to cut noise. We minimize trust and blast radius with short-lived keys, least-privilege, and content-addressed caches; we stay air-gap friendly with mirrored feeds; and we keep governance honest with reviewable OPA/Rego policy gates and VEX-based waivers. The result is a platform that shortens time-to-truth, makes risk measurable, and lets you ship with confidence—anywhere, under any sovereignty requirement.
|
||||
More documention is available ./docs/*.md files. Read `docs/README.md` to gather information about the available documentation. You could inquiry specific documents as your work requires it
|
||||
|
||||
---
|
||||
|
||||
# 3) Practices
|
||||
|
||||
## 3.1) Naming
|
||||
All modules are .NET projects based on .NET 10 (preview). Exclussion is the UI. It is based on Angular
|
||||
All modules are contained by one or more projects. Each project goes in its dedicated folder. Each project starts with StellaOps.<ModuleName>. In case it is common for for all StellaOps modules it is library or plugin and it is named StellaOps.<LibraryOrPlugin>.
|
||||
|
||||
## 3.2) Key technologies & integrations
|
||||
|
||||
- **Runtime**: .NET 10 (`net10.0`) preview SDK; C# latest preview features. Any dependencies like Microsoft.* should strive to be closests version.
|
||||
- **Nuget**: Try to re-use / cache nugets to /local-nugets
|
||||
- **Data**: MongoDB (canonical store and job/export state). MongoDB driver version should be > 3.0
|
||||
- **Observability**: structured logs, counters, and (optional) OpenTelemetry traces.
|
||||
- **Ops posture**: offline‑first, allowlist for remote hosts, strict schema validation, gated LLM fallback (only where explicitly configured).
|
||||
|
||||
## 3.3) Task workflow & guild coordination
|
||||
- **Always sync state before coding.** When you pick up a task, immediately flip its status from `TODO` (or current state) to `DOING` in **both** `docs/implplan/SPRINT_*.md` and the module’s local `TASKS.md`. Tasks must return to `TODO` if you step away, or `DONE` when you ship.
|
||||
- **Read the local agent charter first.** Every task directory must contain an `AGENTS.md` describing roles, expectations, and required prep docs. Review it (and the referenced module documentation) before touching code.
|
||||
- **Mirror state across artefacts.** Any status update in `TASKS.md` requires the same change in `SPRINT_*.md`, plus context noted in commit/PR descriptions.
|
||||
- **Document prerequisites.** If an `AGENTS.md` points to onboarding docs, verify you have read them before setting `DOING`. When new docs are required, update the agent charter alongside the task change.
|
||||
- **Coordination**. Coordination is done only via leaving tasks remarks or in case it is documentation or bigger remark, it will be task remark with link to appropriate document on /docs/**/*.md
|
||||
|
||||
# 4) Modules
|
||||
StellaOps ships as containerised building blocks; each module owns a clear boundary and has its own code folder, deployable image, and deep-dive architecture dossier.
|
||||
|
||||
| Module | Primary path(s) | Key doc |
|
||||
|--------|-----------------|---------|
|
||||
| Authority | `src/Authority/StellaOps.Authority`<br>`src/Authority/StellaOps.Authority.Plugin.*` | `docs/modules/authority/architecture.md` |
|
||||
| Signer | `src/Signer/StellaOps.Signer` | `docs/modules/signer/architecture.md` |
|
||||
| Attestor | `src/Attestor/StellaOps.Attestor`<br>`src/Attestor/StellaOps.Attestor.Verify` | `docs/modules/attestor/architecture.md` |
|
||||
| Concelier | `src/Concelier/StellaOps.Concelier.WebService`<br>`src/Concelier/__Libraries/StellaOps.Concelier.*` | `docs/modules/concelier/architecture.md` |
|
||||
| Excititor | `src/Excititor/StellaOps.Excititor.WebService`<br>`src/Excititor/__Libraries/StellaOps.Excititor.*` | `docs/modules/excititor/architecture.md` |
|
||||
| Policy Engine | `src/Policy/StellaOps.Policy.Engine`<br>`src/Policy/__Libraries/StellaOps.Policy.*` | `docs/modules/policy/architecture.md` |
|
||||
| Scanner | `src/Scanner/StellaOps.Scanner.WebService`<br>`src/Scanner/StellaOps.Scanner.Worker`<br>`src/Scanner/__Libraries/StellaOps.Scanner.*` | `docs/modules/scanner/architecture.md` |
|
||||
| Scheduler | `src/Scheduler/StellaOps.Scheduler.WebService`<br>`src/Scheduler/StellaOps.Scheduler.Worker` | `docs/modules/scheduler/architecture.md` |
|
||||
| CLI | `src/Cli/StellaOps.Cli`<br>`src/Cli/StellaOps.Cli.Core`<br>`src/Cli/StellaOps.Cli.Plugins.*` | `docs/modules/cli/architecture.md` |
|
||||
| UI / Console | `src/UI/StellaOps.UI` | `docs/modules/ui/architecture.md` |
|
||||
| Notify | `src/Notify/StellaOps.Notify.WebService`<br>`src/Notify/StellaOps.Notify.Worker` | `docs/modules/notify/architecture.md` |
|
||||
| Export Center | `src/ExportCenter/StellaOps.ExportCenter.WebService`<br>`src/ExportCenter/StellaOps.ExportCenter.Worker` | `docs/modules/export-center/architecture.md` |
|
||||
| Registry Token Service | `src/Registry/StellaOps.Registry.TokenService`<br>`src/Registry/__Tests/StellaOps.Registry.TokenService.Tests` | `docs/modules/registry/architecture.md` |
|
||||
| Advisory AI | `src/AdvisoryAI/StellaOps.AdvisoryAI` | `docs/modules/advisory-ai/architecture.md` |
|
||||
| Orchestrator | `src/Orchestrator/StellaOps.Orchestrator` | `docs/modules/orchestrator/architecture.md` |
|
||||
| Vulnerability Explorer | `src/VulnExplorer/StellaOps.VulnExplorer.Api` | `docs/modules/vuln-explorer/architecture.md` |
|
||||
| VEX Lens | `src/VexLens/StellaOps.VexLens` | `docs/modules/vex-lens/architecture.md` |
|
||||
| Graph Explorer | `src/Graph/StellaOps.Graph.Api`<br>`src/Graph/StellaOps.Graph.Indexer` | `docs/modules/graph/architecture.md` |
|
||||
| Telemetry Stack | `ops/devops/telemetry` | `docs/modules/telemetry/architecture.md` |
|
||||
| DevOps / Release | `ops/devops` | `docs/modules/devops/architecture.md` |
|
||||
| Platform | *(cross-cutting docs)* | `docs/modules/platform/architecture-overview.md` |
|
||||
| CI Recipes | *(pipeline templates)* | `docs/modules/ci/architecture.md` |
|
||||
| Zastava | `src/Zastava/StellaOps.Zastava.Observer`<br>`src/Zastava/StellaOps.Zastava.Webhook`<br>`src/Zastava/StellaOps.Zastava.Core` | `docs/modules/zastava/architecture.md` |
|
||||
|
||||
## 4.1 Module cheat sheet
|
||||
|
||||
### Authority
|
||||
- **Path:** `src/Authority/StellaOps.Authority`, plugins in `src/Authority/StellaOps.Authority.Plugin.*`.
|
||||
- **Docs:** `docs/modules/authority/architecture.md`.
|
||||
- **Responsibilities:** Issues short-lived, sender-constrained OpToks (DPoP/mTLS) for services, CLI, and UI; exposes OIDC discovery, device-code, and auth-code flows.
|
||||
- **Key traits:** Ed25519/ES256 signing with JWKS rotation, tenant-aware scopes, stateless JWT validation, optional introspection, and structured audit trails.
|
||||
|
||||
### Signer
|
||||
- **Path:** `src/Signer/StellaOps.Signer`.
|
||||
- **Docs:** `docs/modules/signer/architecture.md`.
|
||||
- **Responsibilities:** Authenticates callers, enforces Proof-of-Entitlement, verifies scanner release signatures, and returns DSSE bundles for SBOMs and reports.
|
||||
- **Key traits:** Supports keyless (Fulcio) and keyful (KMS/HSM) signing, applies plan quotas, stores audit trails, and delegates Rekor logging to the Attestor.
|
||||
|
||||
### Attestor
|
||||
- **Path:** `src/Attestor/StellaOps.Attestor`, proof helpers in `src/Attestor/StellaOps.Attestor.Verify`.
|
||||
- **Docs:** `docs/modules/attestor/architecture.md`.
|
||||
- **Responsibilities:** Submits DSSE bundles to Rekor v2, caches `{uuid, index, proof}`, and serves verification bundles to Scanner, UI, CLI, and Export Center.
|
||||
- **Key traits:** mTLS + OpTok enforcement for Signer-only submissions, Mongo/Redis idempotency, optional DSSE archive mirroring, and resilient retry/backoff.
|
||||
|
||||
### Concelier
|
||||
- **Path:** `src/Concelier/StellaOps.Concelier.WebService` with connectors/exporters under `src/Concelier/__Libraries/StellaOps.Concelier.*`.
|
||||
- **Docs:** `docs/modules/concelier/architecture.md`.
|
||||
- **Responsibilities:** Applies the Aggregation-Only Contract to ingest advisories, produce immutable observations, correlate linksets, and publish deterministic exports.
|
||||
- **Key traits:** Restart-time connectors/exporters, Mongo-backed scheduling, canonical JSON/Trivy outputs, Offline Kit parity, and hash-stable manifests.
|
||||
|
||||
### Excititor
|
||||
- **Path:** `src/Excititor/StellaOps.Excititor.WebService`, connectors/adapters in `src/Excititor/__Libraries/StellaOps.Excititor.*`.
|
||||
- **Docs:** `docs/modules/excititor/architecture.md`.
|
||||
- **Responsibilities:** Normalises VEX statements into observations, builds provenance-rich linksets, and surfaces consensus/conflicts for policy suppression.
|
||||
- **Key traits:** Aggregation-only guardrails, restart-time plug-ins, Mongo persistence, deterministic exports, and Offline Kit-ready bundles.
|
||||
|
||||
### Policy Engine
|
||||
- **Path:** `src/Policy/StellaOps.Policy.Engine`, shared libraries under `src/Policy/__Libraries/StellaOps.Policy.*`.
|
||||
- **Docs:** `docs/modules/policy/architecture.md`.
|
||||
- **Responsibilities:** Evaluates `stella-dsl@1` policies, joins SBOM/advisory/VEX evidence, materialises effective findings, and emits explain traces.
|
||||
- **Key traits:** Deterministic evaluation (no wall clock), change-stream driven increments, simulation endpoints, and Authority-scoped tenancy/RBAC enforcement.
|
||||
|
||||
### Scanner.WebService
|
||||
- **Path:** `src/Scanner/StellaOps.Scanner.WebService`.
|
||||
- **Docs:** `docs/modules/scanner/architecture.md`.
|
||||
- **Responsibilities:** Hosts scan/diff/export APIs, enqueues work, serves SBOM and diff artifacts, and publishes DSSE-ready report metadata.
|
||||
- **Key traits:** Minimal APIs with Redis/NATS queue clients, RustFS artifact integration, BOM-index lookups, and DSSE hand-off to Signer/Attestor.
|
||||
|
||||
### Scanner.Worker
|
||||
- **Path:** `src/Scanner/StellaOps.Scanner.Worker` with analyzers/caches in `src/Scanner/__Libraries/StellaOps.Scanner.*`.
|
||||
- **Docs:** `docs/modules/scanner/architecture.md`.
|
||||
- **Responsibilities:** Runs deterministic OS/language/native analyzers per layer, composes inventory and usage SBOM fragments, and streams them back to the catalog.
|
||||
- **Key traits:** Layer/file CAS caching, restart-time analyzer plug-ins under `plugins/scanner/**`, bounded retries with lease renewals, and DSSE-ready outputs.
|
||||
|
||||
### Scheduler
|
||||
- **Path:** `src/Scheduler/StellaOps.Scheduler.WebService`, `src/Scheduler/StellaOps.Scheduler.Worker`.
|
||||
- **Docs:** `docs/modules/scheduler/architecture.md`.
|
||||
- **Responsibilities:** Detects advisory/VEX deltas, selects impacted assets via BOM index, and schedules analysis-only runs toward Scanner and Policy Engine.
|
||||
- **Key traits:** Mongo impact cursors, Redis/NATS orchestration, webhook fan-out (Policy/Notify/Runtime), and deterministic evaluation windows.
|
||||
|
||||
### CLI
|
||||
- **Path:** `src/Cli/StellaOps.Cli`, helpers in `src/Cli/StellaOps.Cli.Core`, plug-ins in `src/Cli/StellaOps.Cli.Plugins.*`.
|
||||
- **Docs:** `docs/modules/cli/architecture.md`.
|
||||
- **Responsibilities:** Provides deterministic verbs for scan/diff/export/report, Buildx SBOM orchestration, policy/VEX administration, and offline kit workflows.
|
||||
- **Key traits:** Native AOT binaries, device-code/client-credential login with DPoP storage, golden-output tests, and restart-time plug-in manifests in `plugins/cli/**`.
|
||||
|
||||
### UI
|
||||
- **Path:** `src/UI/StellaOps.UI`.
|
||||
- **Docs:** `docs/modules/ui/architecture.md`.
|
||||
- **Responsibilities:** Angular SPA for scans, policy authoring, VEX evidence exploration, runtime posture, and admin tooling via backend APIs.
|
||||
- **Key traits:** Angular Signals with `@ngrx/signals`, typed API clients handling DPoP + SSE, Tailwind theming, and immutable content-hashed bundles.
|
||||
|
||||
### Notify
|
||||
- **Path:** `src/Notify/StellaOps.Notify.WebService`, `src/Notify/StellaOps.Notify.Worker`, connectors in `src/Notify/__Libraries`.
|
||||
- **Docs:** `docs/modules/notify/architecture.md`.
|
||||
- **Responsibilities:** Evaluates notification rules on platform events, renders channel-specific payloads, and delivers messages with throttling/digests.
|
||||
- **Key traits:** Tenant-scoped rule engine, idempotent delivery queues, secrets referenced rather than stored, and comprehensive audit/metrics coverage.
|
||||
|
||||
### Export Center
|
||||
- **Path:** `src/ExportCenter/StellaOps.ExportCenter.WebService`, `src/ExportCenter/StellaOps.ExportCenter.Worker`, adapters in `src/ExportCenter/StellaOps.ExportCenter.*`.
|
||||
- **Docs:** `docs/modules/export-center/architecture.md`.
|
||||
- **Responsibilities:** Packages reproducible evidence bundles (JSON, Trivy, mirror) with provenance, signing, and distribution manifests for offline or mirror deployments.
|
||||
- **Key traits:** Profile-driven exports, Orchestrator-backed job leases, Mongo/object storage staging, and cosign-compatible provenance/signature emission.
|
||||
|
||||
### Registry Token Service
|
||||
- **Path:** `src/Registry/StellaOps.Registry.TokenService`, with integration tests in `src/Registry/__Tests/StellaOps.Registry.TokenService.Tests`.
|
||||
- **Docs:** `docs/modules/registry/operations/token-service.md`.
|
||||
- **Responsibilities:** Issues scoped pull tokens for container/image registries, enforces licence/plan constraints, and publishes audit telemetry for token usage.
|
||||
- **Key traits:** Authority-issued OpTok validation, Mongo-backed issuance ledger, deterministic checksum manifests for Offline Kit bundles, and emergency revoke/rotation tooling.
|
||||
|
||||
### Zastava
|
||||
- **Path:** `src/Zastava/StellaOps.Zastava.Observer`, `src/Zastava/StellaOps.Zastava.Webhook`, shared contracts in `src/Zastava/StellaOps.Zastava.Core`.
|
||||
- **Docs:** `docs/modules/zastava/architecture.md`.
|
||||
- **Responsibilities:** Observes running workloads, emits runtime posture events, and enforces admission-time policy (signed images, SBOM availability, policy verdict).
|
||||
- **Key traits:** Authority-issued OpToks with DPoP/mTLS, ND-JSON batching with local buffering, delta-scan triggers on drift, and Kubernetes webhook enforcement.
|
||||
|
||||
---
|
||||
|
||||
### 4.1.4) Glossary (quick)
|
||||
|
||||
- **OVAL** — Vendor/distro security definition format; authoritative for OS packages.
|
||||
- **NEVRA / EVR** — RPM and Debian version semantics for OS packages.
|
||||
- **PURL / SemVer** — Coordinates and version semantics for OSS ecosystems.
|
||||
- **KEV** — Known Exploited Vulnerabilities (flag only).
|
||||
|
||||
---
|
||||
# 5) Your role as StellaOps contributor
|
||||
|
||||
You acting as information technology engineer that will take different type of roles in goal achieving StellaOps production implementation
|
||||
In order you to work - you have to be supplied with directory that contains `AGENTS.md`,`TASKS.md` files. There will you have more information about the role you have, the scope of your work and the tasks you will have.
|
||||
|
||||
Boundaries:
|
||||
- You operate only in the working directories I gave you, unless there is dependencies that makes you to work on dependency in shared directory. Then you ask for confirmation.
|
||||
|
||||
You main characteristics:
|
||||
- Keep endpoints small, deterministic, and cancellation-aware.
|
||||
- Improve logs/metrics as per tasks.
|
||||
- Update `TASKS.md` when moving tasks forward.
|
||||
- When you are done with all task you state explicitly you are done.
|
||||
- Impersonate the role described on working directory `AGENTS.md` you will read, if role is not available - take role of the CTO of the StellaOps in early stages.
|
||||
- You always strive for best practices
|
||||
- You always strive for re-usability
|
||||
- When in doubt of design decision - you ask then act
|
||||
- You are autonomus - meaning that you will work for long time alone and achieve maximum without stopping for stupid questions
|
||||
- You operate on the same directory where other agents will work. In case you need to work on directory that is dependency on provided `AGENTS.md`,`TASKS.md` files you have to ask for confirmation first.
|
||||
|
||||
## 5.1) Type of contributions
|
||||
|
||||
- **BE‑Base (Platform & Pipeline)**
|
||||
Owns DI, plugin host, job scheduler/coordinator, configuration binding, minimal API endpoints, and Mongo bootstrapping.
|
||||
- **BE‑Conn‑X (Connectors)**
|
||||
One agent per source family (NVD, Red Hat, Ubuntu, Debian, SUSE, GHSA, OSV, PSIRTs, CERTs, KEV, ICS). Implements fetch/parse/map with incremental watermarks.
|
||||
- **BE‑Merge (Canonical Merge & Dedupe)**
|
||||
Identity graph, precedence policies, canonical JSON serializer, and deterministic hashing (`merge_event`).
|
||||
- **BE‑Export (JSON & Trivy DB)**
|
||||
Deterministic export trees, Trivy DB packaging, optional ORAS push, and offline bundle.
|
||||
- **QA (Validation & Observability)**
|
||||
Schema tests, fixture goldens, determinism checks, metrics/logs/traces, e2e reproducibility runs.
|
||||
- **DevEx/Docs**
|
||||
Maintains this agent framework, templates, and per‑directory guides; assists parallelization and reviews.
|
||||
|
||||
|
||||
## 5.2) Work rules (important)
|
||||
|
||||
- **Directory ownership**: Each agent works **only inside its module directory**. Cross‑module edits require a brief handshake in issues/PR description.
|
||||
- **Scoping**: Use each module’s `AGENTS.md` and `TASKS.md` to plan; autonomous agents must read `src/AGENTS.md` and the module docs before acting.
|
||||
- **Determinism**: Sort keys, normalize timestamps to UTC ISO‑8601, avoid non‑deterministic data in exports and tests.
|
||||
- **Status tracking**: Update your module’s `TASKS.md` as you progress (TODO → DOING → DONE/BLOCKED). Before starting of actual work - ensure you have set the task to DOING. When complete or stop update the status in corresponding TASKS.md and in ./SPRINT_*.md file.
|
||||
- **Coordination**: In case task is discovered as blocked on other team or task, according TASKS.md files that dependency is on needs to be changed by adding new tasks describing the requirement. the current task must be updated as completed. In case task changes, scope or requirements or rules - other documentations needs be updated accordingly.
|
||||
- **Sprint synchronization**: When given task seek for relevant directory to work on from correspoding sprint file SPRINT_*.md. Confirm its state on both correspoding sprint file SPRINT_*.md and the relevant TASKS.md file. Always check the AGENTS.md in the relevant TASKS.md directory.
|
||||
- **Tests**: Add/extend fixtures and unit tests per change; never regress determinism or precedence.
|
||||
- **Test layout**: Use module-specific projects in `StellaOps.Concelier.<Component>.Tests`; shared fixtures/harnesses live in `StellaOps.Concelier.Testing`.
|
||||
- **Execution autonomous**: In case you need to continue with more than one options just continue sequentially, unless the continue requires design decision.
|
||||
- **Additional references**: When a task mentions historical epics, consult the corresponding module guides or domain playbooks under `docs/modules/**`, `docs/api/`, `docs/risk/`, or `docs/airgap/` for the latest specification.
|
||||
|
||||
---
|
||||
|
||||
## Required Reading
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- Review the relevant module dossier (for example, `docs/modules/authority/architecture.md`) before editing component-specific content.
|
||||
|
||||
## Working Agreement
|
||||
- 1. Update task status to `DOING`/`DONE` in both corresponding `docs/implplan/SPRINT_*.md` and the local `TASKS.md` when you start or finish work.
|
||||
- 2. Review this charter and the Required Reading documents before coding; confirm prerequisites are met.
|
||||
- 3. Keep changes deterministic (stable ordering, timestamps, hashes) and align with offline/air-gap expectations.
|
||||
- 4. Coordinate doc updates, tests, and cross-guild communication whenever contracts or workflows change.
|
||||
- 5. Revert to `TODO` if you pause the task without shipping changes; leave notes in commit/PR descriptions for context.
|
||||
### 0) Identity — Who You Are
|
||||
|
||||
You are an autonomous software engineering agent for **StellaOps**. You can take different roles in the software development lifecycle and must switch behavior depending on the role requested.
|
||||
|
||||
You are capable of:
|
||||
|
||||
* Acting in different engineering roles: **document author**, **backend developer**, **frontend developer**, **tester/QA automation engineer**.
|
||||
* Acting in management roles: **product manager** and **technical project manager**, capable of:
|
||||
|
||||
* Understanding market / competitor trends.
|
||||
* Translating them into coherent development stories, epics, and sprints.
|
||||
* Operating with minimal supervision, respecting the process rules and directory boundaries defined below.
|
||||
|
||||
Unless explicitly told otherwise, assume you are working inside the StellaOps monorepo and following its documentation and sprint files.
|
||||
|
||||
---
|
||||
|
||||
### 1) What is StellaOps?
|
||||
|
||||
**StellaOps** is a next-generation, sovereign container-security toolkit built for high-speed, offline operation and released under AGPL-3.0-or-later.
|
||||
|
||||
StellaOps is a self-hostable, sovereign container-security platform that makes proof—not promises—default. It binds every container digest to content-addressed SBOMs (SPDX 3.0.1 and CycloneDX 1.6), in-toto/DSSE attestations, and optional Sigstore Rekor transparency, then layers deterministic, replayable scanning with entry-trace and VEX-first decisioning.
|
||||
|
||||
“Next-gen” means:
|
||||
|
||||
* Findings are reproducible and explainable.
|
||||
* Exploitability is modeled in OpenVEX and merged with lattice logic for stable outcomes.
|
||||
* The same workflow runs online or fully air-gapped.
|
||||
|
||||
“Sovereign” means cryptographic and operational independence:
|
||||
|
||||
* Bring-your-own trust roots.
|
||||
* Regional crypto readiness (eIDAS/FIPS/GOST/SM).
|
||||
* Offline bundles and post-quantum-ready modes.
|
||||
|
||||
Target users are regulated organizations that need authenticity & integrity by default, provenance attached to digests, transparency for tamper-evidence, determinism & replay for audits, explainability engineers can act on, and exploitability-over-enumeration to cut noise. We minimize trust and blast radius with short-lived keys, least-privilege, and content-addressed caches; we stay air-gap friendly with mirrored feeds; and we keep governance honest with reviewable OPA/Rego policy gates and VEX-based waivers.
|
||||
|
||||
More documentation is in `./docs/*.md`. Start with `docs/README.md` to discover available documentation. When needed, you may request specific documents to be provided (e.g., `docs/modules/scanner/architecture.md`).
|
||||
|
||||
---
|
||||
|
||||
#### 1.1) Required Reading
|
||||
|
||||
Before doing any non-trivial work, you must assume you have read and understood:
|
||||
|
||||
* `docs/README.md`
|
||||
* `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
* `docs/modules/platform/architecture-overview.md`
|
||||
* The relevant module dossier (for example `docs/modules/authority/architecture.md`) before editing module-specific content.
|
||||
|
||||
When you are told you are working in a particular module or directory, assume you have read that module’s `AGENTS.md` and architecture docs under `docs/modules/<module>/*.md`.
|
||||
|
||||
---
|
||||
|
||||
### 2) Core Practices
|
||||
|
||||
#### 2.1) Key technologies & integrations
|
||||
|
||||
* **Runtime**: .NET 10 (`net10.0`) with latest C# preview features. Microsoft.* dependencies should target the closest compatible versions.
|
||||
* **Frontend**: Angular v17 for the UI.
|
||||
* **NuGet**: Uses standard NuGet feeds configured in `nuget.config` (dotnet-public, nuget-mirror, nuget.org). Packages restore to the global NuGet cache.
|
||||
* **Data**: PostgreSQL as canonical store and for job/export state. Use a PostgreSQL driver version ≥ 3.0.
|
||||
* **Observability**: Structured logs, counters, and (optional) OpenTelemetry traces.
|
||||
* **Ops posture**: Offline-first, remote host allowlist, strict schema validation, and gated LLM usage (only where explicitly configured).
|
||||
|
||||
#### 2.2) Naming conventions
|
||||
|
||||
* All modules are .NET 10 projects, except the UI (Angular).
|
||||
* Each module lives in one or more projects. Each project is in its own folder.
|
||||
* Project naming:
|
||||
|
||||
* Module projects: `StellaOps.<ModuleName>`.
|
||||
* Libraries or plugins common to multiple modules: `StellaOps.<LibraryOrPlugin>`.
|
||||
|
||||
#### 2.3) Task workflow & guild coordination
|
||||
|
||||
* **Always sync state before coding.**
|
||||
When you pick up a task, update its status in the relevant `docs/implplan/SPRINT_*.md` entry: `TODO` → `DOING`.
|
||||
If you stop without shipping, move it back to `TODO`.
|
||||
When completed, set it to `DONE`.
|
||||
* **Read the local agent charter first.**
|
||||
Each working directory has an `AGENTS.md` describing roles, expectations, and required prep docs. Assume you have reviewed this (and referenced module docs) before touching code.
|
||||
* **Mirror state across artefacts.**
|
||||
Sprint files are the single source of truth. Status changes must be reflected in:
|
||||
|
||||
* The `SPRINT_*.md` table.
|
||||
* Commit/PR descriptions with brief context.
|
||||
* **Document prerequisites.**
|
||||
If onboarding docs are referenced in `AGENTS.md`, treat them as read before setting `DOING`. If new docs are needed, update the charter alongside your task updates.
|
||||
* **Coordination.**
|
||||
Coordination happens through:
|
||||
|
||||
* Task remarks in sprint files, and
|
||||
* Longer remarks in dedicated docs under `docs/**/*.md` linked from the sprint/task remarks.
|
||||
* **AGENTS.md ownership and usage.**
|
||||
* Project / technical managers are responsible for creating and curating a module-specific `AGENTS.md` in each working directory (for example `src/Scanner/AGENTS.md`, `src/Concelier/AGENTS.md`). This file must synthesise:
|
||||
* The roles expected in that module (e.g., backend engineer, UI engineer, QA).
|
||||
* Module-specific working agreements and constraints.
|
||||
* Required documentation and runbooks to read before coding.
|
||||
* Any module-specific testing or determinism rules.
|
||||
* Implementers are responsible for fully reading and following the local `AGENTS.md` before starting work in that directory and must treat it as the binding local contract for that module.
|
||||
---
|
||||
|
||||
### 3) Architecture Overview
|
||||
|
||||
StellaOps is a monorepo:
|
||||
|
||||
* Code in `src/**`.
|
||||
* Documents in `docs/**`.
|
||||
* CI/CD in Gitea workflows under `.gitea/**`.
|
||||
|
||||
It ships as containerised building blocks; each module owns a clear boundary and has:
|
||||
|
||||
* Its own code folder.
|
||||
* Its own deployable image.
|
||||
* A deep-dive architecture dossier in `docs/modules/<module>/architecture.md`.
|
||||
|
||||
| Module | Primary path(s) | Key doc |
|
||||
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
|
||||
| Authority | `src/Authority/StellaOps.Authority`<br>`src/Authority/StellaOps.Authority.Plugin.*` | `docs/modules/authority/architecture.md` |
|
||||
| Signer | `src/Signer/StellaOps.Signer` | `docs/modules/signer/architecture.md` |
|
||||
| Attestor | `src/Attestor/StellaOps.Attestor`<br>`src/Attestor/StellaOps.Attestor.Verify` | `docs/modules/attestor/architecture.md` |
|
||||
| Concelier | `src/Concelier/StellaOps.Concelier.WebService`<br>`src/Concelier/__Libraries/StellaOps.Concelier.*` | `docs/modules/concelier/architecture.md` |
|
||||
| Excititor | `src/Excititor/StellaOps.Excititor.WebService`<br>`src/Excititor/__Libraries/StellaOps.Excititor.*` | `docs/modules/excititor/architecture.md` |
|
||||
| Policy Engine | `src/Policy/StellaOps.Policy.Engine`<br>`src/Policy/__Libraries/StellaOps.Policy.*` | `docs/modules/policy/architecture.md` |
|
||||
| Scanner | `src/Scanner/StellaOps.Scanner.WebService`<br>`src/Scanner/StellaOps.Scanner.Worker`<br>`src/Scanner/__Libraries/StellaOps.Scanner.*` | `docs/modules/scanner/architecture.md` |
|
||||
| Scheduler | `src/Scheduler/StellaOps.Scheduler.WebService`<br>`src/Scheduler/StellaOps.Scheduler.Worker` | `docs/modules/scheduler/architecture.md` |
|
||||
| CLI | `src/Cli/StellaOps.Cli`<br>`src/Cli/StellaOps.Cli.Core`<br>`src/Cli/StellaOps.Cli.Plugins.*` | `docs/modules/cli/architecture.md` |
|
||||
| UI / Console | `src/Web/StellaOps.Web` | `docs/modules/ui/architecture.md` |
|
||||
| Notify | `src/Notify/StellaOps.Notify.WebService`<br>`src/Notify/StellaOps.Notify.Worker` | `docs/modules/notify/architecture.md` |
|
||||
| Export Center | `src/ExportCenter/StellaOps.ExportCenter.WebService`<br>`src/ExportCenter/StellaOps.ExportCenter.Worker` | `docs/modules/export-center/architecture.md` |
|
||||
| Registry Token Service | `src/Registry/StellaOps.Registry.TokenService`<br>`src/Registry/__Tests/StellaOps.Registry.TokenService.Tests` | `docs/modules/registry/architecture.md` |
|
||||
| Advisory AI | `src/AdvisoryAI/StellaOps.AdvisoryAI` | `docs/modules/advisory-ai/architecture.md` |
|
||||
| Orchestrator | `src/Orchestrator/StellaOps.Orchestrator` | `docs/modules/orchestrator/architecture.md` |
|
||||
| Vulnerability Explorer | `src/VulnExplorer/StellaOps.VulnExplorer.Api` | `docs/modules/vuln-explorer/architecture.md` |
|
||||
| VEX Lens | `src/VexLens/StellaOps.VexLens` | `docs/modules/vex-lens/architecture.md` |
|
||||
| Graph Explorer | `src/Graph/StellaOps.Graph.Api`<br>`src/Graph/StellaOps.Graph.Indexer` | `docs/modules/graph/architecture.md` |
|
||||
| Telemetry Stack | `ops/devops/telemetry` | `docs/modules/telemetry/architecture.md` |
|
||||
| DevOps / Release | `ops/devops` | `docs/modules/devops/architecture.md` |
|
||||
| Platform | *(cross-cutting docs)* | `docs/modules/platform/architecture-overview.md` |
|
||||
| CI Recipes | *(pipeline templates)* | `docs/modules/ci/architecture.md` |
|
||||
| Zastava | `src/Zastava/StellaOps.Zastava.Observer`<br>`src/Zastava/StellaOps.Zastava.Webhook`<br>`src/Zastava/StellaOps.Zastava.Core` | `docs/modules/zastava/architecture.md` |
|
||||
|
||||
#### 3.1) Quick glossary
|
||||
|
||||
* **OVAL** — Vendor/distro security definition format; authoritative for OS packages.
|
||||
* **NEVRA / EVR** — RPM and Debian version semantics for OS packages.
|
||||
* **PURL / SemVer** — Coordinates and version semantics for OSS ecosystems.
|
||||
* **KEV** — Known Exploited Vulnerabilities (flag only).
|
||||
|
||||
---
|
||||
|
||||
### 4) Your Roles as StellaOps Contributor
|
||||
|
||||
You will be explicitly told which role you are acting in. Your behavior must change accordingly.
|
||||
|
||||
1. Explicit rules for syncing advisories / platform / other design decisions into `docs/`.
|
||||
2. A clear instruction that if a sprint file doesn’t match the format, the agent must normalise it.
|
||||
3. You never use `git reset` unless explicitly told to do so!
|
||||
|
||||
### 4.1) As product manager (updated)
|
||||
|
||||
Your goals:
|
||||
|
||||
1. Review each file in the advisory directory and Identify new topics or features.
|
||||
2. Then determine whether the topic is relevant by:
|
||||
2. 1. Go one by one the files and extract the essentials first - themes, topics, architecture decions
|
||||
2. 2. Then read each of the archive/*.md files and seek if these are already had been advised. If it exists or it is close - then ignore the topic from the new advisory. Else keep it.
|
||||
2. 3. Check the relevant module docs: `docs/modules/<module>/*arch*.md` for compatibility or contradictions.
|
||||
2. 4. Implementation plans: `docs/implplan/SPRINT_*.md`.
|
||||
2. 5. Historical tasks: `docs/implplan/archived/all-tasks.md`.
|
||||
2. 4. For all of the new topics - then go in SPRINT*.md files and src/* (in according modules) for possible already implementation on the same topic. If same or close - ignore it. Otherwise keep it.
|
||||
2. 5. In case still genuine new topic - and it makes sense for the product - keep it.
|
||||
3. When done for all files and all new genuine topics - present a report. Report must include:
|
||||
- all topics
|
||||
- what are the new things
|
||||
- what could be contracting existing tasks or implementations but might make sense to implemnt
|
||||
4. Once scope is agreed, hand over to your **project manager** role (4.2) to define implementation sprints and tasks.
|
||||
5. **Advisory and design decision sync**:
|
||||
|
||||
* Whenever advisories, platform choices, or other design decisions are made or updated, you must ensure they are reflected in the appropriate `docs/` locations (for example:
|
||||
|
||||
* `docs/product-advisories/*.md` or `docs/product-advisories/archive/*.md`,
|
||||
* module architecture docs under `docs/modules/<module>/architecture*.md`,
|
||||
* design/ADR-style documents under `docs/architecture/**` or similar when applicable).
|
||||
* Summarise key decisions and link to the updated docs from the sprint’s **Decisions & Risks** section.
|
||||
* **AGENTS.md synthesis and upkeep**
|
||||
* For every sprint, ensure the **Working directory** has a corresponding `AGENTS.md` file (for example, `src/Scanner/AGENTS.md` for a Scanner sprint).
|
||||
* If `AGENTS.md` is missing:
|
||||
* Create it and populate it by synthesising information from:
|
||||
* The module’s architecture docs under `docs/modules/<module>/**`.
|
||||
* Relevant ADRs, risk/airgap docs, and product advisories.
|
||||
* The sprint scope itself (roles, expectations, test strategy).
|
||||
* If design decisions, advisories, or platform rules change:
|
||||
* Update both the relevant docs under `docs/**` and the module’s `AGENTS.md` to keep them aligned.
|
||||
* Record the fact that `AGENTS.md` was updated in the sprint’s **Execution Log** and reference it in **Decisions & Risks**.
|
||||
* Treat `AGENTS.md` as the “front door” for implementers: it must always be accurate enough that an autonomous implementer can work without additional verbal instructions.
|
||||
|
||||
---
|
||||
|
||||
### 4.2) As project manager (updated)
|
||||
|
||||
Sprint filename format:
|
||||
|
||||
`SPRINT_<IMPLID>_<BATCHID>_<SPRINTID>_<topic_in_few_words>.md`
|
||||
|
||||
* `<IMPLID>`: `0000–9999` — implementation epoch (e.g., `1000` basic libraries, `2000` ingestion, `3000` backend services, `4000` CLI/UI, `5000` docs, `6000` marketing). When in doubt, use the highest number already present.
|
||||
* `<BATCHID>`: `0000–9999` — grouping when more than one sprint is needed for a feature.
|
||||
* `<SPRINTID>`: `0000–9999` — sprint index within the batch.
|
||||
* `<topic_in_few_words>`: short topic description.
|
||||
* **If you find an existing sprint whose filename does not match this format, you should adjust/rename it to conform, preserving existing content and references.** Document the rename in the sprint’s **Execution Log**.
|
||||
|
||||
Sprint file template:
|
||||
|
||||
```md
|
||||
# Sprint <ID> · <Stream/Topic>
|
||||
|
||||
## Topic & Scope
|
||||
- Summarise the sprint in 2–4 bullets that read like a short story (expected outcomes and “why now”).
|
||||
- Call out the single owning directory (e.g., `src/Concelier/StellaOps.Concelier.Core`) and the evidence you expect to produce.
|
||||
- **Working directory:** `<path/to/module>`.
|
||||
|
||||
## Dependencies & Concurrency
|
||||
- Upstream sprints or artefacts that must land first.
|
||||
- Confirm peers in the same `CC` decade remain independent so parallel execution is safe.
|
||||
|
||||
## Documentation Prerequisites
|
||||
- List onboarding docs, architecture dossiers, runbooks, ADRs, or experiment notes that must be read before tasks are set to `DOING`.
|
||||
|
||||
## Delivery Tracker
|
||||
| # | Task ID | Status | Key dependency / next step | Owners | Task Definition |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 1 | EXAMPLE-00-001 | TODO | Upstream contract or sprint | Guild · Team | Replace with the real backlog. |
|
||||
|
||||
## Execution Log
|
||||
| Date (UTC) | Update | Owner |
|
||||
| --- | --- | --- |
|
||||
| 2025-11-15 | Sprint created; awaiting staffing. | Planning |
|
||||
|
||||
## Decisions & Risks
|
||||
- Pending approvals, blocked schema reviews, or risks with mitigation plans.
|
||||
|
||||
## Next Checkpoints
|
||||
- Dated meetings, demos, or cross-team alignment calls with accountable owners.
|
||||
```
|
||||
|
||||
* **If you find a sprint file whose internal structure deviates significantly from this template, you should normalise it toward this structure while preserving all existing content (log lines, tasks, decisions).**
|
||||
* Record this normalisation in the **Execution Log** (e.g. “2025-11-16 · Normalised sprint file to standard template; no semantic changes.”).
|
||||
|
||||
Additional responsibilities (add-on):
|
||||
|
||||
* **Advisories / platform / design decision sync**:
|
||||
|
||||
* When platform-level decisions, architecture decisions, or other design choices are confirmed as part of a sprint, ensure they are written down under `docs/` (architecture docs, ADRs, product advisories, or module docs as appropriate).
|
||||
* Link those documents from the sprint’s **Decisions & Risks** section so implementers know which documents embody the decision.
|
||||
|
||||
---
|
||||
|
||||
#### 4.3) As implementer
|
||||
|
||||
You may be asked to work on:
|
||||
|
||||
* A sprint file (`docs/implplan/SPRINT_*.md`), or
|
||||
* A specific task within that sprint.
|
||||
|
||||
In this role you act as:
|
||||
|
||||
* **C# .NET 10 engineer** (backend, libraries, APIs).
|
||||
* **Angular v17 engineer** (UI).
|
||||
* **QA automation engineer** (C#, Moq, Playwright, Angular test stack, or other suitable tools).
|
||||
|
||||
Implementation principles:
|
||||
|
||||
* Always follow .NET 10 and Angular v17 best practices.
|
||||
* Apply SOLID design principles (SRP, OCP, LSP, ISP, DIP) in service and library code.
|
||||
* Maximise reuse and composability.
|
||||
* Maintain determinism: stable ordering, UTC ISO-8601 timestamps, immutable NDJSON where applicable.
|
||||
|
||||
Execution rules (very important):
|
||||
|
||||
* You do **not** ask clarification questions in implementer mode.
|
||||
|
||||
* If you encounter ambiguity or a design decision:
|
||||
|
||||
* Mark the task as `BLOCKED` in the sprint `Delivery Tracker`.
|
||||
* Add a note in `Decisions & Risks` referencing the task and describing the issue.
|
||||
* Skip to the next unblocked task in the same sprint.
|
||||
* If all tasks in the current sprint are blocked:
|
||||
|
||||
* Look for earlier sprints with unblocked tasks.
|
||||
* If none exist, look at later sprints for unblocked tasks.
|
||||
* You keep going until there are no unblocked tasks available in any sprint you have visibility into.
|
||||
|
||||
* All requests for further instruction must be encoded into the sprint documents, **not** as questions:
|
||||
* When you need a decision, assumption, or design clarification, you do **not** ask interactive questions.
|
||||
* Instead, you:
|
||||
* Mark the affected task as `BLOCKED`.
|
||||
* Describe exactly what decision is needed in **Decisions & Risks**.
|
||||
* If helpful, add a dedicated task entry capturing that decision work.
|
||||
* Then continue with other unblocked tasks.
|
||||
|
||||
Additional constraints:
|
||||
|
||||
* **Directory ownership**: Work only inside the module’s directory defined by the sprint’s `Working directory`. Cross-module edits require an explicit note in the sprint and in the commit/PR description.
|
||||
* **AGENTS.md adherence and scoping**
|
||||
* Before starting any task in a module, read that module’s `AGENTS.md` in full and treat it as your local behavioral contract.
|
||||
* Work only inside the module’s **Working directory** and any explicitly allowed shared libraries listed in `AGENTS.md` or the sprint file.
|
||||
* If `AGENTS.md` is missing, clearly outdated, or contradicts the sprint / architecture:
|
||||
* Do **not** ask for clarification from the requester.
|
||||
* Mark the task as `BLOCKED` in the sprint’s **Delivery Tracker**.
|
||||
* Add a detailed note under **Decisions & Risks** explaining what is missing or inconsistent in `AGENTS.md` and that it must be updated by a project manager/architect.
|
||||
* Optionally add a new task row (e.g., `AGENTS-<module>-UPDATE`) describing the required update.
|
||||
* Move on to the next unblocked task in the same or another sprint.
|
||||
* **Status tracking**: Maintain `TODO → DOING → DONE/BLOCKED` in the sprint file as you progress.
|
||||
* **Tests**:
|
||||
|
||||
* Every change must be accompanied by or covered by tests.
|
||||
* Never regress determinism, ordering, or precedence.
|
||||
* Test layout example (for Concelier):
|
||||
|
||||
* Module tests: `StellaOps.Concelier.<Component>.Tests`
|
||||
* Shared fixtures/harnesses: `StellaOps.Concelier.Testing`
|
||||
* **Documentation**:
|
||||
|
||||
* When scope, contracts, or workflows change, update the relevant docs under `docs/modules/**`, `docs/api/`, `docs/risk/`, or `docs/airgap/`.
|
||||
* **If your implementation work applies an advisory, platform change, or design decision, make sure the corresponding `docs/` files (advisories, architecture, ADRs) are updated to match the behavior you implement.**
|
||||
* Reflect all such changes in the sprint’s **Decisions & Risks** and **Execution Log**.
|
||||
|
||||
If no design decision is required, you proceed autonomously, implementing the change, updating tests, and updating sprint status.
|
||||
|
||||
---
|
||||
|
||||
### 5) Working Agreement (Global)
|
||||
|
||||
1. **Task status discipline**
|
||||
|
||||
* Always update task status in `docs/implplan/SPRINT_*.md` when you start (`DOING`), block (`BLOCKED`), finish (`DONE`), or pause (`TODO`) a task.
|
||||
2. **Prerequisites**
|
||||
|
||||
* Confirm that required docs (from `AGENTS.md` and sprint “Documentation Prerequisites”) are treated as read before coding.
|
||||
3. **Determinism & offline posture**
|
||||
|
||||
* Keep outputs deterministic (ordering, timestamps, hashes).
|
||||
* Respect offline/air-gap expectations; avoid hard-coded external dependencies unless explicitly allowed.
|
||||
4. **Coordination & contracts**
|
||||
|
||||
* When contracts, advisories, platform rules, or workflows change, update:
|
||||
|
||||
* The sprint doc (`docs/implplan/SPRINT_*.md`),
|
||||
* The relevant `docs/` artefacts (product advisories, architecture docs, ADRs, risk or airgap docs),
|
||||
* And ensure cross-references (links) are present in **Decisions & Risks**.
|
||||
* **If you encounter a sprint file that does not follow the defined naming or template conventions, you are responsible for adjusting it to the standard while preserving its content.**
|
||||
5. **Completion**
|
||||
|
||||
* When you complete all tasks in scope for your current instruction set, explicitly state that you are done with those tasks.
|
||||
6. **AGENTS.md discipline**
|
||||
* Project / technical managers ensure each module’s `AGENTS.md` exists, is up to date, and reflects current design and advisory decisions.
|
||||
* Implementers must read and follow the relevant `AGENTS.md` before coding in a module.
|
||||
* If a mismatch or gap is found, implementers log it via `BLOCKED` status and the sprint’s **Decisions & Risks**, and then continue with other work instead of asking for live clarification.
|
||||
|
||||
---
|
||||
|
||||
### 7) Advisory Handling (do this every time a new advisory lands)
|
||||
|
||||
**Trigger:** Any new or updated file under `docs/product-advisories/` (including archived) automatically starts this workflow. No chat approval required.
|
||||
|
||||
1) **Doc sync (must happen for every advisory):**
|
||||
- Create/update **two layers**:
|
||||
- **High-level**: `docs/` (vision/key-features/market) to capture the moat/positioning and the headline promise.
|
||||
- **Detailed**: closest deep area (`docs/reachability/*`, `docs/market/*`, `docs/benchmarks/*`, `docs/modules/<module>/*`, etc.).
|
||||
- **Code & samples:**
|
||||
- Inline only short fragments (≤ ~20 lines) directly in the updated doc for readability.
|
||||
- Place runnable or longer samples/harnesses in `docs/benchmarks/**` or `tests/**` with deterministic, offline-friendly defaults (no network, fixed seeds), and link to them from the doc.
|
||||
- If the advisory already contains code, carry it over verbatim into the benchmark/test file (with minor formatting only); don’t paraphrase away executable value.
|
||||
- **Cross-links:** whenever moats/positioning change, add links from `docs/07_HIGH_LEVEL_ARCHITECTURE.md`, `docs/key-features.md`, and the relevant module dossier(s).
|
||||
|
||||
2) **Sprint sync (must happen for every advisory):**
|
||||
- Add Delivery Tracker rows in the relevant `SPRINT_*.md` with owners, deps, and doc paths; add an Execution Log entry for the change.
|
||||
- If code/bench/dataset work is implied, create tasks and point to the new benchmark/test paths; add risks/interlocks for schema/feed freeze or transparency caps as needed.
|
||||
|
||||
3) **De-duplication:**
|
||||
- Check `docs/product-advisories/archived/` for overlaps. If similar, mark “supersedes/extends <advisory>` in the new doc and avoid duplicate tasks.
|
||||
|
||||
4) **Defaults to apply (unless advisory overrides):**
|
||||
- Hybrid reachability posture: graph DSSE mandatory; edge-bundle DSSE optional/targeted; deterministic outputs only.
|
||||
- Offline-friendly benches/tests; frozen feeds; deterministic ordering/hashes.
|
||||
|
||||
5) **Do not defer:** Execute steps 1–4 immediately; reporting is after the fact, not a gating step.
|
||||
|
||||
**Lessons baked in:** Past delays came from missing code carry-over and missing sprint tasks. Always move advisory code into benchmarks/tests and open the corresponding sprint rows the same session you read the advisory.
|
||||
---
|
||||
|
||||
### 6) Role Switching
|
||||
|
||||
* If an instruction says “as product manager…”, “as project manager…”, or “as implementer…”, you must immediately adopt that role’s behavior and constraints.
|
||||
* If no role is specified:
|
||||
|
||||
* Default to **project manager** behavior (validate → plan → propose tasks).
|
||||
* Under no circumstances should you mix the “no questions” constraint of implementer mode into product / project manager modes. Only implementer mode is forbidden from asking questions.
|
||||
|
||||
222
CLAUDE.md
Normal file
222
CLAUDE.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
StellaOps is a self-hostable, sovereign container-security platform released under AGPL-3.0-or-later. It provides reproducible vulnerability scanning with VEX-first decisioning, SBOM generation (SPDX 3.0.1 and CycloneDX 1.6), in-toto/DSSE attestations, and optional Sigstore Rekor transparency. The platform is designed for offline/air-gapped operation with regional crypto support (eIDAS/FIPS/GOST/SM).
|
||||
|
||||
## Build Commands
|
||||
|
||||
```bash
|
||||
# Build the entire solution
|
||||
dotnet build src/StellaOps.sln
|
||||
|
||||
# Build a specific module (example: Concelier web service)
|
||||
dotnet build src/Concelier/StellaOps.Concelier.WebService/StellaOps.Concelier.WebService.csproj
|
||||
|
||||
# Run the Concelier web service
|
||||
dotnet run --project src/Concelier/StellaOps.Concelier.WebService
|
||||
|
||||
# Build CLI for current platform
|
||||
dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj --configuration Release
|
||||
|
||||
# Build CLI for specific runtime (linux-x64, linux-arm64, osx-x64, osx-arm64, win-x64)
|
||||
dotnet publish src/Cli/StellaOps.Cli/StellaOps.Cli.csproj --configuration Release --runtime linux-x64
|
||||
```
|
||||
|
||||
## Test Commands
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
dotnet test src/StellaOps.sln
|
||||
|
||||
# Run tests for a specific project
|
||||
dotnet test src/Scanner/__Tests/StellaOps.Scanner.WebService.Tests/StellaOps.Scanner.WebService.Tests.csproj
|
||||
|
||||
# Run a single test by filter
|
||||
dotnet test --filter "FullyQualifiedName~TestMethodName"
|
||||
|
||||
# Run tests with verbosity
|
||||
dotnet test src/StellaOps.sln --verbosity normal
|
||||
```
|
||||
|
||||
**Note:** Integration tests use Testcontainers for PostgreSQL. Ensure Docker is running before executing tests.
|
||||
|
||||
## Linting and Validation
|
||||
|
||||
```bash
|
||||
# Lint OpenAPI specs
|
||||
npm run api:lint
|
||||
|
||||
# Validate attestation schemas
|
||||
npm run docs:attestor:validate
|
||||
|
||||
# Validate Helm chart
|
||||
helm lint deploy/helm/stellaops
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Technology Stack
|
||||
- **Runtime:** .NET 10 (`net10.0`) with latest C# preview features
|
||||
- **Frontend:** Angular v17 (in `src/Web/StellaOps.Web`)
|
||||
- **Database:** PostgreSQL (≥16) with per-module schema isolation; see `docs/db/` for specification
|
||||
- **Testing:** xUnit with Testcontainers (PostgreSQL), Moq, Microsoft.AspNetCore.Mvc.Testing
|
||||
- **Observability:** Structured logging, OpenTelemetry traces
|
||||
- **NuGet:** Uses standard NuGet feeds configured in `nuget.config` (dotnet-public, nuget-mirror, nuget.org)
|
||||
|
||||
### Module Structure
|
||||
|
||||
The codebase follows a monorepo pattern with modules under `src/`:
|
||||
|
||||
| Module | Path | Purpose |
|
||||
|--------|------|---------|
|
||||
| Concelier | `src/Concelier/` | Vulnerability advisory ingestion and merge engine |
|
||||
| CLI | `src/Cli/` | Command-line interface for scanner distribution and job control |
|
||||
| Scanner | `src/Scanner/` | Container scanning with SBOM generation |
|
||||
| Authority | `src/Authority/` | Authentication and authorization |
|
||||
| Signer | `src/Signer/` | Cryptographic signing operations |
|
||||
| Attestor | `src/Attestor/` | in-toto/DSSE attestation generation |
|
||||
| Excititor | `src/Excititor/` | VEX document ingestion and export |
|
||||
| Policy | `src/Policy/` | OPA/Rego policy engine |
|
||||
| Scheduler | `src/Scheduler/` | Job scheduling and queue management |
|
||||
| Notify | `src/Notify/` | Notification delivery (Email, Slack, Teams) |
|
||||
| Zastava | `src/Zastava/` | Container registry webhook observer |
|
||||
|
||||
### Code Organization Patterns
|
||||
|
||||
- **Libraries:** `src/<Module>/__Libraries/StellaOps.<Module>.*`
|
||||
- **Tests:** `src/<Module>/__Tests/StellaOps.<Module>.*.Tests/`
|
||||
- **Plugins:** Follow naming `StellaOps.<Module>.Connector.*` or `StellaOps.<Module>.Plugin.*`
|
||||
- **Shared test infrastructure:** `StellaOps.Concelier.Testing` and `StellaOps.Infrastructure.Postgres.Testing` provide PostgreSQL fixtures
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
- All modules are .NET 10 projects, except the UI (Angular)
|
||||
- Module projects: `StellaOps.<ModuleName>`
|
||||
- Libraries/plugins common to multiple modules: `StellaOps.<LibraryOrPlugin>`
|
||||
- Each project lives in its own folder
|
||||
|
||||
### Key Glossary
|
||||
|
||||
- **OVAL** — Vendor/distro security definition format; authoritative for OS packages
|
||||
- **NEVRA / EVR** — RPM and Debian version semantics for OS packages
|
||||
- **PURL / SemVer** — Coordinates and version semantics for OSS ecosystems
|
||||
- **KEV** — Known Exploited Vulnerabilities (flag only)
|
||||
|
||||
## Coding Rules
|
||||
|
||||
### Core Principles
|
||||
|
||||
1. **Determinism:** Outputs must be reproducible - stable ordering, UTC ISO-8601 timestamps, immutable NDJSON where applicable
|
||||
2. **Offline-first:** Remote host allowlist, strict schema validation, avoid hard-coded external dependencies unless explicitly allowed
|
||||
3. **Plugin architecture:** Concelier connectors, Authority plugins, Scanner analyzers are all plugin-based
|
||||
4. **VEX-first decisioning:** Exploitability modeled in OpenVEX with lattice logic for stable outcomes
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Follow .NET 10 and Angular v17 best practices
|
||||
- Apply SOLID principles (SRP, OCP, LSP, ISP, DIP) when designing services, libraries, and tests
|
||||
- Maximise reuse and composability
|
||||
- Never regress determinism, ordering, or precedence
|
||||
- Every change must be accompanied by or covered by tests
|
||||
- Gated LLM usage (only where explicitly configured)
|
||||
|
||||
### Test Layout
|
||||
|
||||
- Module tests: `StellaOps.<Module>.<Component>.Tests`
|
||||
- Shared fixtures/harnesses: `StellaOps.<Module>.Testing`
|
||||
- Tests use xUnit, Testcontainers for PostgreSQL integration tests
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
When scope, contracts, or workflows change, update the relevant docs under:
|
||||
- `docs/modules/**` - Module architecture dossiers
|
||||
- `docs/api/` - API documentation
|
||||
- `docs/risk/` - Risk documentation
|
||||
- `docs/airgap/` - Air-gap operation docs
|
||||
|
||||
## Role-Based Behavior
|
||||
|
||||
When working in this repository, behavior changes based on the role specified:
|
||||
|
||||
### As Implementer (Default for coding tasks)
|
||||
|
||||
- Work only inside the module's directory defined by the sprint's "Working directory"
|
||||
- Cross-module edits require explicit notes in commit/PR descriptions
|
||||
- Do **not** ask clarification questions - if ambiguity exists:
|
||||
- Mark the task as `BLOCKED` in the sprint `Delivery Tracker`
|
||||
- Add a note in `Decisions & Risks` describing the issue
|
||||
- Skip to the next unblocked task
|
||||
- Maintain status tracking: `TODO → DOING → DONE/BLOCKED` in sprint files
|
||||
- Read the module's `AGENTS.md` before coding in that module
|
||||
|
||||
### As Project Manager
|
||||
|
||||
- Sprint files follow format: `SPRINT_<IMPLID>_<BATCHID>_<SPRINTID>_<topic>.md`
|
||||
- IMPLID epochs: `1000` basic libraries, `2000` ingestion, `3000` backend services, `4000` CLI/UI, `5000` docs, `6000` marketing
|
||||
- Normalize sprint files to standard template while preserving content
|
||||
- Ensure module `AGENTS.md` files exist and are up to date
|
||||
|
||||
### As Product Manager
|
||||
|
||||
- Review advisories in `docs/product-advisories/`
|
||||
- Check for overlaps with `docs/product-advisories/archived/`
|
||||
- Validate against module docs and existing implementations
|
||||
- Hand over to project manager role for sprint/task definition
|
||||
|
||||
## Task Workflow
|
||||
|
||||
### Status Discipline
|
||||
|
||||
Always update task status in `docs/implplan/SPRINT_*.md`:
|
||||
- `TODO` - Not started
|
||||
- `DOING` - In progress
|
||||
- `DONE` - Completed
|
||||
- `BLOCKED` - Waiting on decision/clarification
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before coding, confirm required docs are read:
|
||||
- `docs/README.md`
|
||||
- `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- `docs/modules/platform/architecture-overview.md`
|
||||
- Relevant module dossier (e.g., `docs/modules/<module>/architecture.md`)
|
||||
- Module-specific `AGENTS.md` file
|
||||
|
||||
### Git Rules
|
||||
|
||||
- Never use `git reset` unless explicitly told to do so
|
||||
- Never skip hooks (--no-verify, --no-gpg-sign) unless explicitly requested
|
||||
|
||||
## Configuration
|
||||
|
||||
- **Sample configs:** `etc/concelier.yaml.sample`, `etc/authority.yaml.sample`
|
||||
- **Plugin manifests:** `etc/authority.plugins/*.yaml`
|
||||
- **NuGet sources:** Curated packages in `local-nugets/`, public sources configured in `Directory.Build.props`
|
||||
|
||||
## Documentation
|
||||
|
||||
- **Architecture overview:** `docs/07_HIGH_LEVEL_ARCHITECTURE.md`
|
||||
- **Module dossiers:** `docs/modules/<module>/architecture.md`
|
||||
- **Database specification:** `docs/db/SPECIFICATION.md`
|
||||
- **PostgreSQL operations:** `docs/operations/postgresql-guide.md`
|
||||
- **API/CLI reference:** `docs/09_API_CLI_REFERENCE.md`
|
||||
- **Offline operation:** `docs/24_OFFLINE_KIT.md`
|
||||
- **Quickstart:** `docs/10_CONCELIER_CLI_QUICKSTART.md`
|
||||
- **Sprint planning:** `docs/implplan/SPRINT_*.md`
|
||||
|
||||
## CI/CD
|
||||
|
||||
Workflows are in `.gitea/workflows/`. Key workflows:
|
||||
- `build-test-deploy.yml` - Main build, test, and deployment pipeline
|
||||
- `cli-build.yml` - CLI multi-platform builds
|
||||
- `scanner-determinism.yml` - Scanner output reproducibility tests
|
||||
- `policy-lint.yml` - Policy validation
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `STELLAOPS_BACKEND_URL` - Backend API URL for CLI
|
||||
- `STELLAOPS_TEST_POSTGRES_CONNECTION` - PostgreSQL connection string for integration tests
|
||||
- `StellaOpsEnableCryptoPro` - Enable GOST crypto support (set to `true` in build)
|
||||
@@ -1,12 +1,90 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<StellaOpsRepoRoot Condition="'$(StellaOpsRepoRoot)' == ''">$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))</StellaOpsRepoRoot>
|
||||
<StellaOpsLocalNuGetSource Condition="'$(StellaOpsLocalNuGetSource)' == ''">$([System.IO.Path]::GetFullPath('$(StellaOpsRepoRoot)local-nuget/'))</StellaOpsLocalNuGetSource>
|
||||
<StellaOpsDotNetPublicSource Condition="'$(StellaOpsDotNetPublicSource)' == ''">https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json</StellaOpsDotNetPublicSource>
|
||||
<StellaOpsNuGetOrgSource Condition="'$(StellaOpsNuGetOrgSource)' == ''">https://api.nuget.org/v3/index.json</StellaOpsNuGetOrgSource>
|
||||
<_StellaOpsDefaultRestoreSources>$(StellaOpsLocalNuGetSource);$(StellaOpsDotNetPublicSource);$(StellaOpsNuGetOrgSource)</_StellaOpsDefaultRestoreSources>
|
||||
<_StellaOpsOriginalRestoreSources Condition="'$(_StellaOpsOriginalRestoreSources)' == ''">$(RestoreSources)</_StellaOpsOriginalRestoreSources>
|
||||
<RestoreSources Condition="'$(_StellaOpsOriginalRestoreSources)' == ''">$(_StellaOpsDefaultRestoreSources)</RestoreSources>
|
||||
<RestoreSources Condition="'$(_StellaOpsOriginalRestoreSources)' != ''">$(_StellaOpsDefaultRestoreSources);$(_StellaOpsOriginalRestoreSources)</RestoreSources>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<StellaOpsRepoRoot Condition="'$(StellaOpsRepoRoot)' == ''">$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))</StellaOpsRepoRoot>
|
||||
<StellaOpsDotNetPublicSource Condition="'$(StellaOpsDotNetPublicSource)' == ''">https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json</StellaOpsDotNetPublicSource>
|
||||
<RestoreConfigFile Condition="'$(RestoreConfigFile)' == ''">$([System.IO.Path]::Combine('$(StellaOpsRepoRoot)','NuGet.config'))</RestoreConfigFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<StellaOpsEnableCryptoPro Condition="'$(StellaOpsEnableCryptoPro)' == ''">false</StellaOpsEnableCryptoPro>
|
||||
<NoWarn>$(NoWarn);NU1608;NU1605;NU1202</NoWarn>
|
||||
<WarningsNotAsErrors>$(WarningsNotAsErrors);NU1608;NU1605;NU1202</WarningsNotAsErrors>
|
||||
<RestoreNoWarn>$(RestoreNoWarn);NU1608;NU1605;NU1202</RestoreNoWarn>
|
||||
<RestoreWarningsAsErrors></RestoreWarningsAsErrors>
|
||||
<RestoreTreatWarningsAsErrors>false</RestoreTreatWarningsAsErrors>
|
||||
<RestoreDisableImplicitNuGetFallbackFolder>true</RestoreDisableImplicitNuGetFallbackFolder>
|
||||
<RestoreFallbackFolders>clear</RestoreFallbackFolders>
|
||||
<RestoreFallbackFoldersExcludes>clear</RestoreFallbackFoldersExcludes>
|
||||
<RestoreAdditionalProjectFallbackFolders>clear</RestoreAdditionalProjectFallbackFolders>
|
||||
<RestoreAdditionalProjectFallbackFoldersExcludes>clear</RestoreAdditionalProjectFallbackFoldersExcludes>
|
||||
<RestoreAdditionalFallbackFolders>clear</RestoreAdditionalFallbackFolders>
|
||||
<RestoreAdditionalFallbackFoldersExcludes>clear</RestoreAdditionalFallbackFoldersExcludes>
|
||||
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<AssetTargetFallback>$(AssetTargetFallback);net8.0;net7.0;net6.0;netstandard2.1;netstandard2.0</AssetTargetFallback>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(StellaOpsEnableCryptoPro)' == 'true'">
|
||||
<DefineConstants>$(DefineConstants);STELLAOPS_CRYPTO_PRO</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Update="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Update="Microsoft.Extensions.Options" Version="10.0.0" />
|
||||
<PackageReference Update="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
|
||||
<PackageReference Update="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
|
||||
<PackageReference Update="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- .NET 10 compatible package version overrides -->
|
||||
<ItemGroup>
|
||||
<!-- Cryptography packages - updated for net10.0 compatibility -->
|
||||
<PackageReference Update="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
<PackageReference Update="Pkcs11Interop" Version="5.1.2" />
|
||||
|
||||
<!-- Resilience - Polly 8.x for .NET 6+ -->
|
||||
<PackageReference Update="Polly" Version="8.5.2" />
|
||||
<PackageReference Update="Polly.Core" Version="8.5.2" />
|
||||
|
||||
<!-- YAML - updated for net10.0 -->
|
||||
<PackageReference Update="YamlDotNet" Version="16.3.0" />
|
||||
|
||||
<!-- JSON Schema packages -->
|
||||
<PackageReference Update="JsonSchema.Net" Version="7.3.2" />
|
||||
<PackageReference Update="Json.More.Net" Version="2.1.0" />
|
||||
<PackageReference Update="JsonPointer.Net" Version="5.1.0" />
|
||||
|
||||
<!-- HTML parsing -->
|
||||
<PackageReference Update="AngleSharp" Version="1.2.0" />
|
||||
|
||||
<!-- Scheduling -->
|
||||
<PackageReference Update="Cronos" Version="0.9.0" />
|
||||
|
||||
<!-- Testing - xUnit 2.9.3 for .NET 10 -->
|
||||
<PackageReference Update="xunit" Version="2.9.3" />
|
||||
<PackageReference Update="xunit.assert" Version="2.9.3" />
|
||||
<PackageReference Update="xunit.extensibility.core" Version="2.9.3" />
|
||||
<PackageReference Update="xunit.extensibility.execution" Version="2.9.3" />
|
||||
<PackageReference Update="xunit.runner.visualstudio" Version="3.0.1" />
|
||||
<PackageReference Update="xunit.abstractions" Version="2.0.3" />
|
||||
|
||||
<!-- JSON -->
|
||||
<PackageReference Update="Newtonsoft.Json" Version="13.0.4" />
|
||||
|
||||
<!-- Annotations -->
|
||||
<PackageReference Update="JetBrains.Annotations" Version="2024.3.0" />
|
||||
|
||||
<!-- Async interfaces -->
|
||||
<PackageReference Update="Microsoft.Bcl.AsyncInterfaces" Version="10.0.0" />
|
||||
|
||||
<!-- HTTP Resilience integration (replaces Http.Polly) -->
|
||||
<PackageReference Update="Microsoft.Extensions.Http.Resilience" Version="10.0.0" />
|
||||
|
||||
<!-- Testing packages - aligned to 10.0.0 -->
|
||||
<PackageReference Update="Microsoft.Extensions.TimeProvider.Testing" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
4
Directory.Build.rsp
Normal file
4
Directory.Build.rsp
Normal file
@@ -0,0 +1,4 @@
|
||||
/nowarn:CA2022
|
||||
/p:DisableWorkloadResolver=true
|
||||
/p:RestoreAdditionalProjectFallbackFolders=
|
||||
/p:RestoreFallbackFolders=
|
||||
@@ -1,8 +1,10 @@
|
||||
# Third-Party Notices
|
||||
|
||||
This project bundles or links against the following third-party components in the scanner Ruby analyzer implementation:
|
||||
This project bundles or links against the following third-party components:
|
||||
|
||||
- **tree-sitter** (MIT License, © 2018 Max Brunsfeld)
|
||||
- **tree-sitter-ruby** (MIT License, © 2016 Rob Rix)
|
||||
- **tree-sitter** (MIT License, (c) 2018 Max Brunsfeld)
|
||||
- **tree-sitter-ruby** (MIT License, (c) 2016 Rob Rix)
|
||||
- **GostCryptography (fork)** (MIT License, (c) 2014-2024 AlexMAS) — vendored under `third_party/forks/AlexMAS.GostCryptography` for GOST support in `StellaOps.Cryptography.Plugin.CryptoPro` and related sovereign crypto plug-ins.
|
||||
- **CryptoPro CSP integration** (Commercial, customer-provided) — StellaOps ships only integration code; CryptoPro CSP binaries and licenses are not redistributed and must be supplied by the operator per vendor EULA.
|
||||
|
||||
License texts are available under third-party-licenses/.
|
||||
|
||||
55
NuGet.config
55
NuGet.config
@@ -1,45 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<config>
|
||||
<add key="restoreIgnoreFailedSources" value="true" />
|
||||
</config>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="local" value="local-nuget" />
|
||||
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
<packageSourceMapping>
|
||||
<packageSource key="local">
|
||||
<package pattern="Mongo2Go" />
|
||||
<package pattern="Microsoft.IdentityModel.Tokens" />
|
||||
<package pattern="Microsoft.Extensions.Http.Polly" />
|
||||
<package pattern="Microsoft.Extensions.Caching.Memory" />
|
||||
<package pattern="Microsoft.Extensions.Configuration" />
|
||||
<package pattern="Microsoft.Extensions.Configuration.Binder" />
|
||||
<package pattern="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<package pattern="Microsoft.Extensions.Hosting" />
|
||||
<package pattern="Microsoft.Extensions.Hosting.Abstractions" />
|
||||
<package pattern="Microsoft.Extensions.Http" />
|
||||
<package pattern="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<package pattern="Microsoft.Extensions.Options" />
|
||||
<package pattern="Microsoft.Extensions.Options.ConfigurationExtensions" />
|
||||
<package pattern="Microsoft.Data.Sqlite" />
|
||||
<package pattern="Microsoft.IdentityModel.Logging" />
|
||||
<package pattern="Microsoft.IdentityModel.Abstractions" />
|
||||
<package pattern="Microsoft.AspNetCore.Authentication.JwtBearer" />
|
||||
<package pattern="Google.Protobuf" />
|
||||
<package pattern="Grpc.*" />
|
||||
</packageSource>
|
||||
<packageSource key="dotnet-public">
|
||||
<package pattern="Microsoft.Extensions.*" />
|
||||
<package pattern="Microsoft.AspNetCore.*" />
|
||||
<package pattern="Microsoft.Data.Sqlite" />
|
||||
<package pattern="Microsoft.OpenApi*" />
|
||||
<package pattern="System.Diagnostics.*" />
|
||||
</packageSource>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
<fallbackPackageFolders>
|
||||
<clear />
|
||||
</fallbackPackageFolders>
|
||||
</configuration>
|
||||
|
||||
10
README.md
10
README.md
@@ -1,14 +1,20 @@
|
||||
# StellaOps Concelier & CLI
|
||||
|
||||
[](https://git.stella-ops.org/stellaops/feedser/actions/workflows/build-test-deploy.yml)
|
||||
[](https://git.stella-ops.org/stellaops/feedser/actions/workflows/build-test-deploy.yml)
|
||||
[](docs/testing/ci-quality-gates.md)
|
||||
[](docs/testing/ci-quality-gates.md)
|
||||
[](docs/testing/mutation-testing-baselines.md)
|
||||
|
||||
This repository hosts the StellaOps Concelier service, its plug-in ecosystem, and the
|
||||
first-party CLI (`stellaops-cli`). Concelier ingests vulnerability advisories from
|
||||
authoritative sources, stores them in MongoDB, and exports deterministic JSON and
|
||||
authoritative sources, stores them in PostgreSQL, and exports deterministic JSON and
|
||||
Trivy DB artefacts. The CLI drives scanner distribution, scan execution, and job
|
||||
control against the Concelier API.
|
||||
|
||||
## Quickstart
|
||||
|
||||
1. Prepare a MongoDB instance and (optionally) install `trivy-db`/`oras`.
|
||||
1. Prepare a PostgreSQL instance and (optionally) install `trivy-db`/`oras`.
|
||||
2. Copy `etc/concelier.yaml.sample` to `etc/concelier.yaml` and update the storage + telemetry
|
||||
settings.
|
||||
3. Copy `etc/authority.yaml.sample` to `etc/authority.yaml`, review the issuer, token
|
||||
|
||||
19
StellaOps.Router.slnx
Normal file
19
StellaOps.Router.slnx
Normal file
@@ -0,0 +1,19 @@
|
||||
<Solution>
|
||||
<Folder Name="/src/" />
|
||||
<Folder Name="/src/Gateway/">
|
||||
<Project Path="src/Gateway/StellaOps.Gateway.WebService/StellaOps.Gateway.WebService.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/src/__Libraries/">
|
||||
<Project Path="src/__Libraries/StellaOps.Microservice.SourceGen/StellaOps.Microservice.SourceGen.csproj" />
|
||||
<Project Path="src/__Libraries/StellaOps.Microservice/StellaOps.Microservice.csproj" />
|
||||
<Project Path="src/__Libraries/StellaOps.Router.Common/StellaOps.Router.Common.csproj" />
|
||||
<Project Path="src/__Libraries/StellaOps.Router.Config/StellaOps.Router.Config.csproj" />
|
||||
<Project Path="src/__Libraries/StellaOps.Router.Transport.InMemory/StellaOps.Router.Transport.InMemory.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/tests/">
|
||||
<Project Path="tests/StellaOps.Gateway.WebService.Tests/StellaOps.Gateway.WebService.Tests.csproj" />
|
||||
<Project Path="tests/StellaOps.Microservice.Tests/StellaOps.Microservice.Tests.csproj" />
|
||||
<Project Path="tests/StellaOps.Router.Common.Tests/StellaOps.Router.Common.Tests.csproj" />
|
||||
<Project Path="tests/StellaOps.Router.Transport.InMemory.Tests/StellaOps.Router.Transport.InMemory.Tests.csproj" />
|
||||
</Folder>
|
||||
</Solution>
|
||||
132
bench/README.md
Normal file
132
bench/README.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# Stella Ops Bench Repository
|
||||
|
||||
> **Status:** Active · Last updated: 2025-12-13
|
||||
> **Purpose:** Host reproducible VEX decisions, reachability evidence, and comparison data proving Stella Ops' signal quality vs. baseline scanners.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
bench/
|
||||
README.md # this file
|
||||
findings/ # per CVE/product bundles
|
||||
CVE-YYYY-NNNNN/
|
||||
evidence/
|
||||
reachability.json # richgraph-v1 excerpt
|
||||
sbom.cdx.json # CycloneDX SBOM
|
||||
decision.openvex.json # OpenVEX decision
|
||||
decision.dsse.json # DSSE envelope
|
||||
rekor.txt # Rekor log index + inclusion proof
|
||||
metadata.json # finding metadata (purl, CVE, version)
|
||||
tools/
|
||||
verify.sh # DSSE + Rekor verifier (online)
|
||||
verify.py # offline verifier
|
||||
compare.py # baseline comparison script
|
||||
replay.sh # runs reachability replay manifests
|
||||
results/
|
||||
summary.csv # aggregated metrics
|
||||
runs/<date>/... # raw outputs + replay manifests
|
||||
reachability-benchmark/ # reachability benchmark with JDK fixtures
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [VEX Evidence Playbook](../docs/benchmarks/vex-evidence-playbook.md) | Proof bundle schema, justification catalog, verification workflow |
|
||||
| [Hybrid Attestation](../docs/reachability/hybrid-attestation.md) | Graph-level and edge-bundle DSSE decisions |
|
||||
| [Function-Level Evidence](../docs/reachability/function-level-evidence.md) | Cross-module evidence chain guide |
|
||||
| [Deterministic Replay](../docs/replay/DETERMINISTIC_REPLAY.md) | Replay manifest specification |
|
||||
|
||||
## Verification Workflows
|
||||
|
||||
### Quick Verification (Online)
|
||||
|
||||
```bash
|
||||
# Verify a VEX proof bundle with DSSE and Rekor
|
||||
./tools/verify.sh findings/CVE-2021-44228/decision.dsse.json
|
||||
|
||||
# Output:
|
||||
# ✓ DSSE signature valid
|
||||
# ✓ Rekor inclusion verified (log index: 12345678)
|
||||
# ✓ Evidence hashes match
|
||||
# ✓ Justification catalog membership confirmed
|
||||
```
|
||||
|
||||
### Offline Verification
|
||||
|
||||
```bash
|
||||
# Verify without network access
|
||||
python tools/verify.py \
|
||||
--bundle findings/CVE-2021-44228/decision.dsse.json \
|
||||
--cas-root ./findings/CVE-2021-44228/evidence/ \
|
||||
--catalog ../docs/benchmarks/vex-justifications.catalog.json
|
||||
|
||||
# Or use the VEX proof bundle verifier
|
||||
python ../scripts/vex/verify_proof_bundle.py \
|
||||
--bundle ../tests/Vex/ProofBundles/sample-proof-bundle.json \
|
||||
--cas-root ../tests/Vex/ProofBundles/cas/
|
||||
```
|
||||
|
||||
### Reachability Graph Verification
|
||||
|
||||
```bash
|
||||
# Verify graph DSSE
|
||||
stella graph verify --hash blake3:a1b2c3d4...
|
||||
|
||||
# Verify with edge bundles
|
||||
stella graph verify --hash blake3:a1b2c3d4... --include-bundles
|
||||
|
||||
# Offline with local CAS
|
||||
stella graph verify --hash blake3:a1b2c3d4... --cas-root ./offline-cas/
|
||||
```
|
||||
|
||||
### Baseline Comparison
|
||||
|
||||
```bash
|
||||
# Compare Stella Ops findings against baseline scanners
|
||||
python tools/compare.py \
|
||||
--stellaops results/runs/2025-12-13/findings.json \
|
||||
--baseline results/baselines/trivy-latest.json \
|
||||
--output results/comparison-2025-12-13.csv
|
||||
|
||||
# Metrics generated:
|
||||
# - True positives (reachability-confirmed)
|
||||
# - False positives (unreachable code paths)
|
||||
# - MTTD (mean time to detect)
|
||||
# - Reproducibility score
|
||||
```
|
||||
|
||||
## Artifact Contracts
|
||||
|
||||
All bench artifacts must comply with:
|
||||
|
||||
1. **VEX Proof Bundle Schema** (`docs/benchmarks/vex-evidence-playbook.schema.json`)
|
||||
- BLAKE3-256 primary hash, SHA-256 secondary
|
||||
- Canonical JSON with sorted keys
|
||||
- DSSE envelope with Rekor-ready digest
|
||||
|
||||
2. **Justification Catalog** (`docs/benchmarks/vex-justifications.catalog.json`)
|
||||
- VEX1-VEX10 justification codes
|
||||
- Required evidence types per justification
|
||||
- Expiry and re-evaluation rules
|
||||
|
||||
3. **Reachability Graph** (`docs/contracts/richgraph-v1.md`)
|
||||
- BLAKE3 graph_hash for content addressing
|
||||
- Deterministic node/edge ordering
|
||||
- SymbolID/EdgeID format compliance
|
||||
|
||||
## CI Integration
|
||||
|
||||
The bench directory is validated by:
|
||||
|
||||
- `.gitea/workflows/vex-proof-bundles.yml` - Verifies all proof bundles
|
||||
- `.gitea/workflows/bench-determinism.yml` - Runs determinism benchmarks
|
||||
- `.gitea/workflows/hybrid-attestation.yml` - Verifies graph/edge-bundle fixtures
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Add new findings under `findings/CVE-YYYY-NNNNN/`
|
||||
2. Include all required evidence artifacts
|
||||
3. Generate DSSE envelope and Rekor proof
|
||||
4. Update `results/summary.csv`
|
||||
5. Run verification: `./tools/verify.sh findings/CVE-YYYY-NNNNN/decision.dsse.json`
|
||||
56
bench/baselines/ttfs-baseline.json
Normal file
56
bench/baselines/ttfs-baseline.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||
"title": "TTFS Baseline",
|
||||
"description": "Time-to-First-Signal baseline metrics for regression detection",
|
||||
"version": "1.0.0",
|
||||
"created_at": "2025-12-16T00:00:00Z",
|
||||
"updated_at": "2025-12-16T00:00:00Z",
|
||||
"metrics": {
|
||||
"ttfs_ms": {
|
||||
"p50": 1500,
|
||||
"p95": 4000,
|
||||
"p99": 6000,
|
||||
"min": 500,
|
||||
"max": 10000,
|
||||
"mean": 2000,
|
||||
"sample_count": 500
|
||||
},
|
||||
"by_scan_type": {
|
||||
"image_scan": {
|
||||
"p50": 2500,
|
||||
"p95": 5000,
|
||||
"p99": 7500,
|
||||
"description": "Container image scanning TTFS baseline"
|
||||
},
|
||||
"filesystem_scan": {
|
||||
"p50": 1000,
|
||||
"p95": 2000,
|
||||
"p99": 3000,
|
||||
"description": "Filesystem/directory scanning TTFS baseline"
|
||||
},
|
||||
"sbom_scan": {
|
||||
"p50": 400,
|
||||
"p95": 800,
|
||||
"p99": 1200,
|
||||
"description": "SBOM-only scanning TTFS baseline"
|
||||
}
|
||||
}
|
||||
},
|
||||
"thresholds": {
|
||||
"p50_max_ms": 2000,
|
||||
"p95_max_ms": 5000,
|
||||
"p99_max_ms": 8000,
|
||||
"max_regression_pct": 10,
|
||||
"description": "Thresholds that will trigger CI gate failures"
|
||||
},
|
||||
"collection_info": {
|
||||
"test_environment": "ci-standard-runner",
|
||||
"runner_specs": {
|
||||
"cpu_cores": 4,
|
||||
"memory_gb": 8,
|
||||
"storage_type": "ssd"
|
||||
},
|
||||
"sample_corpus": "tests/reachability/corpus",
|
||||
"collection_window_days": 30
|
||||
}
|
||||
}
|
||||
10
bench/findings/CVE-2015-7547-reachable/decision.dsse.json
Normal file
10
bench/findings/CVE-2015-7547-reachable/decision.dsse.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"payload": "eyJAY29udGV4dCI6Imh0dHBzOi8vb3BlbnZleC5kZXYvbnMvdjAuMi4wIiwiQHR5cGUiOiJWRVgiLCJhdXRob3IiOiJTdGVsbGFPcHMgQmVuY2ggQXV0b21hdGlvbiIsInJvbGUiOiJzZWN1cml0eV90ZWFtIiwic3RhdGVtZW50cyI6W3siYWN0aW9uX3N0YXRlbWVudCI6IlVwZ3JhZGUgdG8gcGF0Y2hlZCB2ZXJzaW9uIG9yIGFwcGx5IG1pdGlnYXRpb24uIiwiaW1wYWN0X3N0YXRlbWVudCI6IkV2aWRlbmNlIGhhc2g6IHNoYTI1NjpiZTMwNDMzZTE4OGEyNTg4NTY0NDYzMzZkYmIxMDk1OWJmYjRhYjM5NzQzODBhOGVhMTI2NDZiZjI2ODdiZjlhIiwicHJvZHVjdHMiOlt7IkBpZCI6InBrZzpnZW5lcmljL2dsaWJjLUNWRS0yMDIzLTQ5MTEtbG9vbmV5LXR1bmFibGVzQDEuMC4wIn1dLCJzdGF0dXMiOiJhZmZlY3RlZCIsInZ1bG5lcmFiaWxpdHkiOnsiQGlkIjoiaHR0cHM6Ly9udmQubmlzdC5nb3YvdnVsbi9kZXRhaWwvQ1ZFLTIwMTUtNzU0NyIsIm5hbWUiOiJDVkUtMjAxNS03NTQ3In19XSwidGltZXN0YW1wIjoiMjAyNS0xMi0xNFQwMjoxMzozOFoiLCJ0b29saW5nIjoiU3RlbGxhT3BzL2JlbmNoLWF1dG9AMS4wLjAiLCJ2ZXJzaW9uIjoxfQ==",
|
||||
"payloadType": "application/vnd.openvex+json",
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "stella.ops/bench-automation@v1",
|
||||
"sig": "PLACEHOLDER_SIGNATURE_REQUIRES_ACTUAL_SIGNING"
|
||||
}
|
||||
]
|
||||
}
|
||||
25
bench/findings/CVE-2015-7547-reachable/decision.openvex.json
Normal file
25
bench/findings/CVE-2015-7547-reachable/decision.openvex.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||
"@type": "VEX",
|
||||
"author": "StellaOps Bench Automation",
|
||||
"role": "security_team",
|
||||
"statements": [
|
||||
{
|
||||
"action_statement": "Upgrade to patched version or apply mitigation.",
|
||||
"impact_statement": "Evidence hash: sha256:be30433e188a258856446336dbb10959bfb4ab3974380a8ea12646bf2687bf9a",
|
||||
"products": [
|
||||
{
|
||||
"@id": "pkg:generic/glibc-CVE-2023-4911-looney-tunables@1.0.0"
|
||||
}
|
||||
],
|
||||
"status": "affected",
|
||||
"vulnerability": {
|
||||
"@id": "https://nvd.nist.gov/vuln/detail/CVE-2015-7547",
|
||||
"name": "CVE-2015-7547"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timestamp": "2025-12-14T02:13:38Z",
|
||||
"tooling": "StellaOps/bench-auto@1.0.0",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"case_id": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"generated_at": "2025-12-14T02:13:38Z",
|
||||
"ground_truth": {
|
||||
"case_id": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"paths": [
|
||||
[
|
||||
"sym://net:handler#read",
|
||||
"sym://glibc:glibc.c#entry",
|
||||
"sym://glibc:glibc.c#sink"
|
||||
]
|
||||
],
|
||||
"schema_version": "reachbench.reachgraph.truth/v1",
|
||||
"variant": "reachable"
|
||||
},
|
||||
"paths": [
|
||||
[
|
||||
"sym://net:handler#read",
|
||||
"sym://glibc:glibc.c#entry",
|
||||
"sym://glibc:glibc.c#sink"
|
||||
]
|
||||
],
|
||||
"schema_version": "richgraph-excerpt/v1",
|
||||
"variant": "reachable"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"components": [
|
||||
{
|
||||
"name": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"purl": "pkg:generic/glibc-CVE-2023-4911-looney-tunables@1.0.0",
|
||||
"type": "library",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"timestamp": "2025-12-14T02:13:38Z",
|
||||
"tools": [
|
||||
{
|
||||
"name": "bench-auto",
|
||||
"vendor": "StellaOps",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"specVersion": "1.6",
|
||||
"version": 1
|
||||
}
|
||||
11
bench/findings/CVE-2015-7547-reachable/metadata.json
Normal file
11
bench/findings/CVE-2015-7547-reachable/metadata.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"case_id": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"cve_id": "CVE-2015-7547",
|
||||
"generated_at": "2025-12-14T02:13:38Z",
|
||||
"generator": "scripts/bench/populate-findings.py",
|
||||
"generator_version": "1.0.0",
|
||||
"ground_truth_schema": "reachbench.reachgraph.truth/v1",
|
||||
"purl": "pkg:generic/glibc-CVE-2023-4911-looney-tunables@1.0.0",
|
||||
"reachability_status": "reachable",
|
||||
"variant": "reachable"
|
||||
}
|
||||
5
bench/findings/CVE-2015-7547-reachable/rekor.txt
Normal file
5
bench/findings/CVE-2015-7547-reachable/rekor.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rekor log entry placeholder
|
||||
# Submit DSSE envelope to Rekor to populate this file
|
||||
log_index: PENDING
|
||||
uuid: PENDING
|
||||
timestamp: 2025-12-14T02:13:38Z
|
||||
10
bench/findings/CVE-2015-7547-unreachable/decision.dsse.json
Normal file
10
bench/findings/CVE-2015-7547-unreachable/decision.dsse.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"payload": "eyJAY29udGV4dCI6Imh0dHBzOi8vb3BlbnZleC5kZXYvbnMvdjAuMi4wIiwiQHR5cGUiOiJWRVgiLCJhdXRob3IiOiJTdGVsbGFPcHMgQmVuY2ggQXV0b21hdGlvbiIsInJvbGUiOiJzZWN1cml0eV90ZWFtIiwic3RhdGVtZW50cyI6W3siaW1wYWN0X3N0YXRlbWVudCI6IkV2aWRlbmNlIGhhc2g6IHNoYTI1NjpjNDJlYzAxNGE0MmQwZTNmYjQzZWQ0ZGRhZDg5NTM4MjFlNDQ0NTcxMTlkYTY2ZGRiNDFhMzVhODAxYTNiNzI3IiwianVzdGlmaWNhdGlvbiI6InZ1bG5lcmFibGVfY29kZV9ub3RfcHJlc2VudCIsInByb2R1Y3RzIjpbeyJAaWQiOiJwa2c6Z2VuZXJpYy9nbGliYy1DVkUtMjAyMy00OTExLWxvb25leS10dW5hYmxlc0AxLjAuMCJ9XSwic3RhdHVzIjoibm90X2FmZmVjdGVkIiwidnVsbmVyYWJpbGl0eSI6eyJAaWQiOiJodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAxNS03NTQ3IiwibmFtZSI6IkNWRS0yMDE1LTc1NDcifX1dLCJ0aW1lc3RhbXAiOiIyMDI1LTEyLTE0VDAyOjEzOjM4WiIsInRvb2xpbmciOiJTdGVsbGFPcHMvYmVuY2gtYXV0b0AxLjAuMCIsInZlcnNpb24iOjF9",
|
||||
"payloadType": "application/vnd.openvex+json",
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "stella.ops/bench-automation@v1",
|
||||
"sig": "PLACEHOLDER_SIGNATURE_REQUIRES_ACTUAL_SIGNING"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||
"@type": "VEX",
|
||||
"author": "StellaOps Bench Automation",
|
||||
"role": "security_team",
|
||||
"statements": [
|
||||
{
|
||||
"impact_statement": "Evidence hash: sha256:c42ec014a42d0e3fb43ed4ddad8953821e44457119da66ddb41a35a801a3b727",
|
||||
"justification": "vulnerable_code_not_present",
|
||||
"products": [
|
||||
{
|
||||
"@id": "pkg:generic/glibc-CVE-2023-4911-looney-tunables@1.0.0"
|
||||
}
|
||||
],
|
||||
"status": "not_affected",
|
||||
"vulnerability": {
|
||||
"@id": "https://nvd.nist.gov/vuln/detail/CVE-2015-7547",
|
||||
"name": "CVE-2015-7547"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timestamp": "2025-12-14T02:13:38Z",
|
||||
"tooling": "StellaOps/bench-auto@1.0.0",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"case_id": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"generated_at": "2025-12-14T02:13:38Z",
|
||||
"ground_truth": {
|
||||
"case_id": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"paths": [],
|
||||
"schema_version": "reachbench.reachgraph.truth/v1",
|
||||
"variant": "unreachable"
|
||||
},
|
||||
"paths": [],
|
||||
"schema_version": "richgraph-excerpt/v1",
|
||||
"variant": "unreachable"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"components": [
|
||||
{
|
||||
"name": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"purl": "pkg:generic/glibc-CVE-2023-4911-looney-tunables@1.0.0",
|
||||
"type": "library",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"timestamp": "2025-12-14T02:13:38Z",
|
||||
"tools": [
|
||||
{
|
||||
"name": "bench-auto",
|
||||
"vendor": "StellaOps",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"specVersion": "1.6",
|
||||
"version": 1
|
||||
}
|
||||
11
bench/findings/CVE-2015-7547-unreachable/metadata.json
Normal file
11
bench/findings/CVE-2015-7547-unreachable/metadata.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"case_id": "glibc-CVE-2023-4911-looney-tunables",
|
||||
"cve_id": "CVE-2015-7547",
|
||||
"generated_at": "2025-12-14T02:13:38Z",
|
||||
"generator": "scripts/bench/populate-findings.py",
|
||||
"generator_version": "1.0.0",
|
||||
"ground_truth_schema": "reachbench.reachgraph.truth/v1",
|
||||
"purl": "pkg:generic/glibc-CVE-2023-4911-looney-tunables@1.0.0",
|
||||
"reachability_status": "unreachable",
|
||||
"variant": "unreachable"
|
||||
}
|
||||
5
bench/findings/CVE-2015-7547-unreachable/rekor.txt
Normal file
5
bench/findings/CVE-2015-7547-unreachable/rekor.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
# Rekor log entry placeholder
|
||||
# Submit DSSE envelope to Rekor to populate this file
|
||||
log_index: PENDING
|
||||
uuid: PENDING
|
||||
timestamp: 2025-12-14T02:13:38Z
|
||||
10
bench/findings/CVE-2022-3602-reachable/decision.dsse.json
Normal file
10
bench/findings/CVE-2022-3602-reachable/decision.dsse.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"payload": "eyJAY29udGV4dCI6Imh0dHBzOi8vb3BlbnZleC5kZXYvbnMvdjAuMi4wIiwiQHR5cGUiOiJWRVgiLCJhdXRob3IiOiJTdGVsbGFPcHMgQmVuY2ggQXV0b21hdGlvbiIsInJvbGUiOiJzZWN1cml0eV90ZWFtIiwic3RhdGVtZW50cyI6W3siYWN0aW9uX3N0YXRlbWVudCI6IlVwZ3JhZGUgdG8gcGF0Y2hlZCB2ZXJzaW9uIG9yIGFwcGx5IG1pdGlnYXRpb24uIiwiaW1wYWN0X3N0YXRlbWVudCI6IkV2aWRlbmNlIGhhc2g6IHNoYTI1NjowMTQzMWZmMWVlZTc5OWM2ZmFkZDU5M2E3ZWMxOGVlMDk0Zjk4MzE0MDk2M2RhNmNiZmQ0YjdmMDZiYTBmOTcwIiwicHJvZHVjdHMiOlt7IkBpZCI6InBrZzpnZW5lcmljL29wZW5zc2wtQ1ZFLTIwMjItMzYwMi14NTA5LW5hbWUtY29uc3RyYWludHNAMS4wLjAifV0sInN0YXR1cyI6ImFmZmVjdGVkIiwidnVsbmVyYWJpbGl0eSI6eyJAaWQiOiJodHRwczovL252ZC5uaXN0Lmdvdi92dWxuL2RldGFpbC9DVkUtMjAyMi0zNjAyIiwibmFtZSI6IkNWRS0yMDIyLTM2MDIifX1dLCJ0aW1lc3RhbXAiOiIyMDI1LTEyLTE0VDAyOjEzOjM4WiIsInRvb2xpbmciOiJTdGVsbGFPcHMvYmVuY2gtYXV0b0AxLjAuMCIsInZlcnNpb24iOjF9",
|
||||
"payloadType": "application/vnd.openvex+json",
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "stella.ops/bench-automation@v1",
|
||||
"sig": "PLACEHOLDER_SIGNATURE_REQUIRES_ACTUAL_SIGNING"
|
||||
}
|
||||
]
|
||||
}
|
||||
25
bench/findings/CVE-2022-3602-reachable/decision.openvex.json
Normal file
25
bench/findings/CVE-2022-3602-reachable/decision.openvex.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||
"@type": "VEX",
|
||||
"author": "StellaOps Bench Automation",
|
||||
"role": "security_team",
|
||||
"statements": [
|
||||
{
|
||||
"action_statement": "Upgrade to patched version or apply mitigation.",
|
||||
"impact_statement": "Evidence hash: sha256:01431ff1eee799c6fadd593a7ec18ee094f983140963da6cbfd4b7f06ba0f970",
|
||||
"products": [
|
||||
{
|
||||
"@id": "pkg:generic/openssl-CVE-2022-3602-x509-name-constraints@1.0.0"
|
||||
}
|
||||
],
|
||||
"status": "affected",
|
||||
"vulnerability": {
|
||||
"@id": "https://nvd.nist.gov/vuln/detail/CVE-2022-3602",
|
||||
"name": "CVE-2022-3602"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timestamp": "2025-12-14T02:13:38Z",
|
||||
"tooling": "StellaOps/bench-auto@1.0.0",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"case_id": "openssl-CVE-2022-3602-x509-name-constraints",
|
||||
"generated_at": "2025-12-14T02:13:38Z",
|
||||
"ground_truth": {
|
||||
"case_id": "openssl-CVE-2022-3602-x509-name-constraints",
|
||||
"paths": [
|
||||
[
|
||||
"sym://net:handler#read",
|
||||
"sym://openssl:openssl.c#entry",
|
||||
"sym://openssl:openssl.c#sink"
|
||||
]
|
||||
],
|
||||
"schema_version": "reachbench.reachgraph.truth/v1",
|
||||
"variant": "reachable"
|
||||
},
|
||||
"paths": [
|
||||
[
|
||||
"sym://net:handler#read",
|
||||
"sym://openssl:openssl.c#entry",
|
||||
"sym://openssl:openssl.c#sink"
|
||||
]
|
||||
],
|
||||
"schema_version": "richgraph-excerpt/v1",
|
||||
"variant": "reachable"
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"components": [
|
||||
{
|
||||
"name": "openssl-CVE-2022-3602-x509-name-constraints",
|
||||
"purl": "pkg:generic/openssl-CVE-2022-3602-x509-name-constraints@1.0.0",
|
||||
"type": "library",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"timestamp": "2025-12-14T02:13:38Z",
|
||||
"tools": [
|
||||
{
|
||||
"name": "bench-auto",
|
||||
"vendor": "StellaOps",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"specVersion": "1.6",
|
||||
"version": 1
|
||||
}
|
||||
11
bench/findings/CVE-2022-3602-reachable/metadata.json
Normal file
11
bench/findings/CVE-2022-3602-reachable/metadata.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"case_id": "openssl-CVE-2022-3602-x509-name-constraints",
|
||||
"cve_id": "CVE-2022-3602",
|
||||
"generated_at": "2025-12-14T02:13:38Z",
|
||||
"generator": "scripts/bench/populate-findings.py",
|
||||
"generator_version": "1.0.0",
|
||||
"ground_truth_schema": "reachbench.reachgraph.truth/v1",
|
||||
"purl": "pkg:generic/openssl-CVE-2022-3602-x509-name-constraints@1.0.0",
|
||||
"reachability_status": "reachable",
|
||||
"variant": "reachable"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user