dep-rank 0.1.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.
Files changed (57) hide show
  1. dep_rank-0.1.0/.github/CODEOWNERS +2 -0
  2. dep_rank-0.1.0/.github/actions/setup-uv/README.md +18 -0
  3. dep_rank-0.1.0/.github/actions/setup-uv/action.yml +19 -0
  4. dep_rank-0.1.0/.github/dependabot.yml +36 -0
  5. dep_rank-0.1.0/.github/workflows/ci.yml +159 -0
  6. dep_rank-0.1.0/.github/workflows/dependency-cooldown-rescan.yml +23 -0
  7. dep_rank-0.1.0/.github/workflows/dependency-cooldown.yml +22 -0
  8. dep_rank-0.1.0/.github/workflows/pre-commit-autoupdate.yml +55 -0
  9. dep_rank-0.1.0/.github/workflows/release.yml +192 -0
  10. dep_rank-0.1.0/.github/workflows/security.yml +160 -0
  11. dep_rank-0.1.0/.github/workflows/tag-release.yml +27 -0
  12. dep_rank-0.1.0/.gitignore +11 -0
  13. dep_rank-0.1.0/.pre-commit-config.yaml +12 -0
  14. dep_rank-0.1.0/.python-version +1 -0
  15. dep_rank-0.1.0/Dockerfile +23 -0
  16. dep_rank-0.1.0/LICENSE +22 -0
  17. dep_rank-0.1.0/PKG-INFO +201 -0
  18. dep_rank-0.1.0/README.md +171 -0
  19. dep_rank-0.1.0/RELEASE.md +119 -0
  20. dep_rank-0.1.0/main.py +4 -0
  21. dep_rank-0.1.0/pyproject.toml +121 -0
  22. dep_rank-0.1.0/pytest.ini +21 -0
  23. dep_rank-0.1.0/setup.cfg +8 -0
  24. dep_rank-0.1.0/src/dep_rank/__init__.py +5 -0
  25. dep_rank-0.1.0/src/dep_rank/_version.py +24 -0
  26. dep_rank-0.1.0/src/dep_rank/cli/__init__.py +1 -0
  27. dep_rank-0.1.0/src/dep_rank/cli/app.py +370 -0
  28. dep_rank-0.1.0/src/dep_rank/cli/formatters.py +92 -0
  29. dep_rank-0.1.0/src/dep_rank/core/__init__.py +31 -0
  30. dep_rank-0.1.0/src/dep_rank/core/cache.py +92 -0
  31. dep_rank-0.1.0/src/dep_rank/core/graphql.py +87 -0
  32. dep_rank-0.1.0/src/dep_rank/core/models.py +64 -0
  33. dep_rank-0.1.0/src/dep_rank/core/rate_limiter.py +41 -0
  34. dep_rank-0.1.0/src/dep_rank/core/scraper.py +319 -0
  35. dep_rank-0.1.0/src/dep_rank/core/search.py +79 -0
  36. dep_rank-0.1.0/src/dep_rank/core/validation.py +74 -0
  37. dep_rank-0.1.0/src/dep_rank/mcp/__init__.py +1 -0
  38. dep_rank-0.1.0/src/dep_rank/mcp/server.py +262 -0
  39. dep_rank-0.1.0/tests/README.md +135 -0
  40. dep_rank-0.1.0/tests/__init__.py +0 -0
  41. dep_rank-0.1.0/tests/cli/__init__.py +0 -0
  42. dep_rank-0.1.0/tests/cli/test_commands.py +288 -0
  43. dep_rank-0.1.0/tests/cli/test_formatters.py +170 -0
  44. dep_rank-0.1.0/tests/conftest.py +185 -0
  45. dep_rank-0.1.0/tests/core/__init__.py +0 -0
  46. dep_rank-0.1.0/tests/core/test_cache.py +108 -0
  47. dep_rank-0.1.0/tests/core/test_graphql.py +128 -0
  48. dep_rank-0.1.0/tests/core/test_models.py +127 -0
  49. dep_rank-0.1.0/tests/core/test_rate_limiter.py +39 -0
  50. dep_rank-0.1.0/tests/core/test_scraper.py +492 -0
  51. dep_rank-0.1.0/tests/core/test_search.py +112 -0
  52. dep_rank-0.1.0/tests/core/test_validation.py +89 -0
  53. dep_rank-0.1.0/tests/mcp/__init__.py +0 -0
  54. dep_rank-0.1.0/tests/mcp/test_lifecycle.py +16 -0
  55. dep_rank-0.1.0/tests/mcp/test_prompts.py +26 -0
  56. dep_rank-0.1.0/tests/mcp/test_tools.py +246 -0
  57. dep_rank-0.1.0/uv.lock +2632 -0
