atomic-keygen 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 (58) hide show
  1. atomic_keygen-1.0/.copier-answers.yml +12 -0
  2. atomic_keygen-1.0/.github/workflows/release.yml +110 -0
  3. atomic_keygen-1.0/.github/workflows/sync-template.yml +191 -0
  4. atomic_keygen-1.0/.github/workflows/tag.yml +171 -0
  5. atomic_keygen-1.0/.gitignore +51 -0
  6. atomic_keygen-1.0/LICENSE +21 -0
  7. atomic_keygen-1.0/PKG-INFO +183 -0
  8. atomic_keygen-1.0/README.md +156 -0
  9. atomic_keygen-1.0/docs/index.md +11 -0
  10. atomic_keygen-1.0/docs/keygen/blueprint.md +96 -0
  11. atomic_keygen-1.0/docs/keygen/fields/enum.md +37 -0
  12. atomic_keygen-1.0/docs/keygen/fields/field.md +51 -0
  13. atomic_keygen-1.0/docs/keygen/fields/param.md +32 -0
  14. atomic_keygen-1.0/docs/keygen/fields/pool.md +82 -0
  15. atomic_keygen-1.0/docs/keygen/fields.md +1 -0
  16. atomic_keygen-1.0/docs/keygen/key.md +61 -0
  17. atomic_keygen-1.0/docs/keygen/recorders/generator.md +80 -0
  18. atomic_keygen-1.0/docs/keygen/recorders/recorder.md +69 -0
  19. atomic_keygen-1.0/docs/keygen/recorders.md +1 -0
  20. atomic_keygen-1.0/docs/keygen/rengines/protocol.md +51 -0
  21. atomic_keygen-1.0/docs/keygen/rengines/random.md +102 -0
  22. atomic_keygen-1.0/docs/keygen/rengines/sobol.md +70 -0
  23. atomic_keygen-1.0/docs/keygen/rengines.md +1 -0
  24. atomic_keygen-1.0/docs/keygen/store.md +154 -0
  25. atomic_keygen-1.0/docs/keygen.md +1 -0
  26. atomic_keygen-1.0/pyproject.toml +63 -0
  27. atomic_keygen-1.0/setup.cfg +4 -0
  28. atomic_keygen-1.0/src/atomic_keygen.egg-info/PKG-INFO +183 -0
  29. atomic_keygen-1.0/src/atomic_keygen.egg-info/SOURCES.txt +56 -0
  30. atomic_keygen-1.0/src/atomic_keygen.egg-info/dependency_links.txt +1 -0
  31. atomic_keygen-1.0/src/atomic_keygen.egg-info/requires.txt +8 -0
  32. atomic_keygen-1.0/src/atomic_keygen.egg-info/top_level.txt +1 -0
  33. atomic_keygen-1.0/src/keygen/__init__.py +73 -0
  34. atomic_keygen-1.0/src/keygen/blueprint.py +171 -0
  35. atomic_keygen-1.0/src/keygen/fields/__init__.py +14 -0
  36. atomic_keygen-1.0/src/keygen/fields/enum.py +62 -0
  37. atomic_keygen-1.0/src/keygen/fields/field.py +91 -0
  38. atomic_keygen-1.0/src/keygen/fields/param.py +69 -0
  39. atomic_keygen-1.0/src/keygen/fields/pool.py +184 -0
  40. atomic_keygen-1.0/src/keygen/key.py +141 -0
  41. atomic_keygen-1.0/src/keygen/py.typed +0 -0
  42. atomic_keygen-1.0/src/keygen/recorders/__init__.py +12 -0
  43. atomic_keygen-1.0/src/keygen/recorders/generator.py +179 -0
  44. atomic_keygen-1.0/src/keygen/recorders/recorder.py +192 -0
  45. atomic_keygen-1.0/src/keygen/rengines/__init__.py +14 -0
  46. atomic_keygen-1.0/src/keygen/rengines/protocol.py +44 -0
  47. atomic_keygen-1.0/src/keygen/rengines/random.py +111 -0
  48. atomic_keygen-1.0/src/keygen/rengines/sobol.py +104 -0
  49. atomic_keygen-1.0/src/keygen/store.py +293 -0
  50. atomic_keygen-1.0/tests/__init__.py +0 -0
  51. atomic_keygen-1.0/tests/conftest.py +88 -0
  52. atomic_keygen-1.0/tests/test_blueprint.py +246 -0
  53. atomic_keygen-1.0/tests/test_fields.py +267 -0
  54. atomic_keygen-1.0/tests/test_generator.py +200 -0
  55. atomic_keygen-1.0/tests/test_key.py +200 -0
  56. atomic_keygen-1.0/tests/test_recorder.py +191 -0
  57. atomic_keygen-1.0/tests/test_rengines.py +203 -0
  58. atomic_keygen-1.0/tests/test_store.py +227 -0
