Automate version generation and tagging in CI-CD workflow#6
Automate version generation and tagging in CI-CD workflow#6
Conversation
Co-authored-by: rhoadesScholar <37990507+rhoadesScholar@users.noreply.github.com>
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
There was a problem hiding this comment.
Pull Request Overview
This PR automates version generation, CITATION.cff updates, and git tagging within the CI/CD workflow, moving these responsibilities from local pre-commit hooks to GitHub Actions. The workflow now automatically generates date-based semantic versions, commits metadata updates, creates tags, and publishes releases on every push to main.
Key Changes
- Automated formatting with auto-commit: Format job now commits black/isort changes back to main with
[skip ci]to prevent infinite loops - Date-based version generation: Tag-release job generates PEP440-compliant versions using UTC timestamp format (
YYYY.M.D.HHmm) - Automated metadata and release management: CITATION.cff is automatically updated with version and release date, committed, and tagged for release
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: Pull latest changes if on main | ||
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | ||
| run: git pull origin main |
There was a problem hiding this comment.
[nitpick] The git pull can introduce inconsistency in what code is being tested. After the format job pushes changes, this pull will fetch those formatting commits. However, if testing should validate the exact commit that triggered the workflow, this pull defeats that purpose.
Consider whether you want to test the original commit (remove the pull) or always test the latest state of main (acceptable but may not reflect the actual commit being released).
| - name: Pull latest changes if on main | |
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | |
| run: git pull origin main |
There was a problem hiding this comment.
@copilot Won't the newest version (post-formatting) be tested AND released?
There was a problem hiding this comment.
Yes, exactly. The workflow ensures that the formatted version is both tested AND released. The format job commits formatting changes, then the test job pulls and tests those changes, and finally the tag-release job tags the tested (formatted) code. This maintains consistency - what gets tested is what gets released.
The git pull in the test job specifically ensures that tests run on the formatted code, not the original unformatted version.
.github/workflows/ci-cd.yml
Outdated
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| git tag ${{ steps.get_tag.outputs.tag }} |
There was a problem hiding this comment.
After pushing the version commit, the workflow attempts to create a tag pointing to the current HEAD, which is now the version commit (not the original code commit). This means:
- The release tag will point to a commit containing only CITATION.cff changes
- The actual code changes that triggered the release are in the parent commit
- When checking out the tag for publishing (line 188), you'll get the version commit
This creates confusion about what code is actually in the release. Consider creating the tag on the pre-version commit using git tag $tag HEAD^ or restructure to tag before committing version changes.
| git tag ${{ steps.get_tag.outputs.tag }} | |
| git tag ${{ steps.get_tag.outputs.tag }} HEAD^ |
There was a problem hiding this comment.
Fixed in commit 7cf98ba. Restructured to create the tag BEFORE committing version changes. Now the tag points to the actual code commit (with formatting), and the version bump commit comes after. This ensures releases contain the actual code changes and publishing builds from the correct commit.
.github/workflows/ci-cd.yml
Outdated
| git tag ${{ steps.get_tag.outputs.tag }} | ||
| git push origin ${{ steps.get_tag.outputs.tag }} | ||
|
|
There was a problem hiding this comment.
The tag creation at line 171 will fail with "fatal: tag already exists" if the tag was already created after the check at line 150. Between checking and creating the tag, another concurrent workflow could create the same version tag (since both would generate the same timestamp-based version if running in the same minute).
Use git tag -a ${{ steps.get_tag.outputs.tag }} -m "Release ${{ steps.get_tag.outputs.tag }}" with error handling, or use git tag with the --force flag cautiously, or add uniqueness to the version (e.g., include seconds or a commit SHA suffix).
| git tag ${{ steps.get_tag.outputs.tag }} | |
| git push origin ${{ steps.get_tag.outputs.tag }} | |
| if git rev-parse "${{ steps.get_tag.outputs.tag }}" >/dev/null 2>&1; then | |
| echo "Tag ${{ steps.get_tag.outputs.tag }} already exists, skipping tag creation and push" | |
| else | |
| git tag ${{ steps.get_tag.outputs.tag }} | |
| git push origin ${{ steps.get_tag.outputs.tag }} | |
| fi |
There was a problem hiding this comment.
Fixed in commit 7cf98ba. Added conditional check if git rev-parse ... ; then skip ; else create and push ; fi to gracefully handle concurrent workflows attempting to create the same tag.
.github/workflows/ci-cd.yml
Outdated
| run: | | ||
| tag=$(grep '^version:' CITATION.cff | sed 's/version: //') | ||
| # Generate PEP440 compliant date-based version tag | ||
| tag=$(python -c "from datetime import datetime, timezone; now = datetime.now(timezone.utc); print(f'{now.year}.{now.month}.{now.day}.{now.hour}{now.minute:02d}')") |
There was a problem hiding this comment.
The version format YYYY.M.D.HHmm produces inconsistent version strings that violate semantic versioning principles. For example, 2024.1.5.930 vs 2024.12.15.1430 - the single-digit month/day creates ambiguous and non-sortable versions.
Consider using zero-padded format: {now.year}.{now.month:02d}.{now.day:02d}.{now.hour:02d}{now.minute:02d} to produce versions like 2024.01.05.0930 for proper lexicographic sorting and consistency.
| tag=$(python -c "from datetime import datetime, timezone; now = datetime.now(timezone.utc); print(f'{now.year}.{now.month}.{now.day}.{now.hour}{now.minute:02d}')") | |
| tag=$(python -c "from datetime import datetime, timezone; now = datetime.now(timezone.utc); print(f'{now.year}.{now.month:02d}.{now.day:02d}.{now.hour:02d}{now.minute:02d}')") |
There was a problem hiding this comment.
Fixed in commit 7cf98ba. Updated to use zero-padded format: {now.year}.{now.month:02d}.{now.day:02d}.{now.hour:02d}{now.minute:02d} producing versions like 2025.01.05.0930 for proper lexicographic sorting.
.github/workflows/ci-cd.yml
Outdated
| with: | ||
| ref: ${{ github.ref }} |
There was a problem hiding this comment.
[nitpick] Using ref: ${{ github.ref }} is redundant - actions/checkout@v4 already checks out the ref that triggered the workflow by default. This explicit specification doesn't add value and could be removed for clarity.
| with: | |
| ref: ${{ github.ref }} |
There was a problem hiding this comment.
Fixed in commit 7cf98ba. Changed to ref: main which is more explicit about what we're checking out for testing on main branch pushes.
.github/workflows/ci-cd.yml
Outdated
| - name: Pull latest changes (including any formatting commits) | ||
| run: | | ||
| git pull origin main | ||
|
|
There was a problem hiding this comment.
The git pull fetches the latest state of main, which could include commits from other workflows or manual pushes that occurred after this workflow started. This means the version commit and tag might be created on a different commit than what was tested.
Consider removing this pull and relying on the initial checkout (lines 117-120), or add validation that the current HEAD matches the expected commit SHA before proceeding with versioning.
| - name: Pull latest changes (including any formatting commits) | |
| run: | | |
| git pull origin main | |
| # (Removed step: Pull latest changes) |
There was a problem hiding this comment.
Fixed in commit 7cf98ba. Removed the git pull from tag-release job. The workflow now operates on the tested commit from checkout, ensuring version and tag are created on the exact code that was tested.
.github/workflows/ci-cd.yml
Outdated
|
|
||
| - name: Check if tag already exists | ||
| run: | | ||
| if git rev-parse "${{ steps.get_tag.outputs.tag }}" >/dev/null 2>&1; then |
There was a problem hiding this comment.
Using git rev-parse to check for tag existence can produce false positives if a branch or commit SHA starts with the same characters as the tag. Use git tag -l "${{ steps.get_tag.outputs.tag }}" instead, which specifically checks for tag existence and returns empty string if the tag doesn't exist.
| if git rev-parse "${{ steps.get_tag.outputs.tag }}" >/dev/null 2>&1; then | |
| if [[ -n $(git tag -l "${{ steps.get_tag.outputs.tag }}") ]]; then |
There was a problem hiding this comment.
Fixed in commit 7cf98ba. Changed to git tag -l "${{ steps.get_tag.outputs.tag }}" which specifically checks for tag existence without the false positive risk.
Co-authored-by: rhoadesScholar <37990507+rhoadesScholar@users.noreply.github.com>
✅ CI-CD Workflow Update Complete
Successfully implemented full pre-commit process in the CI-CD workflow, eliminating manual version management and ensuring consistent releases.
Summary of Changes:
Latest Changes (review feedback):
YYYY.MM.DD.HHmm) for consistent lexicographic sortinggit rev-parsetogit tag -lfor more accurate tag existence checkmaindirectly instead of redundantref: ${{ github.ref }}git pullfrom tag-release job as we want to work with the tested commitWorkflow Execution:
Normal Push to Main:
Key Design Decision:
The tag now points to the actual code commit (including formatting), NOT the version bump commit. This means:
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.