fastly-sync 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.
- fastly_sync-0.1.0/.github/FUNDING.yml +4 -0
- fastly_sync-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +77 -0
- fastly_sync-0.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
- fastly_sync-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
- fastly_sync-0.1.0/.github/dependabot.yml +68 -0
- fastly_sync-0.1.0/.github/pull_request_template.md +37 -0
- fastly_sync-0.1.0/.github/workflows/ci.yml +308 -0
- fastly_sync-0.1.0/.github/workflows/dependabot-rewrite.yml +94 -0
- fastly_sync-0.1.0/.gitignore +25 -0
- fastly_sync-0.1.0/.multicz/state.json +13 -0
- fastly_sync-0.1.0/.pre-commit-config.yaml +90 -0
- fastly_sync-0.1.0/.python-version +1 -0
- fastly_sync-0.1.0/CHANGELOG.md +31 -0
- fastly_sync-0.1.0/CODE_OF_CONDUCT.md +83 -0
- fastly_sync-0.1.0/CONTRIBUTING.md +62 -0
- fastly_sync-0.1.0/LICENSE +21 -0
- fastly_sync-0.1.0/PKG-INFO +240 -0
- fastly_sync-0.1.0/README.md +213 -0
- fastly_sync-0.1.0/SECURITY.md +47 -0
- fastly_sync-0.1.0/docs/index.md +47 -0
- fastly_sync-0.1.0/docs/stability.md +33 -0
- fastly_sync-0.1.0/multicz.toml +61 -0
- fastly_sync-0.1.0/osv-scanner.toml +14 -0
- fastly_sync-0.1.0/pyproject.toml +88 -0
- fastly_sync-0.1.0/scripts/add_license_header.py +138 -0
- fastly_sync-0.1.0/scripts/rewrite-dependabot-commit.sh +67 -0
- fastly_sync-0.1.0/src/fastly_sync/__init__.py +6 -0
- fastly_sync-0.1.0/src/fastly_sync/blocklist.py +65 -0
- fastly_sync-0.1.0/src/fastly_sync/cli.py +457 -0
- fastly_sync-0.1.0/src/fastly_sync/config.py +66 -0
- fastly_sync-0.1.0/src/fastly_sync/errors.py +30 -0
- fastly_sync-0.1.0/src/fastly_sync/fastly.py +303 -0
- fastly_sync-0.1.0/src/fastly_sync/loader.py +57 -0
- fastly_sync-0.1.0/src/fastly_sync/models.py +86 -0
- fastly_sync-0.1.0/src/fastly_sync/show.py +63 -0
- fastly_sync-0.1.0/src/fastly_sync/spec.py +172 -0
- fastly_sync-0.1.0/src/fastly_sync/sync.py +167 -0
- fastly_sync-0.1.0/src/fastly_sync/waf.py +114 -0
- fastly_sync-0.1.0/tests/__init__.py +2 -0
- fastly_sync-0.1.0/tests/test_blocklist.py +75 -0
- fastly_sync-0.1.0/tests/test_cli.py +464 -0
- fastly_sync-0.1.0/tests/test_config.py +41 -0
- fastly_sync-0.1.0/tests/test_fastly.py +242 -0
- fastly_sync-0.1.0/tests/test_show.py +81 -0
- fastly_sync-0.1.0/tests/test_spec.py +207 -0
- fastly_sync-0.1.0/tests/test_sync.py +222 -0
- fastly_sync-0.1.0/tests/test_waf.py +112 -0
- fastly_sync-0.1.0/uv.lock +992 -0
- fastly_sync-0.1.0/zensical.toml +19 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
name: Bug Report
|
|
5
|
+
description: Report a bug or unexpected behavior
|
|
6
|
+
labels: ["bug"]
|
|
7
|
+
body:
|
|
8
|
+
- type: markdown
|
|
9
|
+
attributes:
|
|
10
|
+
value: |
|
|
11
|
+
Thanks for reporting a bug! Please fill in the details below.
|
|
12
|
+
|
|
13
|
+
- type: input
|
|
14
|
+
id: version
|
|
15
|
+
attributes:
|
|
16
|
+
label: fastly-sync version
|
|
17
|
+
description: "Output of `pip show fastly-sync` or `fastly-sync --version`."
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: description
|
|
23
|
+
attributes:
|
|
24
|
+
label: Description
|
|
25
|
+
description: A clear description of the bug.
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: steps
|
|
31
|
+
attributes:
|
|
32
|
+
label: Steps to Reproduce
|
|
33
|
+
description: How can we reproduce this issue? Include the exact command line.
|
|
34
|
+
validations:
|
|
35
|
+
required: true
|
|
36
|
+
|
|
37
|
+
- type: textarea
|
|
38
|
+
id: expected
|
|
39
|
+
attributes:
|
|
40
|
+
label: Expected Behavior
|
|
41
|
+
validations:
|
|
42
|
+
required: true
|
|
43
|
+
|
|
44
|
+
- type: textarea
|
|
45
|
+
id: actual
|
|
46
|
+
attributes:
|
|
47
|
+
label: Actual Behavior
|
|
48
|
+
validations:
|
|
49
|
+
required: true
|
|
50
|
+
|
|
51
|
+
- type: dropdown
|
|
52
|
+
id: python-version
|
|
53
|
+
attributes:
|
|
54
|
+
label: Python Version
|
|
55
|
+
options:
|
|
56
|
+
- "3.11"
|
|
57
|
+
- "3.12"
|
|
58
|
+
- "3.13"
|
|
59
|
+
validations:
|
|
60
|
+
required: true
|
|
61
|
+
|
|
62
|
+
- type: dropdown
|
|
63
|
+
id: os
|
|
64
|
+
attributes:
|
|
65
|
+
label: Operating System
|
|
66
|
+
options:
|
|
67
|
+
- Linux
|
|
68
|
+
- macOS
|
|
69
|
+
- Windows
|
|
70
|
+
validations:
|
|
71
|
+
required: true
|
|
72
|
+
|
|
73
|
+
- type: textarea
|
|
74
|
+
id: logs
|
|
75
|
+
attributes:
|
|
76
|
+
label: Logs / Traceback
|
|
77
|
+
render: shell
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
blank_issues_enabled: false
|
|
5
|
+
contact_links:
|
|
6
|
+
- name: Code of Conduct
|
|
7
|
+
url: https://github.com/goabonga/fastly-sync/blob/main/CODE_OF_CONDUCT.md
|
|
8
|
+
about: Please read our Code of Conduct before contributing
|
|
9
|
+
- name: Security advisories
|
|
10
|
+
url: https://github.com/goabonga/fastly-sync/security/advisories/new
|
|
11
|
+
about: Report a vulnerability privately (do not open a public issue)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
name: Feature Request
|
|
5
|
+
description: Suggest a new feature or enhancement
|
|
6
|
+
labels: ["enhancement"]
|
|
7
|
+
body:
|
|
8
|
+
- type: markdown
|
|
9
|
+
attributes:
|
|
10
|
+
value: |
|
|
11
|
+
Thanks for suggesting a feature! Please describe your idea below.
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: problem
|
|
15
|
+
attributes:
|
|
16
|
+
label: Problem
|
|
17
|
+
description: What problem does this feature solve? Is it related to a frustration?
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: solution
|
|
23
|
+
attributes:
|
|
24
|
+
label: Proposed Solution
|
|
25
|
+
description: Describe the solution you'd like.
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: alternatives
|
|
31
|
+
attributes:
|
|
32
|
+
label: Alternatives Considered
|
|
33
|
+
|
|
34
|
+
- type: textarea
|
|
35
|
+
id: context
|
|
36
|
+
attributes:
|
|
37
|
+
label: Additional Context
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
# Dependabot v2 configuration.
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
|
|
8
|
+
updates:
|
|
9
|
+
# Python dependencies via uv.
|
|
10
|
+
- package-ecosystem: "uv"
|
|
11
|
+
directory: "/"
|
|
12
|
+
schedule:
|
|
13
|
+
interval: "weekly"
|
|
14
|
+
day: "monday"
|
|
15
|
+
time: "06:00"
|
|
16
|
+
timezone: "Europe/Paris"
|
|
17
|
+
open-pull-requests-limit: 5
|
|
18
|
+
ignore:
|
|
19
|
+
# `hatchling` lives in [build-system].requires; Dependabot's uv
|
|
20
|
+
# FileUpdater cannot write that table. Bump it by hand; pip-audit and
|
|
21
|
+
# osv-scanner still flag any CVEs.
|
|
22
|
+
- dependency-name: "hatchling"
|
|
23
|
+
groups:
|
|
24
|
+
# Bundle routine dev tooling bumps into a single weekly PR. The
|
|
25
|
+
# signed-rewrite script keys "dev-tools group" -> chore(deps).
|
|
26
|
+
dev-tools:
|
|
27
|
+
applies-to: version-updates
|
|
28
|
+
patterns:
|
|
29
|
+
- "ruff"
|
|
30
|
+
- "mypy"
|
|
31
|
+
- "pytest*"
|
|
32
|
+
- "pre-commit"
|
|
33
|
+
# Runtime deps ship in the package -> fixes; the signed rewrite confirms
|
|
34
|
+
# this from [dependency-groups].dev membership. Dev tooling stays chore.
|
|
35
|
+
commit-message:
|
|
36
|
+
prefix: "fix(deps)"
|
|
37
|
+
prefix-development: "chore(deps)"
|
|
38
|
+
labels:
|
|
39
|
+
- "dependencies"
|
|
40
|
+
- "python"
|
|
41
|
+
|
|
42
|
+
# Pinned versions of GitHub Actions in `.github/workflows/*.yml`.
|
|
43
|
+
- package-ecosystem: "github-actions"
|
|
44
|
+
directory: "/"
|
|
45
|
+
schedule:
|
|
46
|
+
interval: "weekly"
|
|
47
|
+
day: "monday"
|
|
48
|
+
time: "06:00"
|
|
49
|
+
timezone: "Europe/Paris"
|
|
50
|
+
open-pull-requests-limit: 5
|
|
51
|
+
groups:
|
|
52
|
+
ci-actions:
|
|
53
|
+
applies-to: version-updates
|
|
54
|
+
patterns:
|
|
55
|
+
- "actions/*"
|
|
56
|
+
- "astral-sh/*"
|
|
57
|
+
- "google/*"
|
|
58
|
+
- "pypa/*"
|
|
59
|
+
- "codecov/*"
|
|
60
|
+
- "compilerla/*"
|
|
61
|
+
- "pre-commit/*"
|
|
62
|
+
- "docker/*"
|
|
63
|
+
- "softprops/*"
|
|
64
|
+
commit-message:
|
|
65
|
+
prefix: "ci"
|
|
66
|
+
labels:
|
|
67
|
+
- "dependencies"
|
|
68
|
+
- "github-actions"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!-- Describe what this PR does and why. -->
|
|
4
|
+
|
|
5
|
+
## Type
|
|
6
|
+
|
|
7
|
+
<!-- Check the one that applies: -->
|
|
8
|
+
|
|
9
|
+
- [ ] `feat` - New feature
|
|
10
|
+
- [ ] `fix` - Bug fix
|
|
11
|
+
- [ ] `docs` - Documentation
|
|
12
|
+
- [ ] `refactor` - Code refactoring
|
|
13
|
+
- [ ] `test` - Adding or updating tests
|
|
14
|
+
- [ ] `chore` - Maintenance
|
|
15
|
+
- [ ] `ci` - CI / release pipeline
|
|
16
|
+
|
|
17
|
+
## Changes
|
|
18
|
+
|
|
19
|
+
<!-- List the main changes introduced by this PR: -->
|
|
20
|
+
|
|
21
|
+
-
|
|
22
|
+
|
|
23
|
+
## Related Issues
|
|
24
|
+
|
|
25
|
+
<!-- Link related issues: Closes #123, Fixes #456 -->
|
|
26
|
+
|
|
27
|
+
## Checklist
|
|
28
|
+
|
|
29
|
+
- [ ] Commits follow [Conventional Commits](https://www.conventionalcommits.org/)
|
|
30
|
+
- [ ] Branch is up to date with `main`
|
|
31
|
+
- [ ] `uv sync` succeeds
|
|
32
|
+
- [ ] `uv run ruff check src tests` and `uv run ruff format --check src tests` are clean
|
|
33
|
+
- [ ] `uv run mypy src` is clean (if the project uses mypy)
|
|
34
|
+
- [ ] `uv run pytest` passes (if the project has tests)
|
|
35
|
+
- [ ] `uv tool run multicz validate --strict` passes
|
|
36
|
+
- [ ] SPDX license headers are present (`python scripts/add_license_header.py --path . --types py,toml --check`)
|
|
37
|
+
- [ ] No `Co-Authored-By` trailer in commit messages
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
# Full CI for a PyPI-distributed project. Drop the `type-check` job if the
|
|
5
|
+
# code is not typed, and the `test` job (+ codecov) if there is no suite.
|
|
6
|
+
# Rename to ci.yml in the repo.
|
|
7
|
+
|
|
8
|
+
name: ci
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
pull_request:
|
|
12
|
+
branches: [main]
|
|
13
|
+
push:
|
|
14
|
+
branches: [main]
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
18
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
lint:
|
|
25
|
+
name: lint
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v6
|
|
29
|
+
- uses: astral-sh/setup-uv@v7
|
|
30
|
+
with:
|
|
31
|
+
enable-cache: true
|
|
32
|
+
cache-suffix: ${{ github.job }}
|
|
33
|
+
python-version: "3.12"
|
|
34
|
+
- run: uv sync --frozen --only-group dev
|
|
35
|
+
- run: uv run --no-sync ruff check --output-format=github src tests
|
|
36
|
+
- run: uv run --no-sync ruff format --check src tests
|
|
37
|
+
|
|
38
|
+
# Optional: remove if the project does not use mypy.
|
|
39
|
+
type-check:
|
|
40
|
+
name: type-check
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v6
|
|
44
|
+
- uses: astral-sh/setup-uv@v7
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
cache-suffix: ${{ github.job }}
|
|
48
|
+
python-version: "3.12"
|
|
49
|
+
- run: uv sync --frozen
|
|
50
|
+
- run: uv run --no-sync mypy src
|
|
51
|
+
|
|
52
|
+
license-headers:
|
|
53
|
+
name: license-headers
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
steps:
|
|
56
|
+
- uses: actions/checkout@v6
|
|
57
|
+
- uses: astral-sh/setup-uv@v7
|
|
58
|
+
with:
|
|
59
|
+
enable-cache: true
|
|
60
|
+
cache-suffix: ${{ github.job }}
|
|
61
|
+
python-version: "3.12"
|
|
62
|
+
- name: Check SPDX headers
|
|
63
|
+
run: |
|
|
64
|
+
uv run --no-project python scripts/add_license_header.py --path src --types py --check
|
|
65
|
+
uv run --no-project python scripts/add_license_header.py --path tests --types py --check
|
|
66
|
+
uv run --no-project python scripts/add_license_header.py --path scripts --types py,sh --check
|
|
67
|
+
uv run --no-project python scripts/add_license_header.py --path .github --types yml --check
|
|
68
|
+
uv run --no-project python scripts/add_license_header.py --path . --types toml --check
|
|
69
|
+
|
|
70
|
+
# Optional: remove if there is no test suite.
|
|
71
|
+
test:
|
|
72
|
+
name: test
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v6
|
|
76
|
+
- uses: astral-sh/setup-uv@v7
|
|
77
|
+
with:
|
|
78
|
+
enable-cache: true
|
|
79
|
+
cache-suffix: ${{ github.job }}
|
|
80
|
+
python-version: "3.12"
|
|
81
|
+
- run: uv sync --frozen
|
|
82
|
+
- name: Run tests with coverage
|
|
83
|
+
run: |
|
|
84
|
+
uv run --no-sync pytest \
|
|
85
|
+
--cov=fastly_sync \
|
|
86
|
+
--cov-report=term-missing \
|
|
87
|
+
--cov-report=xml:coverage.xml \
|
|
88
|
+
--junitxml=junit.xml \
|
|
89
|
+
-o junit_family=legacy
|
|
90
|
+
- name: Upload coverage to Codecov
|
|
91
|
+
uses: codecov/codecov-action@v6
|
|
92
|
+
with:
|
|
93
|
+
files: coverage.xml
|
|
94
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
95
|
+
fail_ci_if_error: false
|
|
96
|
+
disable_search: true
|
|
97
|
+
- name: Upload test results to Codecov
|
|
98
|
+
if: ${{ !cancelled() }}
|
|
99
|
+
uses: codecov/codecov-action@v6
|
|
100
|
+
with:
|
|
101
|
+
files: junit.xml
|
|
102
|
+
report_type: test_results
|
|
103
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
104
|
+
fail_ci_if_error: false
|
|
105
|
+
disable_search: true
|
|
106
|
+
|
|
107
|
+
bandit:
|
|
108
|
+
name: bandit
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
steps:
|
|
111
|
+
- uses: actions/checkout@v6
|
|
112
|
+
- uses: astral-sh/setup-uv@v7
|
|
113
|
+
with:
|
|
114
|
+
enable-cache: true
|
|
115
|
+
cache-suffix: ${{ github.job }}
|
|
116
|
+
python-version: "3.12"
|
|
117
|
+
# Drop `--from 'bandit[toml]' ... -c pyproject.toml` to plain
|
|
118
|
+
# `uvx bandit --recursive src` if there is no [tool.bandit] skip list.
|
|
119
|
+
- run: uvx --from 'bandit[toml]' bandit -c pyproject.toml --recursive src
|
|
120
|
+
|
|
121
|
+
pip-audit:
|
|
122
|
+
name: pip-audit
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
steps:
|
|
125
|
+
- uses: actions/checkout@v6
|
|
126
|
+
- uses: astral-sh/setup-uv@v7
|
|
127
|
+
with:
|
|
128
|
+
enable-cache: true
|
|
129
|
+
cache-suffix: ${{ github.job }}
|
|
130
|
+
python-version: "3.12"
|
|
131
|
+
- run: uv sync --frozen
|
|
132
|
+
- run: uvx pip-audit --skip-editable --path .venv
|
|
133
|
+
|
|
134
|
+
osv-scanner:
|
|
135
|
+
name: osv-scanner
|
|
136
|
+
runs-on: ubuntu-latest
|
|
137
|
+
steps:
|
|
138
|
+
- uses: actions/checkout@v6
|
|
139
|
+
- name: Install OSV-Scanner
|
|
140
|
+
run: |
|
|
141
|
+
mkdir -p "$HOME/.local/bin"
|
|
142
|
+
curl -sSfL \
|
|
143
|
+
"https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_amd64" \
|
|
144
|
+
-o "$HOME/.local/bin/osv-scanner"
|
|
145
|
+
chmod +x "$HOME/.local/bin/osv-scanner"
|
|
146
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
147
|
+
- run: osv-scanner scan source --lockfile=uv.lock
|
|
148
|
+
|
|
149
|
+
release:
|
|
150
|
+
name: release
|
|
151
|
+
needs: [lint, type-check, license-headers, test, bandit, pip-audit, osv-scanner]
|
|
152
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
153
|
+
runs-on: ubuntu-latest
|
|
154
|
+
permissions:
|
|
155
|
+
contents: write
|
|
156
|
+
outputs:
|
|
157
|
+
no_bumps: ${{ steps.bump.outputs.no_bumps }}
|
|
158
|
+
version: ${{ steps.bump.outputs.version }}
|
|
159
|
+
steps:
|
|
160
|
+
- uses: actions/checkout@v6
|
|
161
|
+
with:
|
|
162
|
+
fetch-depth: 0
|
|
163
|
+
persist-credentials: true
|
|
164
|
+
# NOTE: no python-version pin here - multicz needs Python >=3.12 and
|
|
165
|
+
# `uv tool install multicz` fails under 3.11.
|
|
166
|
+
- uses: astral-sh/setup-uv@v7
|
|
167
|
+
with:
|
|
168
|
+
enable-cache: true
|
|
169
|
+
cache-suffix: ${{ github.job }}
|
|
170
|
+
- name: Install multicz
|
|
171
|
+
run: uv tool install multicz
|
|
172
|
+
- name: Configure git author
|
|
173
|
+
run: |
|
|
174
|
+
NAME="${{ github.event.head_commit.author.name }}"
|
|
175
|
+
EMAIL="${{ github.event.head_commit.author.email }}"
|
|
176
|
+
git config user.name "${NAME:-github-actions[bot]}"
|
|
177
|
+
git config user.email "${EMAIL:-41898282+github-actions[bot]@users.noreply.github.com}"
|
|
178
|
+
- name: Detect GPG availability
|
|
179
|
+
id: gpg
|
|
180
|
+
env:
|
|
181
|
+
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
182
|
+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
|
183
|
+
run: |
|
|
184
|
+
if [ -n "$GPG_PRIVATE_KEY" ] && [ -n "$GPG_PASSPHRASE" ]; then
|
|
185
|
+
echo "available=true" >> "$GITHUB_OUTPUT"
|
|
186
|
+
else
|
|
187
|
+
echo "available=false" >> "$GITHUB_OUTPUT"
|
|
188
|
+
fi
|
|
189
|
+
- name: Import and unlock GPG key
|
|
190
|
+
if: steps.gpg.outputs.available == 'true'
|
|
191
|
+
env:
|
|
192
|
+
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
193
|
+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
|
194
|
+
run: |
|
|
195
|
+
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
|
196
|
+
echo test | gpg --batch --yes --passphrase "$GPG_PASSPHRASE" \
|
|
197
|
+
--pinentry-mode loopback --sign > /dev/null
|
|
198
|
+
KEY_ID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/{print $5; exit}')
|
|
199
|
+
git config user.signingkey "$KEY_ID"
|
|
200
|
+
- name: Plan release
|
|
201
|
+
run: uv tool run multicz plan --summary "$GITHUB_STEP_SUMMARY"
|
|
202
|
+
- name: Bump, commit, tag, push (signed)
|
|
203
|
+
id: bump_signed
|
|
204
|
+
if: steps.gpg.outputs.available == 'true'
|
|
205
|
+
run: |
|
|
206
|
+
set -euo pipefail
|
|
207
|
+
PLAN=$(uv tool run multicz bump --commit --tag --push --sign --output json)
|
|
208
|
+
BUMPED=$(echo "$PLAN" | jq -r '.bumps | keys | join(" ")')
|
|
209
|
+
if [ -z "$BUMPED" ]; then echo "no_bumps=true" >> "$GITHUB_OUTPUT";
|
|
210
|
+
else echo "no_bumps=false" >> "$GITHUB_OUTPUT"; echo "version=$(uv tool run multicz get fastly-sync)" >> "$GITHUB_OUTPUT"; fi
|
|
211
|
+
- name: Bump, commit, tag, push
|
|
212
|
+
id: bump_unsigned
|
|
213
|
+
if: steps.gpg.outputs.available != 'true'
|
|
214
|
+
run: |
|
|
215
|
+
set -euo pipefail
|
|
216
|
+
PLAN=$(uv tool run multicz bump --commit --tag --push --output json)
|
|
217
|
+
BUMPED=$(echo "$PLAN" | jq -r '.bumps | keys | join(" ")')
|
|
218
|
+
if [ -z "$BUMPED" ]; then echo "no_bumps=true" >> "$GITHUB_OUTPUT";
|
|
219
|
+
else echo "no_bumps=false" >> "$GITHUB_OUTPUT"; echo "version=$(uv tool run multicz get fastly-sync)" >> "$GITHUB_OUTPUT"; fi
|
|
220
|
+
- name: Resolve release outputs
|
|
221
|
+
id: bump
|
|
222
|
+
run: |
|
|
223
|
+
if [ "${{ steps.gpg.outputs.available }}" = "true" ]; then
|
|
224
|
+
echo 'no_bumps=${{ steps.bump_signed.outputs.no_bumps }}' >> "$GITHUB_OUTPUT"
|
|
225
|
+
echo 'version=${{ steps.bump_signed.outputs.version }}' >> "$GITHUB_OUTPUT"
|
|
226
|
+
else
|
|
227
|
+
echo 'no_bumps=${{ steps.bump_unsigned.outputs.no_bumps }}' >> "$GITHUB_OUTPUT"
|
|
228
|
+
echo 'version=${{ steps.bump_unsigned.outputs.version }}' >> "$GITHUB_OUTPUT"
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
pypi-publish:
|
|
232
|
+
name: pypi-publish
|
|
233
|
+
needs: [release]
|
|
234
|
+
if: >-
|
|
235
|
+
github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
236
|
+
&& needs.release.result == 'success' && needs.release.outputs.no_bumps == 'false'
|
|
237
|
+
runs-on: ubuntu-latest
|
|
238
|
+
environment:
|
|
239
|
+
name: pypi
|
|
240
|
+
url: https://pypi.org/project/fastly-sync/
|
|
241
|
+
permissions:
|
|
242
|
+
contents: write
|
|
243
|
+
id-token: write # Trusted Publishing (OIDC) - do NOT add a token/password
|
|
244
|
+
steps:
|
|
245
|
+
- uses: actions/checkout@v6
|
|
246
|
+
with:
|
|
247
|
+
fetch-depth: 0
|
|
248
|
+
ref: main
|
|
249
|
+
- uses: astral-sh/setup-uv@v7
|
|
250
|
+
with:
|
|
251
|
+
enable-cache: true
|
|
252
|
+
cache-suffix: ${{ github.job }}
|
|
253
|
+
- name: Install multicz
|
|
254
|
+
run: uv tool install multicz
|
|
255
|
+
- name: Build wheel and sdist
|
|
256
|
+
run: uv build --out-dir dist && ls -l dist
|
|
257
|
+
- name: Publish to PyPI
|
|
258
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
259
|
+
with:
|
|
260
|
+
packages-dir: dist
|
|
261
|
+
skip-existing: true
|
|
262
|
+
- name: Create GitHub release
|
|
263
|
+
env:
|
|
264
|
+
GH_TOKEN: ${{ github.token }}
|
|
265
|
+
run: |
|
|
266
|
+
set -euo pipefail
|
|
267
|
+
version="${{ needs.release.outputs.version }}"
|
|
268
|
+
# Use the SAME tag multicz created: bare history -> tag="${version}".
|
|
269
|
+
tag="v${version}"
|
|
270
|
+
notes=$(uv tool run multicz release-notes --tag "$tag")
|
|
271
|
+
gh release create "$tag" --title "$tag" --notes "$notes" dist/*
|
|
272
|
+
|
|
273
|
+
docs:
|
|
274
|
+
name: docs
|
|
275
|
+
needs: [release]
|
|
276
|
+
if: >-
|
|
277
|
+
github.event_name == 'push'
|
|
278
|
+
&& github.ref == 'refs/heads/main'
|
|
279
|
+
&& needs.release.result == 'success'
|
|
280
|
+
&& needs.release.outputs.no_bumps == 'false'
|
|
281
|
+
runs-on: ubuntu-latest
|
|
282
|
+
environment:
|
|
283
|
+
name: github-pages
|
|
284
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
285
|
+
permissions:
|
|
286
|
+
contents: read
|
|
287
|
+
pages: write
|
|
288
|
+
id-token: write
|
|
289
|
+
steps:
|
|
290
|
+
- uses: actions/checkout@v6
|
|
291
|
+
with:
|
|
292
|
+
ref: main
|
|
293
|
+
- uses: actions/configure-pages@v6
|
|
294
|
+
with:
|
|
295
|
+
enablement: true
|
|
296
|
+
- uses: astral-sh/setup-uv@v7
|
|
297
|
+
with:
|
|
298
|
+
enable-cache: true
|
|
299
|
+
cache-suffix: ${{ github.job }}
|
|
300
|
+
- name: Install Zensical (doc group)
|
|
301
|
+
run: uv sync --frozen --only-group doc
|
|
302
|
+
- name: Build site
|
|
303
|
+
run: uv run zensical build --clean
|
|
304
|
+
- uses: actions/upload-pages-artifact@v5
|
|
305
|
+
with:
|
|
306
|
+
path: site
|
|
307
|
+
- id: deployment
|
|
308
|
+
uses: actions/deploy-pages@v5
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2026 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
name: dependabot-rewrite
|
|
5
|
+
|
|
6
|
+
# `pull_request_target` so the workflow can read GPG_PRIVATE_KEY /
|
|
7
|
+
# GPG_PASSPHRASE (regular `pull_request` runs without secrets on bot events).
|
|
8
|
+
# The workflow body is loaded from `main`, not the PR branch, so no untrusted
|
|
9
|
+
# code runs. The actor gate restricts it to genuine Dependabot PRs.
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
pull_request_target:
|
|
13
|
+
types: [opened, synchronize, reopened]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: write
|
|
17
|
+
pull-requests: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
rewrite:
|
|
21
|
+
name: rewrite dependabot commits
|
|
22
|
+
if: github.actor == 'dependabot[bot]'
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
concurrency:
|
|
25
|
+
group: dependabot-rewrite-${{ github.event.pull_request.number }}
|
|
26
|
+
cancel-in-progress: true
|
|
27
|
+
steps:
|
|
28
|
+
- name: Check GPG availability
|
|
29
|
+
id: gate
|
|
30
|
+
env:
|
|
31
|
+
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
32
|
+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
|
33
|
+
run: |
|
|
34
|
+
if [ -z "$GPG_PRIVATE_KEY" ] || [ -z "$GPG_PASSPHRASE" ]; then
|
|
35
|
+
{
|
|
36
|
+
echo "## Dependabot rewrite: skipped"
|
|
37
|
+
echo ""
|
|
38
|
+
echo "GPG_PRIVATE_KEY and/or GPG_PASSPHRASE are not configured. "
|
|
39
|
+
echo "The Dependabot PR ships with bot-authored, web-signed commits."
|
|
40
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
41
|
+
echo "::warning::GPG secrets unset; leaving Dependabot commits as-is"
|
|
42
|
+
echo "skip=true" >> "$GITHUB_OUTPUT"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
- name: Checkout PR head branch
|
|
46
|
+
if: steps.gate.outputs.skip != 'true'
|
|
47
|
+
uses: actions/checkout@v6
|
|
48
|
+
with:
|
|
49
|
+
ref: ${{ github.event.pull_request.head.ref }}
|
|
50
|
+
fetch-depth: 0
|
|
51
|
+
persist-credentials: true
|
|
52
|
+
|
|
53
|
+
- name: Configure GPG and git identity from the imported key
|
|
54
|
+
if: steps.gate.outputs.skip != 'true'
|
|
55
|
+
env:
|
|
56
|
+
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
|
57
|
+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
|
58
|
+
run: |
|
|
59
|
+
set -euo pipefail
|
|
60
|
+
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
|
|
61
|
+
echo test | gpg --batch --yes --passphrase "$GPG_PASSPHRASE" \
|
|
62
|
+
--pinentry-mode loopback --sign > /dev/null
|
|
63
|
+
KEY_ID=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/{print $5; exit}')
|
|
64
|
+
UID_LINE=$(gpg --list-keys --with-colons "$KEY_ID" | awk -F: '/^uid/{print $10; exit}')
|
|
65
|
+
NAME="${UID_LINE%% <*}"
|
|
66
|
+
EMAIL=$(printf '%s' "$UID_LINE" | sed -nE 's/.*<([^>]+)>.*/\1/p')
|
|
67
|
+
git config user.name "$NAME"
|
|
68
|
+
git config user.email "$EMAIL"
|
|
69
|
+
git config user.signingkey "$KEY_ID"
|
|
70
|
+
git config commit.gpgsign true
|
|
71
|
+
|
|
72
|
+
- name: Rewrite commits with scope-aware messages
|
|
73
|
+
if: steps.gate.outputs.skip != 'true'
|
|
74
|
+
run: |
|
|
75
|
+
set -euo pipefail
|
|
76
|
+
BASE_SHA=$(git merge-base "origin/${{ github.event.pull_request.base.ref }}" HEAD)
|
|
77
|
+
git rebase "$BASE_SHA" --exec "scripts/rewrite-dependabot-commit.sh"
|
|
78
|
+
|
|
79
|
+
- name: Force-push rewritten branch
|
|
80
|
+
if: steps.gate.outputs.skip != 'true'
|
|
81
|
+
run: |
|
|
82
|
+
git push --force-with-lease origin "HEAD:${{ github.event.pull_request.head.ref }}"
|
|
83
|
+
|
|
84
|
+
- name: Write step summary
|
|
85
|
+
if: steps.gate.outputs.skip != 'true' && success()
|
|
86
|
+
run: |
|
|
87
|
+
{
|
|
88
|
+
echo "## Dependabot rewrite: applied"
|
|
89
|
+
echo ""
|
|
90
|
+
echo "PR branch \`${{ github.event.pull_request.head.ref }}\` rewritten:"
|
|
91
|
+
echo ""
|
|
92
|
+
echo "- Conventional-commits scope inferred from changed files / dev-group membership"
|
|
93
|
+
echo "- Authored and GPG-signed as \`$(git config user.name) <$(git config user.email)>\`"
|
|
94
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# Test & coverage artifacts
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
.mypy_cache/
|
|
16
|
+
.coverage
|
|
17
|
+
coverage.xml
|
|
18
|
+
junit.xml
|
|
19
|
+
htmlcov/
|
|
20
|
+
|
|
21
|
+
# Docs build output
|
|
22
|
+
site/
|
|
23
|
+
|
|
24
|
+
# Dependabot-generated requirements (Debian builds)
|
|
25
|
+
debian/requirements.txt
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"git_head": "afb82b697a77cecfce51e8fd20f6291d835ae9e5",
|
|
4
|
+
"git_head_short": "afb82b6",
|
|
5
|
+
"timestamp": "2026-06-21T18:23:20Z",
|
|
6
|
+
"components": {
|
|
7
|
+
"fastly-sync": {
|
|
8
|
+
"version": "0.1.0",
|
|
9
|
+
"tag": "v0.1.0",
|
|
10
|
+
"tag_sha": null
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|