Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history + tags for setuptools-scm
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
Expand All @@ -38,6 +40,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history + tags for setuptools-scm
- name: Set up Python
uses: actions/setup-python@v5
with:
Expand Down
95 changes: 95 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Release

# Publishes the package to PyPI via Trusted Publishing (OIDC) — no API tokens
# are stored in GitHub.
#
# Triggers:
# * Pushing a version tag (v*) -> builds and uploads to PyPI.
# * Publishing a GitHub Release -> builds and uploads to PyPI.
# * Manual "Run workflow" -> builds and uploads to TestPyPI (staging).
#
# To cut a release: `git tag v1.2.3 && git push origin v1.2.3`
# (setuptools-scm stamps the build with the tag, so the version is 1.2.3).
#
# One-time setup on the package index (no secrets needed afterwards):
# PyPI: https://pypi.org/manage/account/publishing/
# TestPyPI: https://test.pypi.org/manage/account/publishing/
# Add a "pending publisher" for:
# - PyPI project name: domain-security-analyzer
# - Owner: CallMarcus
# - Repository: domain-security-analyzer
# - Workflow name: release.yml
# - Environment: pypi (for PyPI) / testpypi (for TestPyPI)

on:
push:
tags:
- 'v*'
release:
types: [published]
workflow_dispatch:

jobs:
build:
name: Build distributions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history + tags for setuptools-scm
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Build and validate
run: |
python -m pip install --upgrade pip build twine
python -m build
twine check dist/*
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish-testpypi:
name: Publish to TestPyPI
# Manual runs go to TestPyPI for staging/verification.
if: github.event_name == 'workflow_dispatch'
needs: build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/project/domain-security-analyzer/
permissions:
id-token: write # required for Trusted Publishing (OIDC)
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

publish-pypi:
name: Publish to PyPI
# A pushed version tag or a published GitHub Release uploads to production PyPI.
if: github.event_name == 'push' || github.event_name == 'release'
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/domain-security-analyzer/
permissions:
id-token: write # required for Trusted Publishing (OIDC)
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ build/
dist/
*.egg-info/
*.egg

# Version file generated by setuptools-scm
domain_security_analyzer/_version.py
18 changes: 17 additions & 1 deletion domain_security_analyzer/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
__version__ = "1.0.0"
"""Resolve the installed package version.

The version is owned by git tags via setuptools-scm — there is no hardcoded
version string to keep in sync. At runtime we read it from the installed
package metadata, falling back to the file setuptools-scm writes at build time
(``_version.py``) when running from an unbuilt source tree.
"""

from importlib.metadata import PackageNotFoundError, version as _pkg_version

try:
__version__ = _pkg_version("domain-security-analyzer")
except PackageNotFoundError: # pragma: no cover - source tree without install
try:
from ._version import version as __version__
except Exception:
__version__ = "0.0.0+unknown"
13 changes: 8 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
requires = ["setuptools>=64", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "domain-security-analyzer"
dynamic = ["version"]
description = "Analyze domain security configurations: DNS, email authentication (SPF/DKIM/DMARC), subdomain discovery, and Subresource Integrity (SRI) scanning."
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
license = { text = "MIT" }
authors = [{ name = "CallMarcus" }]
keywords = [
Expand All @@ -31,11 +31,11 @@ classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"dnspython>=2.4.0",
Expand Down Expand Up @@ -65,5 +65,8 @@ py-modules = ["domain_analyzer"]
[tool.setuptools.packages.find]
include = ["domain_security_analyzer*"]

[tool.setuptools.dynamic]
version = { attr = "domain_security_analyzer.__version__.__version__" }
# Version is derived from git tags (single source of truth). Tag `v1.2.3`
# produces version `1.2.3`; commits after a tag get a dev/local suffix.
[tool.setuptools_scm]
version_file = "domain_security_analyzer/_version.py"
fallback_version = "0.0.0"
Loading