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.
- atomic_keygen-1.0/.copier-answers.yml +12 -0
- atomic_keygen-1.0/.github/workflows/release.yml +110 -0
- atomic_keygen-1.0/.github/workflows/sync-template.yml +191 -0
- atomic_keygen-1.0/.github/workflows/tag.yml +171 -0
- atomic_keygen-1.0/.gitignore +51 -0
- atomic_keygen-1.0/LICENSE +21 -0
- atomic_keygen-1.0/PKG-INFO +183 -0
- atomic_keygen-1.0/README.md +156 -0
- atomic_keygen-1.0/docs/index.md +11 -0
- atomic_keygen-1.0/docs/keygen/blueprint.md +96 -0
- atomic_keygen-1.0/docs/keygen/fields/enum.md +37 -0
- atomic_keygen-1.0/docs/keygen/fields/field.md +51 -0
- atomic_keygen-1.0/docs/keygen/fields/param.md +32 -0
- atomic_keygen-1.0/docs/keygen/fields/pool.md +82 -0
- atomic_keygen-1.0/docs/keygen/fields.md +1 -0
- atomic_keygen-1.0/docs/keygen/key.md +61 -0
- atomic_keygen-1.0/docs/keygen/recorders/generator.md +80 -0
- atomic_keygen-1.0/docs/keygen/recorders/recorder.md +69 -0
- atomic_keygen-1.0/docs/keygen/recorders.md +1 -0
- atomic_keygen-1.0/docs/keygen/rengines/protocol.md +51 -0
- atomic_keygen-1.0/docs/keygen/rengines/random.md +102 -0
- atomic_keygen-1.0/docs/keygen/rengines/sobol.md +70 -0
- atomic_keygen-1.0/docs/keygen/rengines.md +1 -0
- atomic_keygen-1.0/docs/keygen/store.md +154 -0
- atomic_keygen-1.0/docs/keygen.md +1 -0
- atomic_keygen-1.0/pyproject.toml +63 -0
- atomic_keygen-1.0/setup.cfg +4 -0
- atomic_keygen-1.0/src/atomic_keygen.egg-info/PKG-INFO +183 -0
- atomic_keygen-1.0/src/atomic_keygen.egg-info/SOURCES.txt +56 -0
- atomic_keygen-1.0/src/atomic_keygen.egg-info/dependency_links.txt +1 -0
- atomic_keygen-1.0/src/atomic_keygen.egg-info/requires.txt +8 -0
- atomic_keygen-1.0/src/atomic_keygen.egg-info/top_level.txt +1 -0
- atomic_keygen-1.0/src/keygen/__init__.py +73 -0
- atomic_keygen-1.0/src/keygen/blueprint.py +171 -0
- atomic_keygen-1.0/src/keygen/fields/__init__.py +14 -0
- atomic_keygen-1.0/src/keygen/fields/enum.py +62 -0
- atomic_keygen-1.0/src/keygen/fields/field.py +91 -0
- atomic_keygen-1.0/src/keygen/fields/param.py +69 -0
- atomic_keygen-1.0/src/keygen/fields/pool.py +184 -0
- atomic_keygen-1.0/src/keygen/key.py +141 -0
- atomic_keygen-1.0/src/keygen/py.typed +0 -0
- atomic_keygen-1.0/src/keygen/recorders/__init__.py +12 -0
- atomic_keygen-1.0/src/keygen/recorders/generator.py +179 -0
- atomic_keygen-1.0/src/keygen/recorders/recorder.py +192 -0
- atomic_keygen-1.0/src/keygen/rengines/__init__.py +14 -0
- atomic_keygen-1.0/src/keygen/rengines/protocol.py +44 -0
- atomic_keygen-1.0/src/keygen/rengines/random.py +111 -0
- atomic_keygen-1.0/src/keygen/rengines/sobol.py +104 -0
- atomic_keygen-1.0/src/keygen/store.py +293 -0
- atomic_keygen-1.0/tests/__init__.py +0 -0
- atomic_keygen-1.0/tests/conftest.py +88 -0
- atomic_keygen-1.0/tests/test_blueprint.py +246 -0
- atomic_keygen-1.0/tests/test_fields.py +267 -0
- atomic_keygen-1.0/tests/test_generator.py +200 -0
- atomic_keygen-1.0/tests/test_key.py +200 -0
- atomic_keygen-1.0/tests/test_recorder.py +191 -0
- atomic_keygen-1.0/tests/test_rengines.py +203 -0
- 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.
|