44 workflow_dispatch :
55 inputs :
66 version :
7- description : ' Version to release (e.g., 0.2.1 )'
7+ description : ' Version to release (e.g., 0.3.0 )'
88 required : true
99 type : string
10- create_tag :
11- description : ' Create git tag for this version '
12- required : true
10+ prerelease :
11+ description : ' Mark as prerelease '
12+ required : false
1313 type : boolean
14- default : true
14+ default : false
1515 draft :
1616 description : ' Create as draft release'
1717 required : false
2020
2121permissions :
2222 contents : write
23- id-token : write
2423
2524jobs :
2625 manual-release :
26+ name : Create Manual Release
2727 runs-on : ubuntu-latest
28-
29- services :
30- redis :
31- image : redis:7-alpine
32- ports :
33- - 6379:6379
34- options : >-
35- --health-cmd "redis-cli ping"
36- --health-interval 10s
37- --health-timeout 5s
38- --health-retries 5
39-
4028 steps :
41- - name : Checkout code
29+ - name : Checkout
4230 uses : actions/checkout@v4
4331 with :
4432 fetch-depth : 0
@@ -48,67 +36,80 @@ jobs:
4836 with :
4937 python-version : ' 3.11'
5038
51- - name : Install build & dev dependencies
39+ - name : Install dependencies
5240 run : |
5341 python -m pip install --upgrade pip
54- pip install -e .[dev]
5542 pip install build twine
5643
57- - name : Verify version matches
44+ - name : Verify version
5845 run : |
59- PACKAGE_VERSION=$(python -c "import yokedcache; print(yokedcache.__version__)")
46+ PACKAGE_VERSION=$(python -c "
47+ import sys
48+ sys.path.insert(0, 'src')
49+ import yokedcache
50+ print(yokedcache.__version__)
51+ ")
52+
53+ echo "Package version: $PACKAGE_VERSION"
54+ echo "Input version: ${{ github.event.inputs.version }}"
55+
6056 if [ "$PACKAGE_VERSION" != "${{ github.event.inputs.version }}" ]; then
57+ echo "❌ Version mismatch!"
6158 echo "Package version ($PACKAGE_VERSION) doesn't match input version (${{ github.event.inputs.version }})"
62- echo "Please update src/yokedcache/__init__.py to version ${{ github.event.inputs.version }}"
59+ echo "Please update src/yokedcache/__init__.py to version ${{ github.event.inputs.version }} first "
6360 exit 1
6461 fi
6562
66- - name : Create tag
67- if : github.event.inputs.create_tag == 'true'
63+ echo "✅ Version verified: $PACKAGE_VERSION"
64+
65+ - name : Check if tag exists
66+ id : tag-check
6867 run : |
69- git config user.name github-actions
70- git config user.email github-actions@github.com
71- git tag -a "v${{ github.event.inputs.version }}" -m "Release v${{ github.event.inputs.version }}"
72- git push origin "v${{ github.event.inputs.version }}"
68+ TAG="v${{ github.event.inputs.version }}"
69+ if git tag --list | grep -q "^${TAG}$"; then
70+ echo "exists=true" >> $GITHUB_OUTPUT
71+ echo "Tag $TAG already exists"
72+ else
73+ echo "exists=false" >> $GITHUB_OUTPUT
74+ echo "Tag $TAG does not exist, will create it"
75+ fi
7376
74- - name : Extract changelog for this version
75- id : changelog
77+ - name : Create tag
78+ if : steps.tag-check.outputs.exists == 'false'
7679 run : |
77- python << 'EOF'
78- import re
79-
80- version = "${{ github.event.inputs.version }}"
81-
82- try:
83- with open('CHANGELOG.md', 'r') as f:
84- content = f.read()
85-
86- pattern = rf'## \[{re.escape(version)}\].*?\n(.*?)(?=\n## \[|\n---|\Z)'
87- match = re.search(pattern, content, re.DOTALL)
88-
89- if match:
90- changelog_content = match.group(1).strip()
91- changelog_content = re.sub(r'\n---+.*$', '', changelog_content, flags=re.MULTILINE)
92-
93- with open('release_notes.md', 'w') as f:
94- f.write(changelog_content)
95- print(f"Extracted changelog for version {version}")
96- else:
97- print(f"No changelog found for version {version}")
98- with open('release_notes.md', 'w') as f:
99- f.write(f"Manual release {version}")
100- except Exception as e:
101- print(f"Error extracting changelog: {e}")
102- with open('release_notes.md', 'w') as f:
103- f.write(f"Manual release {version}")
104- EOF
105-
106- - name : Run tests
80+ git config user.name "github-actions[bot]"
81+ git config user.email "github-actions[bot]@users.noreply.github.com"
82+
83+ TAG="v${{ github.event.inputs.version }}"
84+ git tag -a "$TAG" -m "Manual release $TAG"
85+ git push origin "$TAG"
86+
87+ - name : Generate changelog
10788 run : |
108- pytest -q --disable-warnings
89+ CURRENT_TAG="v${{ github.event.inputs.version }}"
90+
91+ # Find previous tag
92+ PREV_TAG=$(git tag --sort=-v:refname | grep -v "^${CURRENT_TAG}$" | head -n1 || echo "")
93+
94+ if [ -n "$PREV_TAG" ]; then
95+ RANGE="$PREV_TAG..HEAD"
96+ else
97+ RANGE="HEAD"
98+ fi
99+
100+ export CURRENT_TAG PREV_TAG RANGE
101+ python scripts/gen_changelog.py > release_notes.md
102+
103+ echo "Generated release notes:"
104+ cat release_notes.md
109105
110106 - name : Build package
111- run : python -m build
107+ run : |
108+ python -m build
109+
110+ - name : Check package
111+ run : |
112+ twine check dist/*
112113
113114 - name : Create GitHub Release
114115 uses : softprops/action-gh-release@v1
@@ -120,5 +121,11 @@ jobs:
120121 dist/*.whl
121122 dist/*.tar.gz
122123 draft : ${{ github.event.inputs.draft }}
123- prerelease : ${{ contains(github.event.inputs.version, 'rc') || contains(github.event.inputs.version, 'beta') || contains(github.event.inputs.version, 'alpha') }}
124- # Publication to PyPI is performed by publish.yml upon release publication to avoid duplication.
124+ prerelease : ${{ github.event.inputs.prerelease }}
125+ env :
126+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
127+
128+ - name : Summary
129+ run : |
130+ echo "✅ Successfully created manual release for v${{ github.event.inputs.version }}!"
131+ echo "🔗 Release URL: https://github.com/${{ github.repository }}/releases/tag/v${{ github.event.inputs.version }}"
0 commit comments