@@ -0,0 +1,12 @@
1
+ # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
2
+ _commit: 1.8-13-g86b0a7d
3
+ _src_path: /home/maximiliantodea/Github/github-infra
4
+ description: Zero-dependency generic seeded generator framework with dedup and SQLite
5
+ persistence.
6
+ development_status: 5 - Production/Stable
7
+ min_python: '3.12'
8
+ optional_extras:
9
+ sobol:
10
+ - scipy>=1.12
11
+ package_name: keygen
12
+ project_name: atomic-keygen
@@ -0,0 +1,110 @@
1
+ name: Prepare Release Branch
2
+
3
+ # Trigger manually from the Actions UI. The workflow will create
4
+ # release/<version> from the selected dev branch when it does not exist yet,
5
+ # or reuse the existing release branch when it is already present.
6
+ on:
7
+ workflow_dispatch:
8
+ inputs:
9
+ dev-branch:
10
+ description: "Source dev branch to branch from when creating the release branch"
11
+ required: true
12
+ default: ""
13
+ version:
14
+ description: "Release version without the leading v; used as release/<version>"
15
+ required: true
16
+ default: ""
17
+
18
+ permissions:
19
+ contents: write
20
+ pull-requests: write
21
+ models: read
22
+
23
+ jobs:
24
+ setup:
25
+ name: Parse release inputs
26
+ runs-on: ubuntu-latest
27
+ outputs:
28
+ fingerprints: ${{ steps.parse.outputs.fingerprints }}
29
+ version: ${{ steps.parse.outputs.version }}
30
+ dev_branch: ${{ steps.parse.outputs.dev_branch }}
31
+ release_branch: ${{ steps.parse.outputs.release_branch }}
32
+ groom_branch: ${{ steps.parse.outputs.groom_branch }}
33
+
34
+ steps:
35
+ - name: Parse workflow inputs
36
+ id: parse
37
+ run: |
38
+ VERSION_INPUT="${{ github.event.inputs.version || '' }}"
39
+ DEV_BRANCH="${{ github.event.inputs['dev-branch'] || '' }}"
40
+
41
+ VERSION="${VERSION_INPUT#v}"
42
+ VERSION="${VERSION#release/}"
43
+
44
+ if [ -z "$VERSION" ]; then
45
+ echo "Version input is required." >&2
46
+ exit 1
47
+ fi
48
+
49
+ if [ -z "$DEV_BRANCH" ]; then
50
+ echo "dev-branch input is required." >&2
51
+ exit 1
52
+ fi
53
+
54
+ RELEASE_BRANCH="release/${VERSION}"
55
+ GROOM_BRANCH="groom/release-${VERSION}"
56
+
57
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
58
+ echo "dev_branch=${DEV_BRANCH}" >> "$GITHUB_OUTPUT"
59
+ echo "release_branch=${RELEASE_BRANCH}" >> "$GITHUB_OUTPUT"
60
+ echo "groom_branch=${GROOM_BRANCH}" >> "$GITHUB_OUTPUT"
61
+ {
62
+ echo 'fingerprints<<EOF'
63
+ echo 'fingerprints/standardize-docstrings.md'
64
+ echo 'fingerprints/standardize-comments.md'
65
+ echo 'EOF'
66
+ } >> "$GITHUB_OUTPUT"
67
+
68
+ echo "Dev branch : $DEV_BRANCH"
69
+ echo "Release branch : $RELEASE_BRANCH"
70
+ echo "Version : $VERSION"
71
+
72
+ ensure-release-branch:
73
+ name: Ensure release branch exists
74
+ needs: setup
75
+ runs-on: ubuntu-latest
76
+
77
+ steps:
78
+ - uses: actions/checkout@v4
79
+ with:
80
+ fetch-depth: 0
81
+ token: ${{ secrets.TEMPLATE_SYNC_TOKEN != '' && secrets.TEMPLATE_SYNC_TOKEN || github.token }}
82
+
83
+ - name: Create or reuse release branch
84
+ env:
85
+ DEV_BRANCH: ${{ needs.setup.outputs.dev_branch }}
86
+ RELEASE_BRANCH: ${{ needs.setup.outputs.release_branch }}
87
+ run: |
88
+ if git ls-remote --exit-code --heads origin "refs/heads/${RELEASE_BRANCH}" >/dev/null 2>&1; then
89
+ echo "Release branch already exists on origin: ${RELEASE_BRANCH}"
90
+ exit 0
91
+ fi
92
+
93
+ git fetch origin "$DEV_BRANCH" --no-tags
94
+ git config user.name "github-actions[bot]"
95
+ git config user.email "github-actions[bot]@users.noreply.github.com"
96
+ git switch -C "$RELEASE_BRANCH" "origin/$DEV_BRANCH"
97
+ git push origin "$RELEASE_BRANCH"
98
+
99
+ review:
100
+ name: Groom release branch
101
+ needs: [setup, ensure-release-branch]
102
+ uses: importt-ant/github-infra/.github/workflows/prepare-for-release.yml@main
103
+ with:
104
+ python-version: "3.12"
105
+ fingerprints: ${{ needs.setup.outputs.fingerprints }}
106
+ source-branch: ${{ needs.setup.outputs.release_branch }}
107
+ working-branch: ${{ needs.setup.outputs.groom_branch }}
108
+ pr-base-branch: ${{ needs.setup.outputs.release_branch }}
109
+ run-doc-generator: true
110
+ secrets: inherit
@@ -0,0 +1,191 @@
1
+ name: Sync Template
2
+
3
+ # automatically syncs with copier
4
+
5
+ on:
6
+ repository_dispatch:
7
+ types: [github-infra-template-update]
8
+ workflow_dispatch:
9
+ inputs:
10
+ template-ref:
11
+ description: "Commit SHA or ref from github-infra to apply"
12
+ required: false
13
+ default: ""
14
+
15
+ permissions:
16
+ contents: write
17
+ pull-requests: write
18
+
19
+ concurrency:
20
+ group: template-sync
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ sync-template:
25
+ name: Sync Copier template
26
+ runs-on: ubuntu-latest
27
+
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ with:
31
+ fetch-depth: 0
32
+ token: ${{ secrets.TEMPLATE_SYNC_TOKEN != '' && secrets.TEMPLATE_SYNC_TOKEN || github.token }}
33
+
34
+ - uses: actions/setup-python@v5
35
+ with:
36
+ python-version: "3.12"
37
+
38
+ - name: Install copier
39
+ run: pip install --quiet copier
40
+
41
+ - name: Check copier setup
42
+ id: setup
43
+ run: |
44
+ if [ ! -f .copier-answers.yml ]; then
45
+ echo "enabled=false" >> "$GITHUB_OUTPUT"
46
+ echo "No .copier-answers.yml found; skipping."
47
+ exit 0
48
+ fi
49
+
50
+ if ! grep -q 'github-infra' .copier-answers.yml; then
51
+ echo "enabled=false" >> "$GITHUB_OUTPUT"
52
+ echo "This repo is not initialized from github-infra; skipping."
53
+ exit 0
54
+ fi
55
+
56
+ echo "enabled=true" >> "$GITHUB_OUTPUT"
57
+
58
+ - name: Resolve template ref
59
+ if: steps.setup.outputs.enabled == 'true'
60
+ id: ref
61
+ env:
62
+ EVENT_TEMPLATE_REF: ${{ github.event.client_payload.template_ref || '' }}
63
+ INPUT_TEMPLATE_REF: ${{ github.event.inputs['template-ref'] || '' }}
64
+ run: |
65
+ TEMPLATE_REF="${EVENT_TEMPLATE_REF:-$INPUT_TEMPLATE_REF}"
66
+ TEMPLATE_REF="${TEMPLATE_REF:-main}"
67
+
68
+ if printf '%s' "$TEMPLATE_REF" | grep -Eq '^[0-9a-f]{40}$'; then
69
+ TEMPLATE_SHA="$TEMPLATE_REF"
70
+ else
71
+ TEMPLATE_SHA=$(git ls-remote https://github.com/importt-ant/github-infra.git "$TEMPLATE_REF" | awk 'NR==1 {print $1}')
72
+ fi
73
+
74
+ if [ -z "$TEMPLATE_SHA" ]; then
75
+ echo "Could not resolve github-infra ref: $TEMPLATE_REF" >&2
76
+ exit 1
77
+ fi
78
+
79
+ SHORT_SHA="$(printf '%s' "$TEMPLATE_SHA" | cut -c1-12)"
80
+ BRANCH="chore/template-sync-${SHORT_SHA}"
81
+ echo "template_ref=${TEMPLATE_REF}" >> "$GITHUB_OUTPUT"
82
+ echo "template_sha=${TEMPLATE_SHA}" >> "$GITHUB_OUTPUT"
83
+ echo "short_sha=${SHORT_SHA}" >> "$GITHUB_OUTPUT"
84
+ echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
85
+
86
+ - name: Create sync branch
87
+ if: steps.setup.outputs.enabled == 'true'
88
+ id: branch
89
+ env:
90
+ BRANCH: ${{ steps.ref.outputs.branch }}
91
+ run: |
92
+ git config user.name "github-actions[bot]"
93
+ git config user.email "github-actions[bot]@users.noreply.github.com"
94
+ START_SHA="$(git rev-parse HEAD)"
95
+ git switch -C "$BRANCH"
96
+ echo "start_sha=${START_SHA}" >> "$GITHUB_OUTPUT"
97
+
98
+ - name: Normalize copier source
99
+ if: steps.setup.outputs.enabled == 'true'
100
+ env:
101
+ TEMPLATE_SHA: ${{ steps.ref.outputs.template_sha }}
102
+ run: |
103
+ python - <<'PY'
104
+ from pathlib import Path
105
+
106
+ path = Path('.copier-answers.yml')
107
+ lines = path.read_text(encoding='utf-8').splitlines()
108
+ updated: list[str] = []
109
+ changed = False
110
+
111
+ for line in lines:
112
+ if line.startswith('_src_path:'):
113
+ desired = '_src_path: gh:importt-ant/github-infra'
114
+ updated.append(desired)
115
+ changed = changed or line != desired
116
+ else:
117
+ updated.append(line)
118
+
119
+ if changed:
120
+ path.write_text('\n'.join(updated) + '\n', encoding='utf-8')
121
+ PY
122
+
123
+ if [ -n "$(git status --porcelain .copier-answers.yml)" ]; then
124
+ git add .copier-answers.yml
125
+ git commit -m "chore(template): normalize copier source ${TEMPLATE_SHA} [skip ci]"
126
+ fi
127
+
128
+ - name: Run copier update
129
+ if: steps.setup.outputs.enabled == 'true'
130
+ env:
131
+ TEMPLATE_REF: ${{ steps.ref.outputs.template_ref }}
132
+ run: |
133
+ copier update --trust --defaults --vcs-ref "$TEMPLATE_REF"
134
+
135
+ - name: Commit and push
136
+ if: steps.setup.outputs.enabled == 'true'
137
+ id: commit
138
+ env:
139
+ SHORT_SHA: ${{ steps.ref.outputs.short_sha }}
140
+ BRANCH: ${{ steps.ref.outputs.branch }}
141
+ START_SHA: ${{ steps.branch.outputs.start_sha }}
142
+ run: |
143
+ if [ -n "$(git status --porcelain)" ]; then
144
+ git add -A
145
+ git commit -m "chore(template): sync github-infra ${SHORT_SHA} [skip ci]"
146
+ fi
147
+
148
+ if [ "$(git rev-parse HEAD)" = "$START_SHA" ]; then
149
+ echo "No template changes."
150
+ echo "branch=" >> "$GITHUB_OUTPUT"
151
+ exit 0
152
+ fi
153
+
154
+ git push --force-with-lease origin "$BRANCH"
155
+ echo "branch=${BRANCH}" >> "$GITHUB_OUTPUT"
156
+
157
+ - name: Open pull request
158
+ if: steps.commit.outputs.branch != ''
159
+ env:
160
+ GH_TOKEN: ${{ secrets.TEMPLATE_SYNC_TOKEN != '' && secrets.TEMPLATE_SYNC_TOKEN || github.token }}
161
+ BRANCH: ${{ steps.commit.outputs.branch }}
162
+ TEMPLATE_REF: ${{ steps.ref.outputs.template_ref }}
163
+ TEMPLATE_SHA: ${{ steps.ref.outputs.template_sha }}
164
+ SHORT_SHA: ${{ steps.ref.outputs.short_sha }}
165
+ BASE_BRANCH: ${{ github.event.repository.default_branch }}
166
+ run: |
167
+ EXISTING=$(gh pr list --head "$BRANCH" --base "$BASE_BRANCH" --json url --jq '.[0].url' 2>/dev/null || true)
168
+
169
+ if [ -n "$EXISTING" ]; then
170
+ echo "PR already exists: $EXISTING"
171
+ else
172
+ BODY=$(cat <<EOF
173
+ ## Automated template sync
174
+
175
+ This PR updates the repository from the latest `github-infra` Copier template ref: `${TEMPLATE_REF}`.
176
+
177
+ - resolved commit: `${TEMPLATE_SHA}`
178
+
179
+ ### What ran
180
+ - normalized `.copier-answers.yml` to use the remote template source
181
+ - ran `copier update --trust --defaults --vcs-ref ${TEMPLATE_REF}`
182
+
183
+ Review and merge when the generated changes look correct.
184
+ EOF
185
+ )
186
+ gh pr create \
187
+ --title "chore(template): sync github-infra ${SHORT_SHA}" \
188
+ --body "$BODY" \
189
+ --base "$BASE_BRANCH" \
190
+ --head "$BRANCH"
191
+ fi
@@ -0,0 +1,171 @@
1
+ name: Release and Publish
2
+
3
+ # Trigger manually from the GitHub Actions UI while the selected ref is the
4
+ # release/x.y.z branch you want to ship.
5
+ #
6
+ # The workflow:
7
+ # 1. runs pytest on the release branch
8
+ # 2. creates and pushes tag vX.Y.Z from that branch
9
+ # 3. creates the GitHub release
10
+ # 4. publishes to PyPI
11
+ # 5. opens PRs from release/x.y.z into main and dev/x.y
12
+ #
13
+ on:
14
+ workflow_dispatch:
15
+ inputs:
16
+ version:
17
+ description: "Release version without the leading v; defaults to the selected release/x.y.z branch suffix"
18
+ required: false
19
+ default: ""
20
+
21
+ permissions:
22
+ contents: write
23
+ id-token: write
24
+ pull-requests: write
25
+
26
+ jobs:
27
+ setup:
28
+ name: Parse release inputs
29
+ runs-on: ubuntu-latest
30
+ outputs:
31
+ tag: ${{ steps.parse.outputs.tag }}
32
+ version: ${{ steps.parse.outputs.version }}
33
+ release_branch: ${{ steps.parse.outputs.release_branch }}
34
+ dev_branch: ${{ steps.parse.outputs.dev_branch }}
35
+ steps:
36
+ - name: Parse version from branch
37
+ id: parse
38
+ run: |
39
+ BRANCH="${GITHUB_REF_NAME}"
40
+ if [[ "$BRANCH" != release/* ]]; then
41
+ echo "This workflow must be run from a release/x.y.z branch; got '$BRANCH'" >&2
42
+ exit 1
43
+ fi
44
+
45
+ VERSION_INPUT="${{ github.event.inputs.version || '' }}"
46
+ VERSION="${VERSION_INPUT:-${BRANCH#release/}}"
47
+ TAG="v${VERSION}"
48
+ MAJOR="$(echo "$VERSION" | cut -d. -f1)"
49
+ MINOR="$(echo "$VERSION" | cut -d. -f2)"
50
+ RELEASE_BRANCH="$BRANCH"
51
+ DEV_BRANCH="dev/${MAJOR}.${MINOR}"
52
+
53
+ echo "branch=${BRANCH}"
54
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
55
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
56
+ echo "release_branch=${RELEASE_BRANCH}" >> "$GITHUB_OUTPUT"
57
+ echo "dev_branch=${DEV_BRANCH}" >> "$GITHUB_OUTPUT"
58
+
59
+ pytest:
60
+ name: Run pytest before release
61
+ needs: setup
62
+ uses: importt-ant/github-infra/.github/workflows/run-pytest.yml@main
63
+ with:
64
+ python-version: "3.12"
65
+ checkout-ref: ${{ needs.setup.outputs.release_branch }}
66
+ secrets: inherit
67
+
68
+ create-release:
69
+ name: Create tag and GitHub release
70
+ needs: [setup, pytest]
71
+ runs-on: ubuntu-latest
72
+
73
+ steps:
74
+ - uses: actions/checkout@v4
75
+ with:
76
+ fetch-depth: 0
77
+
78
+ - name: Create and push tag
79
+ env:
80
+ TAG: ${{ needs.setup.outputs.tag }}
81
+ RELEASE_BRANCH: ${{ needs.setup.outputs.release_branch }}
82
+ run: |
83
+ git fetch origin "$RELEASE_BRANCH" --tags --no-tags || true
84
+ git fetch origin "$RELEASE_BRANCH" --no-tags
85
+ git switch -C "$RELEASE_BRANCH" "origin/$RELEASE_BRANCH"
86
+
87
+ if git ls-remote --exit-code --tags origin "refs/tags/${TAG}" >/dev/null 2>&1; then
88
+ echo "Tag $TAG already exists on origin."
89
+ else
90
+ git tag "$TAG"
91
+ git push origin "$TAG"
92
+ fi
93
+
94
+ - name: Create published release from the dev line
95
+ env:
96
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
97
+ TAG: ${{ needs.setup.outputs.tag }}
98
+ DEV_BRANCH: ${{ needs.setup.outputs.dev_branch }}
99
+ run: |
100
+ if gh release view "$TAG" >/dev/null 2>&1; then
101
+ echo "Release already exists for $TAG."
102
+ else
103
+ gh release create "$TAG" --verify-tag --generate-notes --target "$DEV_BRANCH"
104
+ fi
105
+
106
+ publish-to-pypi:
107
+ name: Publish to PyPI
108
+ needs: [setup, create-release]
109
+ runs-on: ubuntu-latest
110
+ environment: pypi
111
+
112
+ steps:
113
+ - uses: actions/checkout@v4
114
+ with:
115
+ fetch-depth: 0
116
+ token: ${{ secrets.TEMPLATE_SYNC_TOKEN != '' && secrets.TEMPLATE_SYNC_TOKEN || github.token }}
117
+
118
+ - uses: actions/setup-python@v5
119
+ with:
120
+ python-version: "3.12"
121
+
122
+ - name: Install build
123
+ run: pip install build
124
+
125
+ - name: Build package
126
+ run: python -m build
127
+
128
+ - name: Publish to PyPI
129
+ uses: pypa/gh-action-pypi-publish@release/v1
130
+
131
+ open-merge-prs:
132
+ name: Open merge PRs
133
+ needs: [setup, create-release, publish-to-pypi]
134
+ runs-on: ubuntu-latest
135
+
136
+ steps:
137
+ - name: Open PRs into main and dev
138
+ env:
139
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
140
+ VERSION: ${{ needs.setup.outputs.version }}
141
+ RELEASE_BRANCH: ${{ needs.setup.outputs.release_branch }}
142
+ DEV_BRANCH: ${{ needs.setup.outputs.dev_branch }}
143
+ run: |
144
+ for TARGET in main "$DEV_BRANCH"; do
145
+ EXISTING=$(gh pr list \
146
+ --head "$RELEASE_BRANCH" \
147
+ --base "$TARGET" \
148
+ --json url \
149
+ --jq '.[0].url' 2>/dev/null || true)
150
+
151
+ if [ -n "$EXISTING" ]; then
152
+ echo "PR already exists for $RELEASE_BRANCH -> $TARGET: $EXISTING"
153
+ continue
154
+ fi
155
+
156
+ BODY=$(printf '%s\n' \
157
+ '## Release merge' \
158
+ '' \
159
+ "This PR was opened automatically after publishing version \`${VERSION}\`." \
160
+ '' \
161
+ "- source branch: \`${RELEASE_BRANCH}\`" \
162
+ "- target branch: \`${TARGET}\`" \
163
+ '' \
164
+ 'Merge this PR after the release publication checks look correct.')
165
+
166
+ gh pr create \
167
+ --head "$RELEASE_BRANCH" \
168
+ --base "$TARGET" \
169
+ --title "chore(release): merge ${VERSION} into ${TARGET}" \
170
+ --body "$BODY"
171
+ done
@@ -0,0 +1,51 @@
1
+ # ── Python ────────────────────────────────────────────────────────────────────
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+
7
+ # Distributions
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ *.egg
12
+ .eggs/
13
+
14
+ # Virtual environments
15
+ .venv/
16
+ venv/
17
+ env/
18
+
19
+ # ── Tools ─────────────────────────────────────────────────────────────────────
20
+
21
+ # IDE
22
+ .vscode/
23
+ .idea/
24
+ *.swp
25
+ *.swo
26
+
27
+ # Testing / linting caches
28
+ .pytest_cache/
29
+ .ruff_cache/
30
+ .mypy_cache/
31
+ .coverage
32
+ htmlcov/
33
+
34
+ # Type checker
35
+ .pyright/
36
+
37
+ # ── Build / version ───────────────────────────────────────────────────────────
38
+ *.python-version
39
+ uv.lock
40
+
41
+ # ── OS ────────────────────────────────────────────────────────────────────────
42
+ .DS_Store
43
+ Thumbs.db
44
+
45
+ # ── Environment ───────────────────────────────────────────────────────────────
46
+ .env
47
+ .env.*
48
+ !.env.example
49
+
50
+ # ── Project-specific ──────────────────────────────────────────────────────────
51
+ # (add per-project entries below this line)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Maximilian Todea
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.