dnsid 0.26.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.
- dnsid-0.26.0/.gitattributes +2 -0
- dnsid-0.26.0/.github/CODEOWNERS +1 -0
- dnsid-0.26.0/.github/dependabot.yml +10 -0
- dnsid-0.26.0/.github/workflows/codeql.yml +33 -0
- dnsid-0.26.0/.github/workflows/compliance.yaml +14 -0
- dnsid-0.26.0/.github/workflows/lint-test.yml +57 -0
- dnsid-0.26.0/.github/workflows/regen-docs.yml +73 -0
- dnsid-0.26.0/.github/workflows/release-pr.yml +91 -0
- dnsid-0.26.0/.github/workflows/release.yml +160 -0
- dnsid-0.26.0/.gitignore +57 -0
- dnsid-0.26.0/.gitleaksignore +4 -0
- dnsid-0.26.0/CHANGELOG.md +560 -0
- dnsid-0.26.0/CODE_OF_CONDUCT.md +85 -0
- dnsid-0.26.0/COMPATIBILITY.md +153 -0
- dnsid-0.26.0/CONTRIBUTING.md +104 -0
- dnsid-0.26.0/LICENSE.txt +202 -0
- dnsid-0.26.0/NOTICE.txt +504 -0
- dnsid-0.26.0/PKG-INFO +827 -0
- dnsid-0.26.0/README.md +792 -0
- dnsid-0.26.0/SECURITY.md +85 -0
- dnsid-0.26.0/cliff.toml +47 -0
- dnsid-0.26.0/dnsid/__init__.py +387 -0
- dnsid-0.26.0/dnsid/_crypto.py +249 -0
- dnsid-0.26.0/dnsid/_default_resolver.py +251 -0
- dnsid-0.26.0/dnsid/_https_client.py +363 -0
- dnsid-0.26.0/dnsid/_utils.py +283 -0
- dnsid-0.26.0/dnsid/_verification_budget.py +170 -0
- dnsid-0.26.0/dnsid/agent_status.py +25 -0
- dnsid-0.26.0/dnsid/aws_kms_key_provider.py +611 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/__init__.py +297 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/canonical.py +152 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/checkpoint.py +92 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/checkpoint_store.py +127 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/errors.py +137 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/event_codec.py +472 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/lr.py +185 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/managed_verification_registry.py +176 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/merkle.py +111 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/policy.py +408 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/prepared.py +589 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/proof.py +95 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/reader.py +1486 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/resource_fetcher.py +299 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/signed_note.py +159 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/sqlite_checkpoint_store.py +220 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/stream_bundle.py +759 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/stream_source.py +336 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/stream_verifier.py +674 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/tiles.py +83 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/trust_profile.py +154 -0
- dnsid-0.26.0/dnsid/c2sp_tlog/verification_registry.py +277 -0
- dnsid-0.26.0/dnsid/config_loading.py +742 -0
- dnsid-0.26.0/dnsid/conformance.py +51 -0
- dnsid-0.26.0/dnsid/enums.py +149 -0
- dnsid-0.26.0/dnsid/exceptions.py +190 -0
- dnsid-0.26.0/dnsid/http_signatures.py +889 -0
- dnsid-0.26.0/dnsid/interfaces.py +585 -0
- dnsid-0.26.0/dnsid/jose.py +509 -0
- dnsid-0.26.0/dnsid/local_key_provider.py +616 -0
- dnsid-0.26.0/dnsid/manager.py +2422 -0
- dnsid-0.26.0/dnsid/models.py +2315 -0
- dnsid-0.26.0/dnsid/oidc.py +1029 -0
- dnsid-0.26.0/dnsid/py.typed +0 -0
- dnsid-0.26.0/dnsid/registry.py +69 -0
- dnsid-0.26.0/dnsid/registry_client.py +1759 -0
- dnsid-0.26.0/dnsid/retry.py +86 -0
- dnsid-0.26.0/dnsid/safe_transport.py +385 -0
- dnsid-0.26.0/dnsid/wba_signer.py +435 -0
- dnsid-0.26.0/dnsid/wba_verifier.py +447 -0
- dnsid-0.26.0/dnsid/web_bot_auth.py +625 -0
- dnsid-0.26.0/docs/AGENTS.md +50 -0
- dnsid-0.26.0/docs/quickstart.md +229 -0
- dnsid-0.26.0/docs/reference/configuration.md +335 -0
- dnsid-0.26.0/docs/reference/core.md +643 -0
- dnsid-0.26.0/docs/reference/errors.md +339 -0
- dnsid-0.26.0/docs/reference/interfaces.md +638 -0
- dnsid-0.26.0/docs/reference/key-providers.md +572 -0
- dnsid-0.26.0/docs/reference/nav.json +58 -0
- dnsid-0.26.0/docs/reference/overview.md +60 -0
- dnsid-0.26.0/docs/reference/profile-http-signatures.md +260 -0
- dnsid-0.26.0/docs/reference/profile-jose.md +160 -0
- dnsid-0.26.0/docs/reference/profile-oidc.md +295 -0
- dnsid-0.26.0/docs/reference/profile-web-bot-auth.md +418 -0
- dnsid-0.26.0/docs/reference/records.md +412 -0
- dnsid-0.26.0/docs/reference/registry.md +628 -0
- dnsid-0.26.0/docs/reference/transparency-log.md +1884 -0
- dnsid-0.26.0/docs/reference/verification.md +254 -0
- dnsid-0.26.0/docs/sdk-reference.md +14 -0
- dnsid-0.26.0/docs/security.md +403 -0
- dnsid-0.26.0/examples/a2a/README.md +116 -0
- dnsid-0.26.0/examples/a2a/agent.py +163 -0
- dnsid-0.26.0/examples/a2a/main.py +84 -0
- dnsid-0.26.0/examples/a2a/run.sh +112 -0
- dnsid-0.26.0/examples/a2a/server.py +394 -0
- dnsid-0.26.0/examples/a2a/startup.py +174 -0
- dnsid-0.26.0/examples/local-key-provider/README.md +20 -0
- dnsid-0.26.0/examples/local-key-provider/main.py +80 -0
- dnsid-0.26.0/examples/oidc/README.md +46 -0
- dnsid-0.26.0/examples/oidc/main.py +93 -0
- dnsid-0.26.0/examples/published-signing/README.md +147 -0
- dnsid-0.26.0/examples/published-signing/aws-kms.json +23 -0
- dnsid-0.26.0/examples/published-signing/file.json +19 -0
- dnsid-0.26.0/examples/published-signing/main.py +43 -0
- dnsid-0.26.0/examples/trust-policy/README.md +29 -0
- dnsid-0.26.0/examples/trust-policy/main.py +50 -0
- dnsid-0.26.0/examples/validate-domain/README.md +32 -0
- dnsid-0.26.0/examples/validate-domain/main.py +33 -0
- dnsid-0.26.0/examples/webbotauth/README.md +29 -0
- dnsid-0.26.0/examples/webbotauth/main.py +90 -0
- dnsid-0.26.0/pyproject.toml +75 -0
- dnsid-0.26.0/scripts/gen_docs.py +728 -0
- dnsid-0.26.0/tests/__init__.py +0 -0
- dnsid-0.26.0/tests/_config.py +13 -0
- dnsid-0.26.0/tests/_registry_http.py +23 -0
- dnsid-0.26.0/tests/c2sp_fixtures.py +63 -0
- dnsid-0.26.0/tests/conftest.py +130 -0
- dnsid-0.26.0/tests/test_aws_kms_key_provider.py +663 -0
- dnsid-0.26.0/tests/test_c2sp_logical_identity.py +161 -0
- dnsid-0.26.0/tests/test_c2sp_managed_trust.py +150 -0
- dnsid-0.26.0/tests/test_c2sp_migration.py +692 -0
- dnsid-0.26.0/tests/test_c2sp_resource_fetcher.py +146 -0
- dnsid-0.26.0/tests/test_c2sp_stream_bundle.py +514 -0
- dnsid-0.26.0/tests/test_c2sp_tlog.py +2235 -0
- dnsid-0.26.0/tests/test_c2sp_trust_profile.py +111 -0
- dnsid-0.26.0/tests/test_c2sp_verification_registry.py +500 -0
- dnsid-0.26.0/tests/test_config_loading.py +879 -0
- dnsid-0.26.0/tests/test_corrected_boundaries.py +186 -0
- dnsid-0.26.0/tests/test_counterparty_acceptance.py +504 -0
- dnsid-0.26.0/tests/test_crypto.py +338 -0
- dnsid-0.26.0/tests/test_default_dns_resolver.py +68 -0
- dnsid-0.26.0/tests/test_domain_log.py +261 -0
- dnsid-0.26.0/tests/test_event_signing.py +325 -0
- dnsid-0.26.0/tests/test_gen_docs.py +19 -0
- dnsid-0.26.0/tests/test_generated_kid_is_thumbprint.py +10 -0
- dnsid-0.26.0/tests/test_http_signing.py +827 -0
- dnsid-0.26.0/tests/test_https_client.py +469 -0
- dnsid-0.26.0/tests/test_jwk_from_dict.py +8 -0
- dnsid-0.26.0/tests/test_jws.py +166 -0
- dnsid-0.26.0/tests/test_jwt.py +282 -0
- dnsid-0.26.0/tests/test_key_rotation.py +847 -0
- dnsid-0.26.0/tests/test_local_key_provider.py +270 -0
- dnsid-0.26.0/tests/test_local_key_provider_cli.py +261 -0
- dnsid-0.26.0/tests/test_manager.py +562 -0
- dnsid-0.26.0/tests/test_oidc.py +791 -0
- dnsid-0.26.0/tests/test_post_sg_fanout.py +239 -0
- dnsid-0.26.0/tests/test_presentation_transport.py +227 -0
- dnsid-0.26.0/tests/test_public_api.py +106 -0
- dnsid-0.26.0/tests/test_published_signing_example.py +64 -0
- dnsid-0.26.0/tests/test_registration_groundwork.py +276 -0
- dnsid-0.26.0/tests/test_registration_groundwork_regressions.py +230 -0
- dnsid-0.26.0/tests/test_registry_client.py +1520 -0
- dnsid-0.26.0/tests/test_registry_errors.py +75 -0
- dnsid-0.26.0/tests/test_retry.py +148 -0
- dnsid-0.26.0/tests/test_signed_async_http_client.py +222 -0
- dnsid-0.26.0/tests/test_sqlite_checkpoint_store.py +209 -0
- dnsid-0.26.0/tests/test_ssrf_guard.py +308 -0
- dnsid-0.26.0/tests/test_trust_policy_example.py +34 -0
- dnsid-0.26.0/tests/test_txt_record.py +632 -0
- dnsid-0.26.0/tests/test_unified_registration.py +279 -0
- dnsid-0.26.0/tests/test_utils.py +203 -0
- dnsid-0.26.0/tests/test_verification_budget.py +98 -0
- dnsid-0.26.0/tests/test_verification_coalescing.py +459 -0
- dnsid-0.26.0/tests/test_verify_domain.py +1345 -0
- dnsid-0.26.0/tests/test_wba_contracts.py +442 -0
- dnsid-0.26.0/tests/test_wba_signer.py +295 -0
- dnsid-0.26.0/tests/test_wba_verifier.py +548 -0
- dnsid-0.26.0/tests/test_web_bot_auth.py +828 -0
- dnsid-0.26.0/tests/vectors/c2sp-event-identity.json +37 -0
- dnsid-0.26.0/tests/vectors/c2sp-managed-trust-selection.json +85 -0
- dnsid-0.26.0/tests/vectors/c2sp-stream-bundle-v1.json +68 -0
- dnsid-0.26.0/tests/vectors/generate_c2sp_stream_bundle.py +258 -0
- dnsid-0.26.0/uv.lock +779 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @dnsid-ai/sdk-maintainers
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: '27 3 * * 1'
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
security-events: write
|
|
13
|
+
packages: read
|
|
14
|
+
actions: read
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
analyze:
|
|
19
|
+
name: Analyze (${{ matrix.language }})
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
strategy:
|
|
22
|
+
fail-fast: false
|
|
23
|
+
matrix:
|
|
24
|
+
language: ['python']
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
27
|
+
- uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
|
28
|
+
with:
|
|
29
|
+
languages: ${{ matrix.language }}
|
|
30
|
+
- uses: github/codeql-action/autobuild@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
|
31
|
+
- uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
|
|
32
|
+
with:
|
|
33
|
+
category: /language:${{ matrix.language }}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: Compliance
|
|
2
|
+
on: pull_request
|
|
3
|
+
permissions:
|
|
4
|
+
contents: read
|
|
5
|
+
issues: write
|
|
6
|
+
jobs:
|
|
7
|
+
compliance:
|
|
8
|
+
# The private compliance harness is not available to fork pull requests.
|
|
9
|
+
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
10
|
+
uses: dnsid-ai/dnsid-sdk-compliance/.github/workflows/sdk-compliance.yaml@74b48988742602e9ef95eec04f5638b366491ff2
|
|
11
|
+
with:
|
|
12
|
+
sdk: py
|
|
13
|
+
ref: ${{ github.event.pull_request.head.sha }}
|
|
14
|
+
base-ref: ${{ github.event.pull_request.base.sha }}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Lint and Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches:
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint-test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout repository
|
|
21
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
22
|
+
|
|
23
|
+
- name: Setup Python
|
|
24
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python-version }}
|
|
27
|
+
cache: pip
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
# Scans the installed environment (runtime + dev). Accepted risks: add
|
|
33
|
+
# `--ignore-vuln <ID>` here with a comment linking the advisory.
|
|
34
|
+
- name: Dependency vulnerability audit
|
|
35
|
+
if: matrix.python-version == '3.13'
|
|
36
|
+
run: pip install pip-audit && pip-audit
|
|
37
|
+
|
|
38
|
+
- name: Ruff lint check
|
|
39
|
+
run: ruff check .
|
|
40
|
+
|
|
41
|
+
- name: Mypy type check
|
|
42
|
+
run: mypy dnsid
|
|
43
|
+
|
|
44
|
+
- name: Run tests
|
|
45
|
+
run: pytest
|
|
46
|
+
|
|
47
|
+
# Advisory only: the regen-docs workflow pushes regenerated reference
|
|
48
|
+
# docs to same-repo PR branches automatically. The warning matters for
|
|
49
|
+
# fork PRs, where the bot cannot push.
|
|
50
|
+
- name: Docs freshness (generated reference)
|
|
51
|
+
if: matrix.python-version == '3.13'
|
|
52
|
+
run: |
|
|
53
|
+
python scripts/gen_docs.py
|
|
54
|
+
git diff --exit-code docs/reference/ || {
|
|
55
|
+
echo "::warning::docs/reference/ is stale — the auto-regen workflow pushes the fix to same-repo PRs; fork authors run python scripts/gen_docs.py and commit the result"
|
|
56
|
+
exit 0
|
|
57
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Auto-regenerate reference docs
|
|
2
|
+
|
|
3
|
+
# On every same-repo PR push, regenerate docs/reference/ and, when the output
|
|
4
|
+
# changed, push the fix to the PR branch — contributors never run the docs
|
|
5
|
+
# generator by hand. Fork PRs are skipped (GITHUB_TOKEN cannot push to forks);
|
|
6
|
+
# the freshness warning in lint-test.yml still tells fork authors the manual
|
|
7
|
+
# command.
|
|
8
|
+
#
|
|
9
|
+
# Loop guard: the job skips when the actor is github-actions[bot] (GraphQL-created
|
|
10
|
+
# commits do re-trigger pull_request), and a re-run would find no diff anyway. The same property means CI results shown on the PR
|
|
11
|
+
# belong to the contributor's last commit; the bot commit only touches
|
|
12
|
+
# docs/reference/, which no test job consumes.
|
|
13
|
+
on:
|
|
14
|
+
pull_request:
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: regen-docs-${{ github.event.pull_request.number }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
regen:
|
|
25
|
+
# actor guard: createCommitOnBranch commits DO re-trigger pull_request, unlike GITHUB_TOKEN git pushes
|
|
26
|
+
if: github.event.pull_request.head.repo.full_name == github.repository && github.actor != 'github-actions[bot]'
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
30
|
+
with:
|
|
31
|
+
ref: ${{ github.event.pull_request.head.ref }}
|
|
32
|
+
|
|
33
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.13"
|
|
36
|
+
cache: pip
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: pip install -e ".[dev]"
|
|
40
|
+
|
|
41
|
+
- name: Regenerate reference
|
|
42
|
+
run: python scripts/gen_docs.py
|
|
43
|
+
|
|
44
|
+
# Commit through the GraphQL API instead of `git commit`: commits created
|
|
45
|
+
# by createCommitOnBranch are signed by GitHub's web-flow key, so the bot
|
|
46
|
+
# commit is Verified and satisfies signed-commit branch protection. A
|
|
47
|
+
# GITHUB_TOKEN commit still does not re-trigger this workflow.
|
|
48
|
+
- name: Commit and push if changed
|
|
49
|
+
env:
|
|
50
|
+
GH_TOKEN: ${{ github.token }}
|
|
51
|
+
BRANCH: ${{ github.event.pull_request.head.ref }}
|
|
52
|
+
run: |
|
|
53
|
+
git add -A docs/reference/
|
|
54
|
+
if git diff --cached --quiet; then
|
|
55
|
+
echo "reference is current"
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
git diff --cached --name-only --diff-filter=AM -z \
|
|
59
|
+
| while IFS= read -r -d '' f; do base64 -w0 < "$f" > /tmp/b64; jq -n --arg p "$f" --rawfile c /tmp/b64 '{path: $p, contents: $c}'; done \
|
|
60
|
+
| jq -s . > /tmp/additions.json
|
|
61
|
+
git diff --cached --name-only --diff-filter=D -z \
|
|
62
|
+
| jq -Rs 'split("\u0000") | map(select(length > 0) | {path: .})' > /tmp/deletions.json
|
|
63
|
+
jq -n --arg repo "$GITHUB_REPOSITORY" --arg branch "$BRANCH" --arg head "$(git rev-parse HEAD)" \
|
|
64
|
+
--slurpfile additions /tmp/additions.json --slurpfile deletions /tmp/deletions.json '{
|
|
65
|
+
query: "mutation($input: CreateCommitOnBranchInput!) { createCommitOnBranch(input: $input) { commit { oid } } }",
|
|
66
|
+
variables: { input: {
|
|
67
|
+
branch: { repositoryNameWithOwner: $repo, branchName: $branch },
|
|
68
|
+
expectedHeadOid: $head,
|
|
69
|
+
message: { headline: "docs: regenerate reference" },
|
|
70
|
+
fileChanges: { additions: $additions[0], deletions: $deletions[0] }
|
|
71
|
+
} }
|
|
72
|
+
}' > /tmp/commit.json
|
|
73
|
+
gh api graphql --input /tmp/commit.json --jq '.data.createCommitOnBranch.commit.oid'
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
name: Update Release PR
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
pull-requests: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
update-release-pr:
|
|
13
|
+
if: "!startsWith(github.event.head_commit.message, 'chore: update changelog and version') && !startsWith(github.event.head_commit.message, 'Release v')"
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
|
|
20
|
+
- name: Install git-cliff
|
|
21
|
+
uses: taiki-e/install-action@861a07ce7084f55488e375df125cdc99bba60eb7 # v2
|
|
22
|
+
with:
|
|
23
|
+
tool: git-cliff
|
|
24
|
+
|
|
25
|
+
- name: Setup Python
|
|
26
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.11"
|
|
29
|
+
|
|
30
|
+
- name: Compute next version and changelog
|
|
31
|
+
id: prep
|
|
32
|
+
run: |
|
|
33
|
+
NEXT=$(git cliff --bumped-version | head -1)
|
|
34
|
+
[ -n "$NEXT" ] || { echo "ERROR: git-cliff --bumped-version returned an empty version"; exit 1; }
|
|
35
|
+
|
|
36
|
+
CURRENT="v$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")"
|
|
37
|
+
if [ "$NEXT" = "$CURRENT" ] && git rev-parse "refs/tags/${CURRENT}" >/dev/null 2>&1; then
|
|
38
|
+
echo "No releasable commits found (computed: ${NEXT}, current: ${CURRENT}). Skipping."
|
|
39
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
40
|
+
else
|
|
41
|
+
echo "skip=false" >> "$GITHUB_OUTPUT"
|
|
42
|
+
echo "version=$NEXT" >> "$GITHUB_OUTPUT"
|
|
43
|
+
if [ "$NEXT" = "$CURRENT" ]; then
|
|
44
|
+
python - "$CURRENT" <<'PY'
|
|
45
|
+
import sys
|
|
46
|
+
from pathlib import Path
|
|
47
|
+
|
|
48
|
+
path = Path("CHANGELOG.md")
|
|
49
|
+
changelog = path.read_text()
|
|
50
|
+
marker = f"## [{sys.argv[1].removeprefix('v')}]"
|
|
51
|
+
start = changelog.find(marker)
|
|
52
|
+
if start != -1:
|
|
53
|
+
next_start = changelog.find("\n## [", start + 1)
|
|
54
|
+
path.write_text(changelog[:start] + changelog[next_start + 1 if next_start != -1 else len(changelog):])
|
|
55
|
+
PY
|
|
56
|
+
fi
|
|
57
|
+
git cliff --unreleased --tag "$NEXT" --prepend CHANGELOG.md
|
|
58
|
+
python - "$NEXT" <<'PY' > /tmp/release-notes.md
|
|
59
|
+
import sys
|
|
60
|
+
from pathlib import Path
|
|
61
|
+
|
|
62
|
+
version = sys.argv[1].removeprefix("v")
|
|
63
|
+
changelog = Path("CHANGELOG.md").read_text()
|
|
64
|
+
marker = f"## [{version}]"
|
|
65
|
+
start = changelog.find(marker)
|
|
66
|
+
if start == -1:
|
|
67
|
+
raise SystemExit(f"ERROR: {marker} not found in CHANGELOG.md")
|
|
68
|
+
|
|
69
|
+
next_start = changelog.find("\n## [", start + 1)
|
|
70
|
+
section = changelog[start : next_start if next_start != -1 else len(changelog)]
|
|
71
|
+
print(section.strip())
|
|
72
|
+
PY
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
- name: Bump pyproject.toml version
|
|
76
|
+
if: steps.prep.outputs.skip != 'true'
|
|
77
|
+
run: |
|
|
78
|
+
VERSION="${{ steps.prep.outputs.version }}"
|
|
79
|
+
VERSION_BARE="${VERSION#v}"
|
|
80
|
+
sed -i "s/^version = \".*\"/version = \"${VERSION_BARE}\"/" pyproject.toml
|
|
81
|
+
|
|
82
|
+
- name: Open or update release PR
|
|
83
|
+
if: steps.prep.outputs.skip != 'true'
|
|
84
|
+
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8
|
|
85
|
+
with:
|
|
86
|
+
branch: release/next
|
|
87
|
+
sign-commits: true
|
|
88
|
+
title: "Release ${{ steps.prep.outputs.version }}"
|
|
89
|
+
body-path: /tmp/release-notes.md
|
|
90
|
+
commit-message: "chore: update changelog and version"
|
|
91
|
+
labels: autorelease
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Triggered when the "release/next" PR is merged into main, or manually.
|
|
4
|
+
#
|
|
5
|
+
# The `publish-pypi` job uploads the same checked wheel + sdist to PyPI via
|
|
6
|
+
# trusted publishing (OIDC; no API token stored here). It is gated on the
|
|
7
|
+
# repository variable PYPI_PUBLISHING == "enabled" so releases keep working
|
|
8
|
+
# while the PyPI project name / trusted publisher are pending. To enable:
|
|
9
|
+
# 1. PyPI project `dnsid` → Publishing → add GitHub publisher
|
|
10
|
+
# (owner dnsid-ai, repo dnsid-py, workflow release.yml, environment pypi)
|
|
11
|
+
# 2. Repo Settings → Environments → create `pypi` (add required reviewers if desired)
|
|
12
|
+
# 3. Repo Settings → Variables → PYPI_PUBLISHING = enabled
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
pull_request:
|
|
16
|
+
types: [closed]
|
|
17
|
+
branches: [main]
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
attestations: write
|
|
22
|
+
contents: write
|
|
23
|
+
id-token: write
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
release:
|
|
27
|
+
if: >-
|
|
28
|
+
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') ||
|
|
29
|
+
(github.event.pull_request.merged == true &&
|
|
30
|
+
github.event.pull_request.head.ref == 'release/next' &&
|
|
31
|
+
github.event.pull_request.head.repo.full_name == github.repository)
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
35
|
+
with:
|
|
36
|
+
fetch-depth: 0
|
|
37
|
+
|
|
38
|
+
- name: Setup Python
|
|
39
|
+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
40
|
+
with:
|
|
41
|
+
python-version: "3.11"
|
|
42
|
+
|
|
43
|
+
- name: Read version
|
|
44
|
+
id: version
|
|
45
|
+
run: |
|
|
46
|
+
VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
47
|
+
[ -n "$VERSION" ] || { echo "ERROR: could not read version from pyproject.toml"; exit 1; }
|
|
48
|
+
echo "version=v${VERSION}" >> "$GITHUB_OUTPUT"
|
|
49
|
+
|
|
50
|
+
- name: Extract release notes
|
|
51
|
+
run: |
|
|
52
|
+
python - "${{ steps.version.outputs.version }}" <<'PY' > /tmp/release-notes.md
|
|
53
|
+
import sys
|
|
54
|
+
from pathlib import Path
|
|
55
|
+
|
|
56
|
+
version = sys.argv[1].removeprefix("v")
|
|
57
|
+
changelog = Path("CHANGELOG.md").read_text()
|
|
58
|
+
marker = f"## [{version}]"
|
|
59
|
+
start = changelog.find(marker)
|
|
60
|
+
if start == -1:
|
|
61
|
+
raise SystemExit(f"ERROR: {marker} not found in CHANGELOG.md")
|
|
62
|
+
|
|
63
|
+
next_start = changelog.find("\n## [", start + 1)
|
|
64
|
+
section = changelog[start : next_start if next_start != -1 else len(changelog)]
|
|
65
|
+
print(section.strip())
|
|
66
|
+
PY
|
|
67
|
+
echo "--- release notes ---"
|
|
68
|
+
cat /tmp/release-notes.md
|
|
69
|
+
|
|
70
|
+
- name: Verify release tag
|
|
71
|
+
id: tag
|
|
72
|
+
run: |
|
|
73
|
+
TAG="${{ steps.version.outputs.version }}"
|
|
74
|
+
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
|
|
75
|
+
[ "$(git rev-list -n 1 "${TAG}")" = "$GITHUB_SHA" ] || {
|
|
76
|
+
echo "ERROR: ${TAG} does not point at release commit ${GITHUB_SHA}"
|
|
77
|
+
exit 1
|
|
78
|
+
}
|
|
79
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
80
|
+
else
|
|
81
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
- name: Install build tools
|
|
85
|
+
run: python -m pip install build twine
|
|
86
|
+
|
|
87
|
+
- name: Build and check package
|
|
88
|
+
run: |
|
|
89
|
+
python -m build
|
|
90
|
+
python -m twine check dist/*
|
|
91
|
+
|
|
92
|
+
- name: Smoke test wheel
|
|
93
|
+
run: |
|
|
94
|
+
python -m venv /tmp/dnsid-smoke
|
|
95
|
+
/tmp/dnsid-smoke/bin/pip install dist/*.whl
|
|
96
|
+
(cd /tmp && /tmp/dnsid-smoke/bin/python -c "import dnsid")
|
|
97
|
+
|
|
98
|
+
- name: Upload distribution artifacts
|
|
99
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
100
|
+
with:
|
|
101
|
+
name: dist
|
|
102
|
+
path: dist/
|
|
103
|
+
if-no-files-found: error
|
|
104
|
+
|
|
105
|
+
- name: Attest package provenance
|
|
106
|
+
if: github.event.repository.private == false
|
|
107
|
+
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
|
|
108
|
+
with:
|
|
109
|
+
subject-path: dist/*
|
|
110
|
+
|
|
111
|
+
- name: Tag release
|
|
112
|
+
if: steps.tag.outputs.exists != 'true'
|
|
113
|
+
run: |
|
|
114
|
+
git config user.name "github-actions[bot]"
|
|
115
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
116
|
+
git tag -a "${{ steps.version.outputs.version }}" -m "Release ${{ steps.version.outputs.version }}"
|
|
117
|
+
git push origin "${{ steps.version.outputs.version }}"
|
|
118
|
+
|
|
119
|
+
- name: Generate SBOM (CycloneDX)
|
|
120
|
+
uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 # v0.24.2
|
|
121
|
+
with:
|
|
122
|
+
path: .
|
|
123
|
+
format: cyclonedx-json
|
|
124
|
+
output-file: sbom.cdx.json
|
|
125
|
+
upload-artifact: false
|
|
126
|
+
|
|
127
|
+
- name: Create GitHub Release
|
|
128
|
+
run: |
|
|
129
|
+
if gh release view "${{ steps.version.outputs.version }}" >/dev/null 2>&1; then
|
|
130
|
+
echo "Release ${{ steps.version.outputs.version }} already exists, skipping"
|
|
131
|
+
else
|
|
132
|
+
gh release create "${{ steps.version.outputs.version }}" \
|
|
133
|
+
dist/* sbom.cdx.json \
|
|
134
|
+
--verify-tag \
|
|
135
|
+
--title "Release ${{ steps.version.outputs.version }}" \
|
|
136
|
+
--notes-file /tmp/release-notes.md
|
|
137
|
+
fi
|
|
138
|
+
env:
|
|
139
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
140
|
+
|
|
141
|
+
publish-pypi:
|
|
142
|
+
needs: release
|
|
143
|
+
if: vars.PYPI_PUBLISHING == 'enabled'
|
|
144
|
+
runs-on: ubuntu-latest
|
|
145
|
+
environment:
|
|
146
|
+
name: pypi
|
|
147
|
+
url: https://pypi.org/project/dnsid/
|
|
148
|
+
permissions:
|
|
149
|
+
id-token: write
|
|
150
|
+
steps:
|
|
151
|
+
- name: Download distribution artifacts
|
|
152
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
153
|
+
with:
|
|
154
|
+
name: dist
|
|
155
|
+
path: dist/
|
|
156
|
+
|
|
157
|
+
- name: Publish to PyPI (trusted publishing)
|
|
158
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
159
|
+
with:
|
|
160
|
+
attestations: true
|
dnsid-0.26.0/.gitignore
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
*.pyd
|
|
6
|
+
*.so
|
|
7
|
+
*.egg
|
|
8
|
+
*.egg-info/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
wheels/
|
|
12
|
+
sdist/
|
|
13
|
+
*.egg-link
|
|
14
|
+
pip-wheel-metadata/
|
|
15
|
+
MANIFEST
|
|
16
|
+
|
|
17
|
+
# Virtual environments
|
|
18
|
+
.venv/
|
|
19
|
+
venv/
|
|
20
|
+
env/
|
|
21
|
+
ENV/
|
|
22
|
+
|
|
23
|
+
# Testing / coverage
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
.coverage.*
|
|
27
|
+
htmlcov/
|
|
28
|
+
coverage.xml
|
|
29
|
+
*.xml
|
|
30
|
+
|
|
31
|
+
# Type checking
|
|
32
|
+
.mypy_cache/
|
|
33
|
+
.dmypy.json
|
|
34
|
+
.pytype/
|
|
35
|
+
|
|
36
|
+
# Ruff / linters
|
|
37
|
+
.ruff_cache/
|
|
38
|
+
|
|
39
|
+
# Distribution / packaging
|
|
40
|
+
*.tar.gz
|
|
41
|
+
*.whl
|
|
42
|
+
|
|
43
|
+
# Testnet runtime files (generated keys, certs)
|
|
44
|
+
.testnet/
|
|
45
|
+
keys.json
|
|
46
|
+
|
|
47
|
+
# IDE
|
|
48
|
+
.vscode/
|
|
49
|
+
.idea/
|
|
50
|
+
*.swp
|
|
51
|
+
*.swo
|
|
52
|
+
.DS_Store
|
|
53
|
+
|
|
54
|
+
# Local scratch (never committed)
|
|
55
|
+
.claude/
|
|
56
|
+
/TASK*.md
|
|
57
|
+
/*_ISSUES.md
|