openbox-langgraph-sdk-python 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openbox_langgraph_sdk_python-0.2.0/.coverage +0 -0
- openbox_langgraph_sdk_python-0.2.0/.github/CODEOWNERS +10 -0
- openbox_langgraph_sdk_python-0.2.0/.github/workflows/pr-governance.yml +128 -0
- openbox_langgraph_sdk_python-0.2.0/.github/workflows/pr-quality.yml +204 -0
- openbox_langgraph_sdk_python-0.2.0/.github/workflows/pr-security.yml +173 -0
- openbox_langgraph_sdk_python-0.2.0/.github/workflows/publish.yml +433 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/.gitignore +1 -0
- openbox_langgraph_sdk_python-0.2.0/.gitleaks.toml +2 -0
- openbox_langgraph_sdk_python-0.2.0/CODE_OF_CONDUCT.md +128 -0
- openbox_langgraph_sdk_python-0.2.0/LICENSE +21 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/PKG-INFO +13 -4
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/README.md +8 -2
- openbox_langgraph_sdk_python-0.2.0/SECURITY.md +27 -0
- openbox_langgraph_sdk_python-0.2.0/coverage.xml +1701 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/__init__.py +14 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/client.py +93 -25
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/config.py +74 -14
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/errors.py +4 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/file_governance_hooks.py +7 -1
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/hook_governance.py +38 -11
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/http_governance_hooks.py +32 -4
- openbox_langgraph_sdk_python-0.2.0/openbox_langgraph/identity.py +160 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/langgraph_handler.py +11 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/otel_setup.py +11 -2
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/pyproject.toml +14 -2
- openbox_langgraph_sdk_python-0.2.0/sonar-project.properties +14 -0
- openbox_langgraph_sdk_python-0.2.0/tests/test_agent_identity.py +94 -0
- openbox_langgraph_sdk_python-0.2.0/tests/test_config_client_core.py +255 -0
- openbox_langgraph_sdk_python-0.2.0/tests/test_did_client_signing.py +163 -0
- openbox_langgraph_sdk_python-0.2.0/tests/test_types_hook_core.py +214 -0
- openbox_langgraph_sdk_python-0.2.0/tests/test_verdict_span_hitl_core.py +238 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/uv.lock +338 -11
- openbox_langgraph_sdk_python-0.1.1/.github/workflows/publish.yml +0 -71
- openbox_langgraph_sdk_python-0.1.1/test-agent/.env.example +0 -9
- openbox_langgraph_sdk_python-0.1.1/test-agent/README.md +0 -27
- openbox_langgraph_sdk_python-0.1.1/test-agent/SETUP.md +0 -455
- openbox_langgraph_sdk_python-0.1.1/test-agent/agent.py +0 -172
- openbox_langgraph_sdk_python-0.1.1/test-agent/pyproject.toml +0 -16
- openbox_langgraph_sdk_python-0.1.1/test-agent/uv.lock +0 -1218
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/AGENTS.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/CLAUDE.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/docs/code-standards.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/docs/codebase-summary.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/docs/project-overview-pdr.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/docs/project-roadmap.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/docs/system-architecture.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/db_governance_hooks.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/hitl.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/span_processor.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/tracing.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/types.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/openbox_langgraph/verdict_handler.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/260320-0329-remove-span-collector/plan.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/260321-2019-port-deepagent-fixes/phase-01-remove-hitl-gates.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/260321-2019-port-deepagent-fixes/phase-02-sqlalchemy-engine.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/260321-2019-port-deepagent-fixes/phase-03-hook-hitl-retry.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/260321-2019-port-deepagent-fixes/plan.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/Explore-260330-1430-comprehensive-sdk-analysis.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/Explore-260330-2300-file-reference.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/Explore-260330-2300-integration-guide.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/code-reviewer-260323-1101-code-reuse-otel-hooks.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/code-reviewer-260323-1101-efficiency-review-otel-http-hook-spans.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/code-reviewer-260323-1101-hacky-patterns-review.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/docs-manager-260321-2153-initial-documentation.md +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/plans/reports/span-flow-visualization.html +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/tests/test_contextvars_propagation.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/tests/test_governance_changes.py +0 -0
- {openbox_langgraph_sdk_python-0.1.1 → openbox_langgraph_sdk_python-0.2.0}/tests/test_telemetry_payload.py +0 -0
|
Binary file
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
name: PR Governance
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
types:
|
|
8
|
+
- opened
|
|
9
|
+
- reopened
|
|
10
|
+
- synchronize
|
|
11
|
+
- edited
|
|
12
|
+
- ready_for_review
|
|
13
|
+
push:
|
|
14
|
+
branches:
|
|
15
|
+
- main
|
|
16
|
+
workflow_dispatch:
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
pull-requests: read
|
|
21
|
+
|
|
22
|
+
concurrency:
|
|
23
|
+
group: pr-governance-${{ github.event.pull_request.number || github.ref }}
|
|
24
|
+
cancel-in-progress: true
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
governance:
|
|
28
|
+
name: Validate PR governance
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
|
|
31
|
+
steps:
|
|
32
|
+
- name: Checkout repository
|
|
33
|
+
uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
fetch-depth: 0
|
|
36
|
+
|
|
37
|
+
- name: Validate branch naming
|
|
38
|
+
if: github.event_name == 'pull_request'
|
|
39
|
+
env:
|
|
40
|
+
BRANCH_NAME: ${{ github.head_ref }}
|
|
41
|
+
run: |
|
|
42
|
+
pattern='^(feat|fix|hotfix|chore|docs|refactor|test|ci|build|release)\/[a-z0-9._-]+$'
|
|
43
|
+
if [[ ! "${BRANCH_NAME}" =~ ${pattern} ]]; then
|
|
44
|
+
echo "Invalid branch name: ${BRANCH_NAME}"
|
|
45
|
+
echo "Expected pattern: <type>/<short-description>"
|
|
46
|
+
exit 1
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
- name: Validate PR title convention
|
|
50
|
+
if: github.event_name == 'pull_request'
|
|
51
|
+
env:
|
|
52
|
+
PR_TITLE: ${{ github.event.pull_request.title }}
|
|
53
|
+
run: |
|
|
54
|
+
pattern='^(feat|fix|chore|docs|refactor|test|build|ci|perf|revert)(\([a-z0-9._/-]+\))?!?: .+$'
|
|
55
|
+
if [[ ! "${PR_TITLE}" =~ ${pattern} ]]; then
|
|
56
|
+
echo "Invalid PR title: ${PR_TITLE}"
|
|
57
|
+
echo "Use Conventional Commits, for example: feat(worker): add hook governance"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
- name: Validate commit messages when enabled
|
|
62
|
+
if: github.event_name == 'pull_request' && vars.ENFORCE_COMMIT_CONVENTION == 'true'
|
|
63
|
+
env:
|
|
64
|
+
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
65
|
+
run: |
|
|
66
|
+
git fetch origin "${BASE_REF}" --depth=1
|
|
67
|
+
invalid=0
|
|
68
|
+
while IFS= read -r subject; do
|
|
69
|
+
if [[ -z "${subject}" ]]; then
|
|
70
|
+
continue
|
|
71
|
+
fi
|
|
72
|
+
if [[ "${subject}" =~ ^Merge\ ]]; then
|
|
73
|
+
continue
|
|
74
|
+
fi
|
|
75
|
+
if [[ ! "${subject}" =~ ^(feat|fix|chore|docs|refactor|test|build|ci|perf|revert)(\([a-z0-9._/-]+\))?!?:\ .+ ]]; then
|
|
76
|
+
echo "Invalid commit subject: ${subject}"
|
|
77
|
+
invalid=1
|
|
78
|
+
fi
|
|
79
|
+
done < <(git log --format=%s "origin/${BASE_REF}..HEAD")
|
|
80
|
+
exit "${invalid}"
|
|
81
|
+
|
|
82
|
+
- name: Validate sensitive path ownership policy
|
|
83
|
+
if: github.event_name == 'pull_request'
|
|
84
|
+
env:
|
|
85
|
+
BASE_REF: ${{ github.event.pull_request.base.ref }}
|
|
86
|
+
run: |
|
|
87
|
+
git fetch origin "${BASE_REF}" --depth=1
|
|
88
|
+
mapfile -t changed_files < <(git diff --name-only "origin/${BASE_REF}...HEAD")
|
|
89
|
+
|
|
90
|
+
if [ ! -f .github/CODEOWNERS ]; then
|
|
91
|
+
echo ".github/CODEOWNERS is required"
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
needs_check=0
|
|
96
|
+
for file in "${changed_files[@]}"; do
|
|
97
|
+
case "${file}" in
|
|
98
|
+
.github/workflows/*|.github/CODEOWNERS|SECURITY.md|.gitleaks.toml)
|
|
99
|
+
needs_check=1
|
|
100
|
+
;;
|
|
101
|
+
esac
|
|
102
|
+
done
|
|
103
|
+
|
|
104
|
+
if [ "${needs_check}" -eq 0 ]; then
|
|
105
|
+
exit 0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
declare -A required_entries=(
|
|
109
|
+
["/.github/workflows/"]="workflow owners"
|
|
110
|
+
["/.github/CODEOWNERS"]="codeowners owners"
|
|
111
|
+
["/SECURITY.md"]="security owners"
|
|
112
|
+
["/.gitleaks.toml"]="secret scanning owners"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
missing=()
|
|
116
|
+
for pattern in "${!required_entries[@]}"; do
|
|
117
|
+
if ! awk '!/^#/ && NF >= 2 {print $1}' .github/CODEOWNERS | grep -Fxq "${pattern}"; then
|
|
118
|
+
missing+=("${required_entries[$pattern]} (${pattern})")
|
|
119
|
+
fi
|
|
120
|
+
done
|
|
121
|
+
|
|
122
|
+
if [ "${#missing[@]}" -gt 0 ]; then
|
|
123
|
+
printf 'Missing CODEOWNERS coverage for:\n'
|
|
124
|
+
printf ' - %s\n' "${missing[@]}"
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
echo "Sensitive file ownership policy is defined in CODEOWNERS."
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
name: PR Quality
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
types:
|
|
8
|
+
- opened
|
|
9
|
+
- reopened
|
|
10
|
+
- synchronize
|
|
11
|
+
- ready_for_review
|
|
12
|
+
push:
|
|
13
|
+
branches:
|
|
14
|
+
- main
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
pull-requests: write
|
|
20
|
+
|
|
21
|
+
concurrency:
|
|
22
|
+
group: pr-quality-${{ github.event.pull_request.number || github.ref }}
|
|
23
|
+
cancel-in-progress: true
|
|
24
|
+
|
|
25
|
+
env:
|
|
26
|
+
PYTHON_VERSION: "3.11"
|
|
27
|
+
COVERAGE_THRESHOLD: "60"
|
|
28
|
+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
|
29
|
+
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
|
|
30
|
+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
test:
|
|
34
|
+
name: Test
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
|
|
37
|
+
steps:
|
|
38
|
+
- name: Checkout repository
|
|
39
|
+
uses: actions/checkout@v4
|
|
40
|
+
|
|
41
|
+
- name: Set up Python
|
|
42
|
+
uses: actions/setup-python@v5
|
|
43
|
+
with:
|
|
44
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
45
|
+
|
|
46
|
+
- name: Set up uv
|
|
47
|
+
uses: astral-sh/setup-uv@v5
|
|
48
|
+
|
|
49
|
+
- name: Install dependencies
|
|
50
|
+
run: uv sync --all-extras --dev
|
|
51
|
+
|
|
52
|
+
- name: Run unit tests with coverage
|
|
53
|
+
id: unit_tests
|
|
54
|
+
continue-on-error: true
|
|
55
|
+
run: |
|
|
56
|
+
uv run pytest tests/ \
|
|
57
|
+
--cov=openbox_langgraph \
|
|
58
|
+
--cov-report=xml:coverage.xml \
|
|
59
|
+
--cov-report=term-missing \
|
|
60
|
+
--cov-fail-under=${COVERAGE_THRESHOLD} \
|
|
61
|
+
-v
|
|
62
|
+
|
|
63
|
+
- name: Upload coverage report
|
|
64
|
+
if: ${{ always() && hashFiles('coverage.xml') != '' }}
|
|
65
|
+
uses: actions/upload-artifact@v4
|
|
66
|
+
with:
|
|
67
|
+
name: coverage-xml
|
|
68
|
+
path: coverage.xml
|
|
69
|
+
if-no-files-found: error
|
|
70
|
+
|
|
71
|
+
- name: Upload coverage to Codecov
|
|
72
|
+
if: ${{ always() && env.CODECOV_TOKEN != '' && hashFiles('coverage.xml') != '' }}
|
|
73
|
+
uses: codecov/codecov-action@v5
|
|
74
|
+
with:
|
|
75
|
+
files: ./coverage.xml
|
|
76
|
+
flags: unittests
|
|
77
|
+
name: pr-quality
|
|
78
|
+
fail_ci_if_error: true
|
|
79
|
+
verbose: true
|
|
80
|
+
token: ${{ env.CODECOV_TOKEN }}
|
|
81
|
+
|
|
82
|
+
- name: Fail job if tests failed
|
|
83
|
+
if: ${{ steps.unit_tests.outcome == 'failure' }}
|
|
84
|
+
run: exit 1
|
|
85
|
+
|
|
86
|
+
build:
|
|
87
|
+
name: Build
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
if: ${{ always() }}
|
|
90
|
+
needs:
|
|
91
|
+
- test
|
|
92
|
+
|
|
93
|
+
steps:
|
|
94
|
+
- name: Checkout repository
|
|
95
|
+
uses: actions/checkout@v4
|
|
96
|
+
|
|
97
|
+
- name: Set up Python
|
|
98
|
+
uses: actions/setup-python@v5
|
|
99
|
+
with:
|
|
100
|
+
python-version: ${{ env.PYTHON_VERSION }}
|
|
101
|
+
|
|
102
|
+
- name: Install build tooling
|
|
103
|
+
run: python -m pip install --upgrade build
|
|
104
|
+
|
|
105
|
+
- name: Build package
|
|
106
|
+
run: python -m build
|
|
107
|
+
|
|
108
|
+
- name: Upload build artifacts
|
|
109
|
+
uses: actions/upload-artifact@v4
|
|
110
|
+
with:
|
|
111
|
+
name: python-dist
|
|
112
|
+
path: dist/
|
|
113
|
+
if-no-files-found: error
|
|
114
|
+
|
|
115
|
+
sonarqube:
|
|
116
|
+
name: SonarQube
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
if: ${{ always() }}
|
|
119
|
+
needs:
|
|
120
|
+
- test
|
|
121
|
+
|
|
122
|
+
steps:
|
|
123
|
+
- name: Skip when SonarQube secrets are not configured
|
|
124
|
+
if: ${{ env.SONAR_TOKEN == '' || env.SONAR_HOST_URL == '' }}
|
|
125
|
+
run: echo "Skipping SonarQube because SONAR_TOKEN or SONAR_HOST_URL is not configured."
|
|
126
|
+
|
|
127
|
+
- name: Checkout repository
|
|
128
|
+
if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' }}
|
|
129
|
+
uses: actions/checkout@v4
|
|
130
|
+
with:
|
|
131
|
+
fetch-depth: 0
|
|
132
|
+
|
|
133
|
+
- name: Download coverage report
|
|
134
|
+
if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' }}
|
|
135
|
+
continue-on-error: true
|
|
136
|
+
uses: actions/download-artifact@v4
|
|
137
|
+
with:
|
|
138
|
+
name: coverage-xml
|
|
139
|
+
path: .
|
|
140
|
+
|
|
141
|
+
- name: Set up Java
|
|
142
|
+
if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' }}
|
|
143
|
+
uses: actions/setup-java@v4
|
|
144
|
+
with:
|
|
145
|
+
distribution: temurin
|
|
146
|
+
java-version: "17"
|
|
147
|
+
|
|
148
|
+
- name: Cache SonarQube packages
|
|
149
|
+
if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' }}
|
|
150
|
+
uses: actions/cache@v4
|
|
151
|
+
with:
|
|
152
|
+
path: ~/.sonar/cache
|
|
153
|
+
key: ${{ runner.os }}-sonar
|
|
154
|
+
restore-keys: ${{ runner.os }}-sonar
|
|
155
|
+
|
|
156
|
+
- name: SonarQube scan
|
|
157
|
+
if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' }}
|
|
158
|
+
uses: SonarSource/sonarqube-scan-action@v7
|
|
159
|
+
env:
|
|
160
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
161
|
+
SONAR_TOKEN: ${{ env.SONAR_TOKEN }}
|
|
162
|
+
SONAR_HOST_URL: ${{ env.SONAR_HOST_URL }}
|
|
163
|
+
|
|
164
|
+
- name: SonarQube Quality Gate check
|
|
165
|
+
id: sonarqube_quality_gate
|
|
166
|
+
if: ${{ env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' }}
|
|
167
|
+
continue-on-error: true
|
|
168
|
+
uses: SonarSource/sonarqube-quality-gate-action@master
|
|
169
|
+
with:
|
|
170
|
+
pollingTimeoutSec: 600
|
|
171
|
+
env:
|
|
172
|
+
SONAR_TOKEN: ${{ env.SONAR_TOKEN }}
|
|
173
|
+
SONAR_HOST_URL: ${{ env.SONAR_HOST_URL }}
|
|
174
|
+
|
|
175
|
+
- name: Print SonarQube result
|
|
176
|
+
if: ${{ always() && env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' }}
|
|
177
|
+
shell: bash
|
|
178
|
+
run: |
|
|
179
|
+
status="${{ steps.sonarqube_quality_gate.outputs.quality-gate-status }}"
|
|
180
|
+
|
|
181
|
+
cat << 'EOF'
|
|
182
|
+
[QUALITY GATE] Checking NEW CODE conditions...
|
|
183
|
+
- FAIL IF: New Code Issues > 5
|
|
184
|
+
- FAIL IF: New Code Security Hotspots Reviewed < 100%
|
|
185
|
+
- FAIL IF: New Code Coverage < 60.0%
|
|
186
|
+
- FAIL IF: New Code Duplicated Lines > 3.0%
|
|
187
|
+
|
|
188
|
+
[QUALITY GATE] Checking OVERALL CODE conditions...
|
|
189
|
+
- FAIL IF: Overall High Severity Issues > 1
|
|
190
|
+
- FAIL IF: Overall Medium Severity Issues > 4
|
|
191
|
+
EOF
|
|
192
|
+
|
|
193
|
+
if [[ -z "${status}" ]]; then
|
|
194
|
+
status="UNKNOWN"
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
echo "Quality Gate = ${status}"
|
|
198
|
+
|
|
199
|
+
- name: Fail job when SonarQube quality gate fails
|
|
200
|
+
if: ${{ always() && env.SONAR_TOKEN != '' && env.SONAR_HOST_URL != '' && steps.sonarqube_quality_gate.outcome == 'failure' }}
|
|
201
|
+
shell: bash
|
|
202
|
+
run: |
|
|
203
|
+
echo "SonarQube Quality Gate failed."
|
|
204
|
+
exit 1
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
name: PR Security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
types:
|
|
8
|
+
- opened
|
|
9
|
+
- reopened
|
|
10
|
+
- synchronize
|
|
11
|
+
- ready_for_review
|
|
12
|
+
push:
|
|
13
|
+
branches:
|
|
14
|
+
- main
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
actions: read
|
|
19
|
+
contents: read
|
|
20
|
+
pull-requests: read
|
|
21
|
+
security-events: write
|
|
22
|
+
|
|
23
|
+
concurrency:
|
|
24
|
+
group: pr-security-${{ github.event.pull_request.number || github.ref }}
|
|
25
|
+
cancel-in-progress: true
|
|
26
|
+
|
|
27
|
+
env:
|
|
28
|
+
GITLEAKS_VERSION: "8.30.0"
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
trivy-fs:
|
|
32
|
+
name: Trivy Filesystem Scan
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout repository
|
|
37
|
+
uses: actions/checkout@v4
|
|
38
|
+
|
|
39
|
+
- name: Set up Trivy
|
|
40
|
+
uses: aquasecurity/setup-trivy@e07451d2e059ed86c2870430ea286b3a9e0bf241
|
|
41
|
+
with:
|
|
42
|
+
cache: true
|
|
43
|
+
version: v0.69.2
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
- name: Run Trivy filesystem scan for console output
|
|
47
|
+
id: trivy_console
|
|
48
|
+
continue-on-error: true
|
|
49
|
+
shell: bash
|
|
50
|
+
run: |
|
|
51
|
+
set -euo pipefail
|
|
52
|
+
trivy fs . \
|
|
53
|
+
--scanners vuln,misconfig \
|
|
54
|
+
--vuln-type os,library \
|
|
55
|
+
--ignore-unfixed \
|
|
56
|
+
--severity HIGH,CRITICAL \
|
|
57
|
+
--include-dev-deps \
|
|
58
|
+
--format table \
|
|
59
|
+
--exit-code 1
|
|
60
|
+
|
|
61
|
+
- name: Run Trivy filesystem scan for SARIF
|
|
62
|
+
id: trivy_sarif
|
|
63
|
+
if: always()
|
|
64
|
+
continue-on-error: true
|
|
65
|
+
shell: bash
|
|
66
|
+
run: |
|
|
67
|
+
set -euo pipefail
|
|
68
|
+
trivy fs . \
|
|
69
|
+
--scanners vuln,misconfig \
|
|
70
|
+
--vuln-type os,library \
|
|
71
|
+
--ignore-unfixed \
|
|
72
|
+
--severity HIGH,CRITICAL \
|
|
73
|
+
--include-dev-deps \
|
|
74
|
+
--format sarif \
|
|
75
|
+
--output trivy-fs-results.sarif \
|
|
76
|
+
--exit-code 1
|
|
77
|
+
|
|
78
|
+
- name: Upload Trivy SARIF to GitHub Security
|
|
79
|
+
if: always()
|
|
80
|
+
continue-on-error: true
|
|
81
|
+
uses: github/codeql-action/upload-sarif@v4
|
|
82
|
+
with:
|
|
83
|
+
sarif_file: trivy-fs-results.sarif
|
|
84
|
+
category: trivy-fs
|
|
85
|
+
|
|
86
|
+
- name: Upload Trivy SARIF artifact
|
|
87
|
+
if: always()
|
|
88
|
+
uses: actions/upload-artifact@v4
|
|
89
|
+
with:
|
|
90
|
+
name: trivy-fs-results
|
|
91
|
+
path: trivy-fs-results.sarif
|
|
92
|
+
if-no-files-found: error
|
|
93
|
+
|
|
94
|
+
- name: Fail job when Trivy detects issues
|
|
95
|
+
if: always()
|
|
96
|
+
shell: bash
|
|
97
|
+
run: |
|
|
98
|
+
if [[ "${{ steps.trivy_console.outcome }}" == "failure" || "${{ steps.trivy_sarif.outcome }}" == "failure" ]]; then
|
|
99
|
+
echo "Trivy detected HIGH/CRITICAL findings."
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
gitleaks:
|
|
104
|
+
name: Gitleaks
|
|
105
|
+
runs-on: ubuntu-latest
|
|
106
|
+
|
|
107
|
+
steps:
|
|
108
|
+
- name: Checkout repository
|
|
109
|
+
uses: actions/checkout@v4
|
|
110
|
+
with:
|
|
111
|
+
fetch-depth: 0
|
|
112
|
+
|
|
113
|
+
- name: Run Gitleaks SARIF scan
|
|
114
|
+
id: gitleaks_scan
|
|
115
|
+
continue-on-error: true
|
|
116
|
+
uses: gitleaks/gitleaks-action@v2
|
|
117
|
+
env:
|
|
118
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
119
|
+
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
|
|
120
|
+
GITLEAKS_CONFIG: .gitleaks.toml
|
|
121
|
+
GITLEAKS_VERSION: ${{ env.GITLEAKS_VERSION }}
|
|
122
|
+
GITLEAKS_ENABLE_COMMENTS: "false"
|
|
123
|
+
GITLEAKS_ENABLE_UPLOAD_ARTIFACT: "false"
|
|
124
|
+
|
|
125
|
+
- name: Ensure Gitleaks SARIF exists
|
|
126
|
+
if: always()
|
|
127
|
+
shell: bash
|
|
128
|
+
run: |
|
|
129
|
+
if [ ! -s results.sarif ]; then
|
|
130
|
+
cat > results.sarif <<'SARIF'
|
|
131
|
+
{
|
|
132
|
+
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
|
|
133
|
+
"version": "2.1.0",
|
|
134
|
+
"runs": [
|
|
135
|
+
{
|
|
136
|
+
"tool": {
|
|
137
|
+
"driver": {
|
|
138
|
+
"name": "gitleaks",
|
|
139
|
+
"informationUri": "https://github.com/gitleaks/gitleaks",
|
|
140
|
+
"rules": []
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"results": []
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
SARIF
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
- name: Upload Gitleaks SARIF to GitHub Security
|
|
151
|
+
if: always()
|
|
152
|
+
continue-on-error: true
|
|
153
|
+
uses: github/codeql-action/upload-sarif@v4
|
|
154
|
+
with:
|
|
155
|
+
sarif_file: results.sarif
|
|
156
|
+
category: gitleaks
|
|
157
|
+
|
|
158
|
+
- name: Upload Gitleaks SARIF artifact
|
|
159
|
+
if: always()
|
|
160
|
+
uses: actions/upload-artifact@v4
|
|
161
|
+
with:
|
|
162
|
+
name: gitleaks-results
|
|
163
|
+
path: results.sarif
|
|
164
|
+
if-no-files-found: error
|
|
165
|
+
|
|
166
|
+
- name: Fail job when Gitleaks detects issues
|
|
167
|
+
if: always()
|
|
168
|
+
shell: bash
|
|
169
|
+
run: |
|
|
170
|
+
if [[ "${{ steps.gitleaks_scan.outcome }}" == "failure" ]]; then
|
|
171
|
+
echo "Gitleaks detected potential secrets."
|
|
172
|
+
exit 1
|
|
173
|
+
fi
|