@@ -0,0 +1,2 @@
1
+ # Require review for all workflow and action changes
2
+ /.github/ @j7an
@@ -0,0 +1,18 @@
1
+ # setup-uv composite action
2
+
3
+ Installs uv with dependency caching and runs `uv sync --locked` to install
4
+ the project environment. Used by jobs that need a synced dev environment
5
+ to run tests, linters, type-checkers, or `uv build`.
6
+
7
+ ## Inputs
8
+
9
+ | Name | Default | Description |
10
+ | ---------------- | ------- | ------------------------------------ |
11
+ | `python-version` | `3.11` | Python version to install and use. |
12
+
13
+ ## When NOT to use this action
14
+
15
+ Skip this action and call `astral-sh/setup-uv` inline for jobs that:
16
+
17
+ - Mutate the lockfile (`uv lock`) — `--locked` would fail on drift.
18
+ - Don't need the project environment synced (e.g., `pre-commit autoupdate`).
@@ -0,0 +1,19 @@
1
+ name: Setup uv
2
+ description: Install uv with dependency caching and sync the project env (--locked).
3
+ inputs:
4
+ python-version:
5
+ description: "Python version to install"
6
+ required: false
7
+ default: "3.11"
8
+ runs:
9
+ using: "composite"
10
+ steps:
11
+ - name: Install uv
12
+ uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
13
+ with:
14
+ enable-cache: true
15
+ cache-dependency-glob: "uv.lock"
16
+ python-version: ${{ inputs.python-version }}
17
+ - name: Sync dependencies (locked)
18
+ run: uv sync --locked
19
+ shell: bash
@@ -0,0 +1,36 @@
1
+ version: 2
2
+ updates:
3
+ # Python dependencies via uv
4
+ - package-ecosystem: "uv"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "weekly"
8
+ day: "monday"
9
+ time: "09:00"
10
+ open-pull-requests-limit: 10
11
+ labels:
12
+ - "dependencies"
13
+ - "automated"
14
+ commit-message:
15
+ prefix: "deps"
16
+ include: "scope"
17
+ groups:
18
+ minor-and-patch:
19
+ patterns: ["*"]
20
+ update-types: ["minor", "patch"]
21
+ ignore:
22
+ - dependency-name: "*"
23
+ update-types: ["version-update:semver-major"]
24
+
25
+ # GitHub Actions
26
+ - package-ecosystem: "github-actions"
27
+ directory: "/"
28
+ schedule:
29
+ interval: "weekly"
30
+ labels:
31
+ - "dependencies"
32
+ - "github-actions"
33
+ groups:
34
+ all-actions:
35
+ patterns:
36
+ - "*"
@@ -0,0 +1,159 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ concurrency:
9
+ group: ${{ github.workflow }}-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ permissions: {}
13
+
14
+ jobs:
15
+ update-lockfile:
16
+ if: github.event.pull_request.user.login == 'dependabot[bot]'
17
+ runs-on: ubuntu-latest
18
+ outputs:
19
+ committed: ${{ steps.commit.outputs.committed }}
20
+ permissions:
21
+ contents: read
22
+ steps:
23
+ - name: Harden runner
24
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
25
+ with:
26
+ egress-policy: audit
27
+ - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
28
+ id: app-token
29
+ with:
30
+ client-id: ${{ vars.RELEASE_BOT_APP_ID }}
31
+ private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
32
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
33
+ with:
34
+ ref: ${{ github.head_ref }}
35
+ persist-credentials: false
36
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
37
+ - run: uv lock
38
+ - name: Commit updated lockfile
39
+ id: commit
40
+ env:
41
+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
42
+ HEAD_REF: ${{ github.head_ref }}
43
+ REPOSITORY: ${{ github.repository }}
44
+ run: |
45
+ if git diff --quiet uv.lock; then
46
+ echo "No lockfile changes"
47
+ echo "committed=false" >> "$GITHUB_OUTPUT"
48
+ exit 0
49
+ fi
50
+ CONTENT=$(base64 -w0 uv.lock)
51
+ HEAD_OID=$(git rev-parse HEAD)
52
+ # shellcheck disable=SC2016
53
+ gh api graphql -f query='
54
+ mutation($input: CreateCommitOnBranchInput!) {
55
+ createCommitOnBranch(input: $input) {
56
+ commit { url }
57
+ }
58
+ }
59
+ ' -f "input[branchRef][repositoryNameWithOwner]=$REPOSITORY" \
60
+ -f "input[branchRef][branchName]=$HEAD_REF" \
61
+ -f "input[expectedHeadOid]=${HEAD_OID}" \
62
+ -f "input[message][headline]=deps: update uv.lock" \
63
+ -f "input[fileChanges][additions][0][path]=uv.lock" \
64
+ -f "input[fileChanges][additions][0][contents]=${CONTENT}"
65
+ echo "committed=true" >> "$GITHUB_OUTPUT"
66
+
67
+ lint:
68
+ needs: [update-lockfile]
69
+ if: ${{ !cancelled() }}
70
+ runs-on: ubuntu-latest
71
+ permissions:
72
+ contents: read
73
+ steps:
74
+ - name: Harden runner
75
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
76
+ with:
77
+ egress-policy: audit
78
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
79
+ with:
80
+ ref: ${{ needs.update-lockfile.outputs.committed == 'true' && github.head_ref || '' }}
81
+ persist-credentials: false
82
+ - uses: ./.github/actions/setup-uv
83
+ - name: Ruff format check
84
+ run: uv run ruff format --check src/ tests/
85
+ - name: Ruff check
86
+ run: uv run ruff check src/ tests/
87
+
88
+ typecheck:
89
+ needs: [update-lockfile]
90
+ if: ${{ !cancelled() }}
91
+ runs-on: ubuntu-latest
92
+ permissions:
93
+ contents: read
94
+ steps:
95
+ - name: Harden runner
96
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
97
+ with:
98
+ egress-policy: audit
99
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
100
+ with:
101
+ ref: ${{ needs.update-lockfile.outputs.committed == 'true' && github.head_ref || '' }}
102
+ persist-credentials: false
103
+ - uses: ./.github/actions/setup-uv
104
+ - name: Mypy
105
+ run: uv run mypy src/dep_rank/
106
+
107
+ test:
108
+ needs: [update-lockfile]
109
+ if: ${{ !cancelled() }}
110
+ strategy:
111
+ fail-fast: false
112
+ matrix:
113
+ os: [ubuntu-latest, macos-latest, windows-latest]
114
+ python-version: ['3.11', '3.12', '3.13']
115
+ runs-on: ${{ matrix.os }}
116
+ permissions:
117
+ contents: read
118
+ steps:
119
+ - name: Harden runner
120
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
121
+ with:
122
+ egress-policy: audit
123
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
124
+ with:
125
+ ref: ${{ needs.update-lockfile.outputs.committed == 'true' && github.head_ref || '' }}
126
+ persist-credentials: false
127
+ fetch-depth: 0
128
+ - uses: ./.github/actions/setup-uv
129
+ with:
130
+ python-version: ${{ matrix.python-version }}
131
+ - name: Pytest
132
+ run: uv run pytest
133
+ - name: Diff coverage
134
+ if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
135
+ env:
136
+ BASE_REF: ${{ github.base_ref }}
137
+ run: uv run diff-cover coverage.xml --fail-under=80 --compare-branch="origin/$BASE_REF"
138
+
139
+ dependency-review:
140
+ runs-on: ubuntu-latest
141
+ if: github.event_name == 'pull_request'
142
+ permissions:
143
+ contents: read
144
+ pull-requests: write
145
+ steps:
146
+ - name: Harden runner
147
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
148
+ with:
149
+ egress-policy: audit
150
+ - name: Checkout code
151
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
152
+
153
+ - name: Dependency Review
154
+ uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0
155
+ with:
156
+ fail-on-severity: moderate
157
+ deny-licenses: GPL-2.0, GPL-3.0, AGPL-3.0
158
+ allow-dependencies-licenses: pkg:githubactions/trufflesecurity/trufflehog
159
+ comment-summary-in-pr: on-failure
@@ -0,0 +1,23 @@
1
+ name: Dependency Cool-Down Rescan
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "17 6 * * *"
6
+ workflow_dispatch:
7
+ inputs:
8
+ dry_run:
9
+ description: "If true, log decisions but skip gh run rerun"
10
+ type: boolean
11
+ default: false
12
+
13
+ permissions:
14
+ contents: read
15
+ pull-requests: read
16
+ actions: write
17
+ statuses: read
18
+
19
+ jobs:
20
+ rescan:
21
+ uses: j7an/shared-workflows/.github/workflows/cooldown-rescan.yml@365119edbe2b1c7339d7727d9e8d021784e5bc01 # v2.5.2
22
+ with:
23
+ dry_run: ${{ inputs.dry_run || false }}
@@ -0,0 +1,22 @@
1
+ name: Dependency Cool-Down
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened]
6
+
7
+ permissions:
8
+ contents: write
9
+ pull-requests: write
10
+ statuses: write
11
+ issues: write
12
+
13
+ concurrency:
14
+ group: cooldown-${{ github.event.pull_request.number }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ cooldown:
19
+ uses: j7an/shared-workflows/.github/workflows/dependency-cooldown.yml@365119edbe2b1c7339d7727d9e8d021784e5bc01 # v2.5.2
20
+ with:
21
+ auto_merge: true
22
+ cooldown_days: 5
@@ -0,0 +1,55 @@
1
+ name: Pre-commit Autoupdate
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 8 * * 1" # Monday 08:00 UTC
6
+ workflow_dispatch:
7
+
8
+ permissions: {}
9
+
10
+ jobs:
11
+ autoupdate:
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: read
15
+ steps:
16
+ - name: Harden runner
17
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
18
+ with:
19
+ egress-policy: audit
20
+ - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
21
+ id: app-token
22
+ with:
23
+ client-id: ${{ vars.RELEASE_BOT_APP_ID }}
24
+ private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
25
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26
+ with:
27
+ persist-credentials: false
28
+ - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
29
+ - name: Install pre-commit
30
+ run: uv run pre-commit --version
31
+ - name: Run pre-commit autoupdate
32
+ run: uv run pre-commit autoupdate
33
+ - name: Check for changes
34
+ id: diff
35
+ run: |
36
+ if git diff --quiet .pre-commit-config.yaml; then
37
+ echo "changed=false" >> "$GITHUB_OUTPUT"
38
+ else
39
+ echo "changed=true" >> "$GITHUB_OUTPUT"
40
+ fi
41
+ - name: Create Pull Request
42
+ if: steps.diff.outputs.changed == 'true'
43
+ uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
44
+ with:
45
+ token: ${{ steps.app-token.outputs.token }}
46
+ branch: deps/pre-commit-autoupdate
47
+ title: "deps: update pre-commit hooks"
48
+ commit-message: "deps: update pre-commit hooks"
49
+ sign-commits: true
50
+ add-paths: .pre-commit-config.yaml
51
+ body: |
52
+ Automated update of pre-commit hook versions via `pre-commit autoupdate`.
53
+
54
+ Review the diff to verify hook version changes are expected.
55
+ labels: dependencies
@@ -0,0 +1,192 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: CI gate
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - name: Harden runner
17
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
18
+ with:
19
+ egress-policy: audit
20
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
21
+ with:
22
+ fetch-depth: 0
23
+ - uses: ./.github/actions/setup-uv
24
+ - run: uv run ruff check src/ tests/
25
+ - run: uv run mypy src/dep_rank/
26
+ - run: uv run pytest
27
+
28
+ build:
29
+ name: Build distributions
30
+ needs: test
31
+ runs-on: ubuntu-latest
32
+ steps:
33
+ - name: Harden runner
34
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
35
+ with:
36
+ egress-policy: audit
37
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
38
+ with:
39
+ fetch-depth: 0
40
+ fetch-tags: true
41
+
42
+ - name: Verify tag is on main
43
+ run: |
44
+ git fetch origin main
45
+ if ! git merge-base --is-ancestor ${{ github.sha }} origin/main; then
46
+ echo "ERROR: Tagged commit $(git rev-parse --short HEAD) is not on main"
47
+ exit 1
48
+ fi
49
+
50
+ - uses: ./.github/actions/setup-uv
51
+
52
+ - name: Build sdist and wheel
53
+ run: uv build
54
+
55
+ - name: Verify distributions
56
+ run: |
57
+ ls -la dist/
58
+ WHEEL=$(ls dist/*.whl 2>/dev/null | wc -l)
59
+ SDIST=$(ls dist/*.tar.gz 2>/dev/null | wc -l)
60
+ if [ "$WHEEL" -eq 0 ] || [ "$SDIST" -eq 0 ]; then
61
+ echo "ERROR: Expected both a wheel and sdist in dist/"
62
+ ls dist/
63
+ exit 1
64
+ fi
65
+ echo "Build produced: $(ls dist/)"
66
+
67
+ - name: Upload build artifacts
68
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
69
+ with:
70
+ name: dist
71
+ path: dist/
72
+ retention-days: 7
73
+
74
+ publish-testpypi:
75
+ name: Publish to TestPyPI
76
+ needs: build
77
+ runs-on: ubuntu-latest
78
+ environment:
79
+ name: testpypi
80
+ url: https://test.pypi.org/p/dep-rank
81
+ permissions:
82
+ id-token: write
83
+ attestations: write
84
+ steps:
85
+ - name: Harden runner
86
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
87
+ with:
88
+ egress-policy: audit
89
+ - name: Download build artifacts
90
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
91
+ with:
92
+ name: dist
93
+ path: dist/
94
+
95
+ - name: Publish to TestPyPI
96
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
97
+ with:
98
+ repository-url: https://test.pypi.org/legacy/
99
+ skip-existing: true
100
+
101
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
102
+ with:
103
+ python-version: "3.13"
104
+
105
+ - name: Verify TestPyPI install
106
+ run: |
107
+ VERSION="${GITHUB_REF_NAME#v}"
108
+ echo "Waiting for dep-rank==${VERSION} to appear on TestPyPI..."
109
+ for i in 1 2 3 4 5; do
110
+ sleep $((i * 30))
111
+ if pip install \
112
+ --index-url https://test.pypi.org/simple/ \
113
+ --extra-index-url https://pypi.org/simple/ \
114
+ "dep-rank==${VERSION}" 2>/dev/null; then
115
+ ACTUAL=$(dep-rank --version 2>&1)
116
+ echo "Installed version: ${ACTUAL}"
117
+ echo "${ACTUAL}" | grep -qF "${VERSION}" || {
118
+ echo "ERROR: Expected '${VERSION}' in dep-rank --version output"
119
+ exit 1
120
+ }
121
+ echo "TestPyPI install OK"
122
+ exit 0
123
+ fi
124
+ echo "Attempt $i: not yet available, retrying..."
125
+ done
126
+ echo "ERROR: dep-rank==${VERSION} not available on TestPyPI after 7.5 minutes"
127
+ exit 1
128
+
129
+ publish-pypi:
130
+ name: Publish to PyPI
131
+ needs: [build, publish-testpypi]
132
+ runs-on: ubuntu-latest
133
+ environment:
134
+ name: pypi
135
+ url: https://pypi.org/p/dep-rank
136
+ permissions:
137
+ id-token: write
138
+ attestations: write
139
+ steps:
140
+ - name: Harden runner
141
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
142
+ with:
143
+ egress-policy: audit
144
+ - name: Download build artifacts
145
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
146
+ with:
147
+ name: dist
148
+ path: dist/
149
+
150
+ - name: Publish to PyPI
151
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
152
+
153
+ github-release:
154
+ name: Create GitHub Release
155
+ needs: [build, publish-pypi]
156
+ if: needs.publish-pypi.result == 'success'
157
+ runs-on: ubuntu-latest
158
+ permissions:
159
+ contents: write
160
+ steps:
161
+ - name: Harden runner
162
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
163
+ with:
164
+ egress-policy: audit
165
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
166
+ with:
167
+ fetch-depth: 0
168
+
169
+ - name: Download build artifacts
170
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
171
+ with:
172
+ name: dist
173
+ path: dist/
174
+
175
+ - name: Classify tag (stable vs prerelease)
176
+ id: classify
177
+ env:
178
+ REF: ${{ github.ref_name }}
179
+ run: |
180
+ if [[ "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(\.post[0-9]+)?$ ]]; then
181
+ echo "prerelease=false" >> "$GITHUB_OUTPUT"
182
+ else
183
+ echo "prerelease=true" >> "$GITHUB_OUTPUT"
184
+ fi
185
+
186
+ - name: Create GitHub Release with assets
187
+ uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
188
+ with:
189
+ generate_release_notes: true
190
+ draft: true
191
+ prerelease: ${{ steps.classify.outputs.prerelease }}
192
+ files: dist/*
@@ -0,0 +1,160 @@
1
+ name: Security Scanning
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ merge_group:
9
+ branches: [main]
10
+ schedule:
11
+ - cron: "0 6 * * 1" # Weekly Monday 06:00 UTC
12
+
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ permissions: {}
18
+
19
+ jobs:
20
+ codeql:
21
+ name: CodeQL
22
+ runs-on: ubuntu-latest
23
+ permissions:
24
+ contents: read
25
+ security-events: write
26
+ actions: read
27
+ steps:
28
+ - name: Harden runner
29
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
30
+ with:
31
+ egress-policy: audit
32
+
33
+ - name: Checkout repository
34
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
35
+
36
+ - name: Initialize CodeQL
37
+ uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
38
+ with:
39
+ languages: python
40
+ queries: +security-and-quality
41
+
42
+ - name: Perform CodeQL Analysis
43
+ uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
44
+ with:
45
+ category: "/language:python"
46
+
47
+ trufflehog:
48
+ name: TruffleHog
49
+ runs-on: ubuntu-latest
50
+ permissions:
51
+ contents: read
52
+ steps:
53
+ - name: Harden runner
54
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
55
+ with:
56
+ egress-policy: audit
57
+
58
+ - name: Checkout repository
59
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
60
+ with:
61
+ fetch-depth: 0
62
+
63
+ - name: TruffleHog secret scan
64
+ id: trufflehog
65
+ uses: trufflesecurity/trufflehog@17456f8c7d042d8c82c9a8ca9e937231f9f42e26 # v3.95.2
66
+ continue-on-error: true
67
+ with:
68
+ path: ./
69
+ base: ${{ github.event.pull_request.base.sha || github.event.before }}
70
+ head: ${{ github.event.pull_request.head.sha || github.event.after }}
71
+ extra_args: --only-verified
72
+
73
+ - name: Fail on verified secrets
74
+ if: steps.trufflehog.outcome == 'failure'
75
+ run: |
76
+ echo "::error::TruffleHog detected verified secrets. Review the scan output above."
77
+ exit 1
78
+
79
+ zizmor:
80
+ name: Zizmor
81
+ runs-on: ubuntu-latest
82
+ permissions:
83
+ contents: read
84
+ security-events: write
85
+ steps:
86
+ - name: Harden runner
87
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
88
+ with:
89
+ egress-policy: audit
90
+
91
+ - name: Checkout repository
92
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
93
+ with:
94
+ persist-credentials: false
95
+
96
+ - name: Run Zizmor
97
+ uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
98
+ with:
99
+ version: "1.24.1"
100
+ min-severity: medium
101
+ min-confidence: medium
102
+
103
+ trivy:
104
+ name: Trivy
105
+ runs-on: ubuntu-latest
106
+ permissions:
107
+ contents: read
108
+ security-events: write
109
+ steps:
110
+ - name: Harden runner
111
+ uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
112
+ with:
113
+ egress-policy: audit
114
+
115
+ - name: Checkout repository
116
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
117
+
118
+ - name: Generate SARIF report
119
+ uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
120
+ with:
121
+ scan-type: repo
122
+ format: sarif
123
+ output: trivy-results.sarif
124
+
125
+ - name: Upload Trivy results to GitHub Security tab
126
+ if: always()
127
+ uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
128
+ with:
129
+ sarif_file: trivy-results.sarif
130
+ category: trivy
131
+
132
+ - name: Fail on CRITICAL/HIGH vulnerabilities
133
+ uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
134
+ with:
135
+ scan-type: repo
136
+ format: table
137
+ severity: CRITICAL,HIGH
138
+ exit-code: "1"
139
+
140
+ osv-scan:
141
+ name: OSV-Scanner
142
+ if: github.event_name == 'push' || github.event_name == 'schedule'
143
+ permissions:
144
+ actions: read
145
+ contents: read
146
+ security-events: write
147
+ uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@c51854704019a247608d928f370c98740469d4b5 # v2.3.5
148
+ with:
149
+ scan-args: --recursive ./
150
+
151
+ osv-scan-pr:
152
+ name: OSV-Scanner PR
153
+ if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
154
+ permissions:
155
+ actions: read
156
+ contents: read
157
+ security-events: write
158
+ uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@c51854704019a247608d928f370c98740469d4b5 # v2.3.5
159
+ with:
160
+ scan-args: --recursive ./