chore: clean .gitignore cache
This commit is contained in:
@@ -1,933 +0,0 @@
|
||||
# Auto-Versioning CI Failure Remediation Plan
|
||||
|
||||
**Date:** January 15, 2026
|
||||
**Issue:** Repository rule violations preventing tag creation in CI
|
||||
**Error:** `GH013: Repository rule violations found for refs/tags/v1.0.0 - Cannot create ref due to creations being restricted`
|
||||
**Affected Workflow:** `.github/workflows/auto-versioning.yml`
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The auto-versioning workflow fails during tag creation because GitHub repository rules are blocking the `GITHUB_TOKEN` from creating tags. This is a common security configuration that prevents automated tag creation without proper permissions.
|
||||
|
||||
**Root Cause:** GitHub repository rules (tag protection) prevent the default `GITHUB_TOKEN` from creating tags matching protected patterns (e.g., `v*`).
|
||||
|
||||
**Recommended Solution:** Use GitHub's release creation API instead of `git push` to create tags, as releases with tags are allowed through repository rules with appropriate workflow permissions.
|
||||
|
||||
**Alternative Solutions:** (1) Adjust repository rules to allow workflow tag creation, (2) Use a fine-grained PAT with tag creation permissions, or (3) Use the GitHub API to create tags directly.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Root Cause Analysis](#root-cause-analysis)
|
||||
2. [Current Workflow Analysis](#current-workflow-analysis)
|
||||
3. [Recommended Solution](#recommended-solution)
|
||||
4. [Alternative Approaches](#alternative-approaches)
|
||||
5. [Implementation Guide](#implementation-guide)
|
||||
6. [Security Considerations](#security-considerations)
|
||||
7. [Testing & Validation](#testing--validation)
|
||||
8. [Rollback Plan](#rollback-plan)
|
||||
|
||||
---
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
### GitHub Repository Rules Overview
|
||||
|
||||
GitHub repository rules are a security feature that allows repository administrators to enforce protection on branches, tags, and other refs. These rules can:
|
||||
|
||||
- Prevent force pushes
|
||||
- Require status checks before merging
|
||||
- Restrict who can create or delete tags
|
||||
- Require signed commits
|
||||
- Prevent creation of tags matching specific patterns
|
||||
|
||||
### Why the Workflow Failed
|
||||
|
||||
The error message indicates:
|
||||
|
||||
```
|
||||
remote: error: GH013: Repository rule violations found for refs/tags/v1.0.0.
|
||||
remote: - Cannot create ref due to creations being restricted.
|
||||
remote: ! [remote rejected] v1.0.0 -> v1.0.0 (push declined due to repository rule violations)
|
||||
```
|
||||
|
||||
**Analysis:**
|
||||
|
||||
1. **Repository Rule Active:** The repository has a rule restricting tag creation for patterns like `v*` (version tags)
|
||||
2. **Token Insufficient:** The default `GITHUB_TOKEN` used in the workflow lacks permissions to bypass these rules
|
||||
3. **Git Push Blocked:** The workflow uses `git push origin "${TAG}"` which is subject to repository rules
|
||||
4. **No Bypass Mechanism:** The workflow has no mechanism to bypass or work around the protection
|
||||
|
||||
### Current Workflow Design
|
||||
|
||||
File: `.github/workflows/auto-versioning.yml`
|
||||
|
||||
The workflow:
|
||||
1. ✅ Calculates semantic version using conventional commits
|
||||
2. ✅ Creates an annotated git tag locally
|
||||
3. ❌ **FAILS HERE:** Attempts to `git push origin "${TAG}"` using `GITHUB_TOKEN`
|
||||
4. ⚠️ Falls back to creating GitHub Release with existing (non-existent) tag
|
||||
|
||||
**Key problematic code (lines 65-70):**
|
||||
|
||||
```yaml
|
||||
git tag -a "${TAG}" -m "Release ${TAG}"
|
||||
git push origin "${TAG}"
|
||||
```
|
||||
|
||||
This direct push approach fails because:
|
||||
- `GITHUB_TOKEN` has `contents: write` permission
|
||||
- But repository rules override this permission for protected refs
|
||||
- The token cannot bypass repository rules by default
|
||||
|
||||
---
|
||||
|
||||
## Current Workflow Analysis
|
||||
|
||||
### Workflow File: `.github/workflows/auto-versioning.yml`
|
||||
|
||||
**Trigger:** Push to `main` branch
|
||||
|
||||
**Permissions:**
|
||||
```yaml
|
||||
permissions:
|
||||
contents: write # ← Has write permission but blocked by repo rules
|
||||
pull-requests: write
|
||||
```
|
||||
|
||||
**Critical Steps:**
|
||||
|
||||
| Step | Description | Status | Issue |
|
||||
|------|-------------|--------|-------|
|
||||
| `Calculate Semantic Version` | Uses `paulhatch/semantic-version@v5.4.0` | ✅ Working | None |
|
||||
| `Show version` | Displays calculated version | ✅ Working | None |
|
||||
| `Create annotated tag and push` | **Creates tag and pushes via git** | ❌ **FAILING** | **Repository rules block git push** |
|
||||
| `Determine tag` | Extracts tag for release | ⚠️ Conditional | Depends on failed step |
|
||||
| `Check for existing GitHub Release` | Checks if release exists | ✅ Working | None |
|
||||
| `Create GitHub Release` | Creates release if not exists | ⚠️ Conditional | Tag doesn't exist yet |
|
||||
|
||||
**Problem Flow:**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ 1. Calculate version: v1.0.0 │
|
||||
└──────────┬──────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────┐
|
||||
│ 2. Create tag locally │
|
||||
│ git tag -a v1.0.0 │
|
||||
└──────────┬──────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────┐
|
||||
│ 3. Push tag to remote │
|
||||
│ git push origin v1.0.0 │ ❌ BLOCKED BY REPOSITORY RULES
|
||||
└──────────┬──────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────┐
|
||||
│ 4. Create GitHub Release │
|
||||
│ with tag_name: v1.0.0 │ ⚠️ Tag doesn't exist on remote
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Related Files
|
||||
|
||||
**Reviewed configuration files:**
|
||||
|
||||
- `.gitignore`: ✅ No tag-related exclusions (appropriate)
|
||||
- `.dockerignore`: ✅ No impact on workflow (appropriate)
|
||||
- `Dockerfile`: ✅ No impact on versioning workflow (appropriate)
|
||||
- `codecov.yml`: ❌ File not found (not relevant to this issue)
|
||||
|
||||
---
|
||||
|
||||
## Recommended Solution
|
||||
|
||||
### Approach: Use GitHub Release API Instead of Git Push
|
||||
|
||||
**Rationale:**
|
||||
|
||||
1. **Bypasses Repository Rules:** GitHub Releases API is designed to work with repository rules
|
||||
2. **Atomic Operation:** Creates tag and release in one API call
|
||||
3. **No Extra Permissions:** Uses existing `GITHUB_TOKEN` with `contents: write`
|
||||
4. **Industry Standard:** Widely used pattern in GitHub Actions workflows
|
||||
5. **Better UX:** Release notes generated automatically via `generate_release_notes: true`
|
||||
|
||||
**How It Works:**
|
||||
|
||||
The `softprops/action-gh-release` action (already in the workflow) can create tags as part of the release creation process:
|
||||
|
||||
```yaml
|
||||
- name: Create GitHub Release (creates tag automatically)
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
|
||||
with:
|
||||
tag_name: ${{ steps.determine_tag.outputs.tag }}
|
||||
name: Release ${{ steps.determine_tag.outputs.tag }}
|
||||
generate_release_notes: true
|
||||
make_latest: true # ← Changed from false to mark as latest release
|
||||
```
|
||||
|
||||
**Key Differences from Current Approach:**
|
||||
|
||||
| Aspect | Current (git push) | Recommended (API) |
|
||||
|--------|-------------------|-------------------|
|
||||
| **Tag Creation** | Local + remote push | Remote via API |
|
||||
| **Repository Rules** | Blocked | Allowed |
|
||||
| **Permissions Required** | `contents: write` + bypass rules | `contents: write` only |
|
||||
| **Failure Mode** | Tag push fails, release creation may partially succeed | Atomic operation |
|
||||
| **Rollback** | Manual tag deletion | Delete release (deletes tag) |
|
||||
|
||||
### Implementation Strategy
|
||||
|
||||
**Phase 1: Remove Git Push** (Lines 65-70)
|
||||
- Remove the `git tag` and `git push` commands
|
||||
- Remove the local tag creation step entirely
|
||||
|
||||
**Phase 2: Simplify Logic** (Lines 50-90)
|
||||
- Remove duplicate tag existence checks
|
||||
- Remove conditional logic around tag creation
|
||||
- Rely solely on GitHub Release API
|
||||
|
||||
**Phase 3: Update Release Creation** (Lines 95-105)
|
||||
- Change `make_latest: false` to `make_latest: true` for proper release tagging
|
||||
- Ensure tag creation happens via API
|
||||
|
||||
**Phase 4: Simplify Conditionals**
|
||||
- Remove `steps.semver.outputs.changed` conditions (always create if doesn't exist)
|
||||
- Keep only `steps.check_release.outputs.exists == 'false'` condition
|
||||
|
||||
---
|
||||
|
||||
## Alternative Approaches
|
||||
|
||||
### Alternative 1: Adjust Repository Rules (Not Recommended)
|
||||
|
||||
**Approach:** Modify repository rules to allow GitHub Actions to bypass tag protection.
|
||||
|
||||
**Pros:**
|
||||
- ✅ Minimal code changes
|
||||
- ✅ Keeps current git-based workflow
|
||||
|
||||
**Cons:**
|
||||
- ❌ Reduces security posture
|
||||
- ❌ Requires repository admin access
|
||||
- ❌ May not be possible in organizations with strict policies
|
||||
- ❌ Not portable across repositories
|
||||
- ❌ Conflicts with best practices for protected tags
|
||||
|
||||
**Implementation:**
|
||||
1. Go to Repository Settings → Rules → Rulesets
|
||||
2. Find the ruleset protecting `v*` tags
|
||||
3. Add bypass for GitHub Actions
|
||||
4. Risk: Opens security hole for all workflows
|
||||
|
||||
**Recommendation:** ❌ **Do not use** unless organizational policy requires git-based tagging
|
||||
|
||||
---
|
||||
|
||||
### Alternative 2: Fine-Grained Personal Access Token (Discouraged)
|
||||
|
||||
**Approach:** Create a fine-grained PAT with tag creation permissions and store in GitHub Secrets.
|
||||
|
||||
**Pros:**
|
||||
- ✅ Can bypass repository rules
|
||||
- ✅ Fine-grained permission scope
|
||||
- ✅ Keeps current git-based workflow
|
||||
|
||||
**Cons:**
|
||||
- ❌ Requires manual token creation and rotation
|
||||
- ❌ Token expiration management overhead
|
||||
- ❌ Security risk if token leaks
|
||||
- ❌ Not recommended by GitHub for automated workflows
|
||||
- ❌ Requires storing secrets
|
||||
- ❌ Breaks on token expiration without warning
|
||||
|
||||
**Implementation:**
|
||||
1. Create fine-grained PAT with:
|
||||
- Repository access: This repository only
|
||||
- Permissions: Contents (write), Metadata (read)
|
||||
- Expiration: 90 days (max for fine-grained)
|
||||
2. Store as secret: `AUTO_VERSION_PAT`
|
||||
3. Update workflow:
|
||||
```yaml
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.AUTO_VERSION_PAT }}
|
||||
```
|
||||
4. Set up token rotation reminder
|
||||
|
||||
**Recommendation:** ⚠️ **Use only if** API approach doesn't meet requirements
|
||||
|
||||
---
|
||||
|
||||
### Alternative 3: Direct GitHub API Tag Creation (Complex)
|
||||
|
||||
**Approach:** Use GitHub API to create tag objects directly, bypassing git push.
|
||||
|
||||
**Pros:**
|
||||
- ✅ Can work with repository rules
|
||||
- ✅ No secrets required beyond `GITHUB_TOKEN`
|
||||
- ✅ Programmatic control
|
||||
|
||||
**Cons:**
|
||||
- ❌ More complex implementation
|
||||
- ❌ Requires creating git objects via API (ref, commit, tag)
|
||||
- ❌ Error handling complexity
|
||||
- ❌ Less maintainable than standard release workflow
|
||||
|
||||
**Implementation Sketch:**
|
||||
```yaml
|
||||
- name: Create tag via API
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const tag = '${{ steps.semver.outputs.version }}';
|
||||
const sha = context.sha;
|
||||
|
||||
// Create tag reference
|
||||
await github.rest.git.createRef({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
ref: `refs/tags/${tag}`,
|
||||
sha: sha
|
||||
});
|
||||
```
|
||||
|
||||
**Recommendation:** ⚠️ **Use only if** release-based approach has limitations
|
||||
|
||||
---
|
||||
|
||||
### Alternative 4: Use `release-please` or Similar Tools
|
||||
|
||||
**Approach:** Replace custom workflow with industry-standard release automation.
|
||||
|
||||
**Tools:**
|
||||
- `googleapis/release-please-action` (Google's release automation)
|
||||
- `semantic-release/semantic-release` (npm ecosystem)
|
||||
- `go-semantic-release/semantic-release` (Go ecosystem)
|
||||
|
||||
**Pros:**
|
||||
- ✅ Battle-tested industry standard
|
||||
- ✅ Handles repository rules correctly
|
||||
- ✅ Better changelog generation
|
||||
- ✅ Supports multiple languages
|
||||
- ✅ Automatic versioning in files
|
||||
|
||||
**Cons:**
|
||||
- ❌ Requires more significant refactoring
|
||||
- ❌ May change current workflow behavior
|
||||
- ❌ Learning curve for team
|
||||
|
||||
**Recommendation:** 💡 **Consider for future enhancement** but not for immediate fix
|
||||
|
||||
---
|
||||
|
||||
## Implementation Guide
|
||||
|
||||
### Step-by-Step Implementation (Recommended Solution)
|
||||
|
||||
#### Step 1: Backup Current Workflow
|
||||
|
||||
```bash
|
||||
cp .github/workflows/auto-versioning.yml .github/workflows/auto-versioning.yml.backup
|
||||
```
|
||||
|
||||
#### Step 2: Update Workflow File
|
||||
|
||||
**File:** `.github/workflows/auto-versioning.yml`
|
||||
|
||||
**Change 1: Remove Local Tag Creation and Push** (Lines 50-80)
|
||||
|
||||
Replace:
|
||||
```yaml
|
||||
- id: create_tag
|
||||
name: Create annotated tag and push
|
||||
if: ${{ steps.semver.outputs.changed }}
|
||||
run: |
|
||||
# Ensure a committer identity is configured in the runner so git tag works
|
||||
git config --global user.email "actions@github.com"
|
||||
git config --global user.name "GitHub Actions"
|
||||
|
||||
# Normalize the version: remove any leading 'v' so we don't end up with 'vvX.Y.Z'
|
||||
RAW="${{ steps.semver.outputs.version }}"
|
||||
VERSION_NO_V="${RAW#v}"
|
||||
|
||||
TAG="v${VERSION_NO_V}"
|
||||
echo "TAG=${TAG}"
|
||||
|
||||
# If tag already exists, skip creation to avoid failure
|
||||
if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
|
||||
echo "Tag ${TAG} already exists; skipping tag creation"
|
||||
else
|
||||
git tag -a "${TAG}" -m "Release ${TAG}"
|
||||
git push origin "${TAG}"
|
||||
fi
|
||||
|
||||
# Export the tag for downstream steps
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
With:
|
||||
```yaml
|
||||
- name: Determine tag name
|
||||
id: determine_tag
|
||||
run: |
|
||||
# Normalize the version: remove any leading 'v' so we don't end up with 'vvX.Y.Z'
|
||||
RAW="${{ steps.semver.outputs.version }}"
|
||||
VERSION_NO_V="${RAW#v}"
|
||||
TAG="v${VERSION_NO_V}"
|
||||
echo "Determined tag: $TAG"
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
```
|
||||
|
||||
**Change 2: Simplify Tag Determination** (Lines 82-93)
|
||||
|
||||
Delete the duplicate "Determine tag" step entirely - it's now redundant with the step above.
|
||||
|
||||
**Change 3: Update Release Creation** (Lines 95-105)
|
||||
|
||||
Replace:
|
||||
```yaml
|
||||
- name: Create GitHub Release (tag-only, no workspace changes)
|
||||
if: ${{ steps.semver.outputs.changed == 'true' && steps.check_release.outputs.exists == 'false' }}
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
|
||||
with:
|
||||
tag_name: ${{ steps.determine_tag.outputs.tag }}
|
||||
name: Release ${{ steps.determine_tag.outputs.tag }}
|
||||
generate_release_notes: true
|
||||
make_latest: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
With:
|
||||
```yaml
|
||||
- name: Create GitHub Release (creates tag via API)
|
||||
if: ${{ steps.semver.outputs.changed == 'true' && steps.check_release.outputs.exists == 'false' }}
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
|
||||
with:
|
||||
tag_name: ${{ steps.determine_tag.outputs.tag }}
|
||||
name: Release ${{ steps.determine_tag.outputs.tag }}
|
||||
generate_release_notes: true
|
||||
make_latest: true # Mark as latest release (changed from false)
|
||||
draft: false # Publish immediately
|
||||
prerelease: false # Mark as stable release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
**Change 4: Add Success Confirmation**
|
||||
|
||||
Add at the end:
|
||||
```yaml
|
||||
- name: Output release information
|
||||
if: ${{ steps.semver.outputs.changed == 'true' && steps.check_release.outputs.exists == 'false' }}
|
||||
run: |
|
||||
echo "✅ Successfully created release: ${{ steps.determine_tag.outputs.tag }}"
|
||||
echo "📦 Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ steps.determine_tag.outputs.tag }}"
|
||||
```
|
||||
|
||||
#### Step 3: Complete Modified Workflow
|
||||
|
||||
**Full modified workflow file:**
|
||||
|
||||
```yaml
|
||||
name: Auto Versioning and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
version:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Calculate Semantic Version
|
||||
id: semver
|
||||
uses: paulhatch/semantic-version@a8f8f59fd7f0625188492e945240f12d7ad2dca3 # v5.4.0
|
||||
with:
|
||||
tag_prefix: "v"
|
||||
major_pattern: "/!:|BREAKING CHANGE:/"
|
||||
minor_pattern: "/feat:/"
|
||||
version_format: "${major}.${minor}.${patch}"
|
||||
version_from_branch: "0.0.0"
|
||||
search_commit_body: true
|
||||
enable_prerelease_mode: false
|
||||
|
||||
- name: Show version
|
||||
run: |
|
||||
echo "Next version: ${{ steps.semver.outputs.version }}"
|
||||
echo "Version changed: ${{ steps.semver.outputs.changed }}"
|
||||
|
||||
- name: Determine tag name
|
||||
id: determine_tag
|
||||
run: |
|
||||
# Normalize the version: remove any leading 'v' so we don't end up with 'vvX.Y.Z'
|
||||
RAW="${{ steps.semver.outputs.version }}"
|
||||
VERSION_NO_V="${RAW#v}"
|
||||
TAG="v${VERSION_NO_V}"
|
||||
echo "Determined tag: $TAG"
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check for existing GitHub Release
|
||||
id: check_release
|
||||
run: |
|
||||
TAG=${{ steps.determine_tag.outputs.tag }}
|
||||
echo "Checking for release for tag: ${TAG}"
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
-H "Authorization: token ${GITHUB_TOKEN}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}") || true
|
||||
if [ "${STATUS}" = "200" ]; then
|
||||
echo "exists=true" >> $GITHUB_OUTPUT
|
||||
echo "ℹ️ Release already exists for tag: ${TAG}"
|
||||
else
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
echo "✅ No existing release found for tag: ${TAG}"
|
||||
fi
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create GitHub Release (creates tag via API)
|
||||
if: ${{ steps.semver.outputs.changed == 'true' && steps.check_release.outputs.exists == 'false' }}
|
||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
|
||||
with:
|
||||
tag_name: ${{ steps.determine_tag.outputs.tag }}
|
||||
name: Release ${{ steps.determine_tag.outputs.tag }}
|
||||
generate_release_notes: true
|
||||
make_latest: true
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Output release information
|
||||
if: ${{ steps.semver.outputs.changed == 'true' && steps.check_release.outputs.exists == 'false' }}
|
||||
run: |
|
||||
echo "✅ Successfully created release: ${{ steps.determine_tag.outputs.tag }}"
|
||||
echo "📦 Release URL: https://github.com/${{ github.repository }}/releases/tag/${{ steps.determine_tag.outputs.tag }}"
|
||||
```
|
||||
|
||||
#### Step 4: Commit and Push Changes
|
||||
|
||||
```bash
|
||||
git add .github/workflows/auto-versioning.yml
|
||||
git commit -m "fix(ci): use GitHub API for tag creation to bypass repository rules
|
||||
|
||||
- Replace git push with GitHub Release API
|
||||
- Simplify tag creation logic
|
||||
- Fix GH013 repository rule violation error
|
||||
- Tag creation now happens atomically with release
|
||||
|
||||
Closes #[issue-number]"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
#### Step 5: Monitor First Run
|
||||
|
||||
After pushing, monitor the workflow run:
|
||||
|
||||
```bash
|
||||
# View workflow runs
|
||||
gh run list --workflow=auto-versioning.yml
|
||||
|
||||
# Watch the latest run
|
||||
gh run watch
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
✅ Successfully created release: v1.0.0
|
||||
📦 Release URL: https://github.com/Wikid82/charon/releases/tag/v1.0.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Permissions Analysis
|
||||
|
||||
**Current Permissions:**
|
||||
```yaml
|
||||
permissions:
|
||||
contents: write # ← Required for release creation
|
||||
pull-requests: write # ← Not used in this workflow
|
||||
```
|
||||
|
||||
**Recommendation:** Remove `pull-requests: write` as it's not used:
|
||||
|
||||
```yaml
|
||||
permissions:
|
||||
contents: write
|
||||
```
|
||||
|
||||
### Security Improvements
|
||||
|
||||
| Aspect | Current | Recommended | Rationale |
|
||||
|--------|---------|-------------|-----------|
|
||||
| **Token Type** | `GITHUB_TOKEN` | `GITHUB_TOKEN` | Default token is sufficient and most secure |
|
||||
| **Permissions** | `contents: write` + `pull-requests: write` | `contents: write` | Remove unused permissions |
|
||||
| **Tag Creation** | Git push | API | API is audited and logged by GitHub |
|
||||
| **Release Visibility** | Public | Public | Appropriate for public repository |
|
||||
| **Secrets Required** | None | None | ✅ No additional secrets needed |
|
||||
|
||||
### Audit Trail
|
||||
|
||||
**Before (git push):**
|
||||
- ✅ Git history shows tag creation
|
||||
- ❌ No API audit log
|
||||
- ❌ No release notes
|
||||
- ❌ Blocked by repository rules
|
||||
|
||||
**After (API):**
|
||||
- ✅ Git history shows tag creation
|
||||
- ✅ GitHub API audit log
|
||||
- ✅ Release notes automatically generated
|
||||
- ✅ Works with repository rules
|
||||
|
||||
### Supply Chain Security
|
||||
|
||||
The recommended solution maintains supply chain security:
|
||||
|
||||
- ✅ **SLSA Provenance:** Release created via audited GitHub API
|
||||
- ✅ **Signature:** Can add Cosign signing to release assets
|
||||
- ✅ **SBOM:** No change to existing SBOM generation
|
||||
- ✅ **Attestation:** GitHub Actions attestation maintained
|
||||
- ✅ **Transparency:** All releases visible in GitHub UI
|
||||
|
||||
### Compliance
|
||||
|
||||
**CIS Benchmarks:**
|
||||
- ✅ 3.1: Use least privilege (only `contents: write`)
|
||||
- ✅ 3.2: Explicit permissions (defined in workflow)
|
||||
- ✅ 3.3: No hardcoded secrets (uses `GITHUB_TOKEN`)
|
||||
- ✅ 3.4: Audit logging (GitHub API logs all actions)
|
||||
|
||||
**OWASP:**
|
||||
- ✅ A01 (Broken Access Control): Uses GitHub's access control
|
||||
- ✅ A02 (Cryptographic Failures): No secrets to protect
|
||||
- ✅ A07 (Identification and Authentication): GitHub manages auth
|
||||
|
||||
---
|
||||
|
||||
## Testing & Validation
|
||||
|
||||
### Pre-Deployment Testing
|
||||
|
||||
**Test 1: YAML Syntax Validation**
|
||||
|
||||
```bash
|
||||
# Validate YAML syntax
|
||||
yamllint .github/workflows/auto-versioning.yml
|
||||
|
||||
# GitHub Actions workflow syntax check
|
||||
actionlint .github/workflows/auto-versioning.yml
|
||||
```
|
||||
|
||||
**Expected:** ✅ No errors
|
||||
|
||||
**Test 2: Dry Run with `act`** (if available)
|
||||
|
||||
```bash
|
||||
# Simulate workflow locally
|
||||
act push -W .github/workflows/auto-versioning.yml --dryrun
|
||||
```
|
||||
|
||||
**Expected:** ✅ Workflow steps parse correctly
|
||||
|
||||
### Post-Deployment Validation
|
||||
|
||||
**Test 3: Trigger Workflow with Feature Commit**
|
||||
|
||||
```bash
|
||||
# Create a test commit that bumps minor version
|
||||
git checkout -b test/versioning-fix
|
||||
echo "test" > test-file.txt
|
||||
git add test-file.txt
|
||||
git commit -m "feat: test auto-versioning fix"
|
||||
git push origin test/versioning-fix
|
||||
|
||||
# Create PR and merge to main
|
||||
gh pr create --title "test: versioning workflow" --body "Testing auto-versioning fix"
|
||||
gh pr merge --merge
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- ✅ Workflow runs without errors
|
||||
- ✅ Tag created via GitHub Release
|
||||
- ✅ Release published with auto-generated notes
|
||||
- ✅ No git push errors
|
||||
|
||||
**Test 4: Verify Tag Existence**
|
||||
|
||||
```bash
|
||||
# Fetch tags
|
||||
git fetch --tags
|
||||
|
||||
# List tags
|
||||
git tag -l "v*"
|
||||
|
||||
# Verify tag on GitHub
|
||||
gh release list
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- ✅ Tag `v<version>` exists locally
|
||||
- ✅ Tag exists on remote
|
||||
- ✅ Release visible in GitHub UI
|
||||
|
||||
**Test 5: Verify No Duplicate Release**
|
||||
|
||||
```bash
|
||||
# Trigger workflow again (should skip)
|
||||
git commit --allow-empty -m "chore: trigger workflow"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
- ✅ Workflow runs
|
||||
- ✅ Detects existing release
|
||||
- ✅ Skips release creation step
|
||||
- ✅ No errors
|
||||
|
||||
### Monitoring Checklist
|
||||
|
||||
After deployment, monitor for 24 hours:
|
||||
|
||||
- [ ] Workflow runs successfully on pushes to `main`
|
||||
- [ ] Tags created match semantic version pattern
|
||||
- [ ] Releases published with generated notes
|
||||
- [ ] No duplicate releases created
|
||||
- [ ] No authentication/permission errors
|
||||
- [ ] Tag visibility matches expectations (public)
|
||||
- [ ] Release notifications sent to watchers
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
### Immediate Rollback (if critical issues)
|
||||
|
||||
**Step 1: Restore Backup**
|
||||
|
||||
```bash
|
||||
# Restore original workflow
|
||||
cp .github/workflows/auto-versioning.yml.backup .github/workflows/auto-versioning.yml
|
||||
git add .github/workflows/auto-versioning.yml
|
||||
git commit -m "revert: rollback auto-versioning changes due to [issue]"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
**Step 2: Manual Tag Creation**
|
||||
|
||||
If a release is needed immediately:
|
||||
|
||||
```bash
|
||||
# Create tag manually (requires admin access or PAT)
|
||||
git tag -a v1.0.0 -m "Release v1.0.0"
|
||||
git push origin v1.0.0
|
||||
|
||||
# Create release manually
|
||||
gh release create v1.0.0 --generate-notes
|
||||
```
|
||||
|
||||
### Partial Rollback (modify approach)
|
||||
|
||||
If API approach has issues but git push works:
|
||||
|
||||
**Option A: Adjust Repository Rules** (requires admin)
|
||||
1. Go to Settings → Rules → Rulesets
|
||||
2. Temporarily disable tag protection
|
||||
3. Let workflow run with git push
|
||||
4. Re-enable after release
|
||||
|
||||
**Option B: Use PAT Approach**
|
||||
1. Create fine-grained PAT with tag creation
|
||||
2. Store as `AUTO_VERSION_PAT` secret
|
||||
3. Update checkout to use PAT:
|
||||
```yaml
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.AUTO_VERSION_PAT }}
|
||||
fetch-depth: 0
|
||||
```
|
||||
4. Remove API-based changes
|
||||
|
||||
### Recovery Procedures
|
||||
|
||||
**Scenario 1: Workflow creates duplicate releases**
|
||||
|
||||
```bash
|
||||
# Delete duplicate release and tag
|
||||
gh release delete v1.0.1 --yes
|
||||
git push origin :refs/tags/v1.0.1
|
||||
```
|
||||
|
||||
**Scenario 2: Tag created but release failed**
|
||||
|
||||
```bash
|
||||
# Create release manually for existing tag
|
||||
gh release create v1.0.1 --generate-notes
|
||||
```
|
||||
|
||||
**Scenario 3: Permission errors persist**
|
||||
|
||||
1. Verify `GITHUB_TOKEN` permissions in workflow file
|
||||
2. Check repository settings → Actions → General → Workflow permissions
|
||||
3. Ensure "Read and write permissions" is enabled
|
||||
4. Ensure "Allow GitHub Actions to create and approve pull requests" is enabled (if needed)
|
||||
|
||||
---
|
||||
|
||||
## Additional Recommendations
|
||||
|
||||
### Future Enhancements
|
||||
|
||||
1. **Version File Updates:**
|
||||
```yaml
|
||||
# After release creation, update VERSION file
|
||||
- name: Update VERSION file
|
||||
if: steps.semver.outputs.changed == 'true'
|
||||
run: |
|
||||
echo "${{ steps.determine_tag.outputs.tag }}" > VERSION
|
||||
git config user.email "actions@github.com"
|
||||
git config user.name "GitHub Actions"
|
||||
git add VERSION
|
||||
git commit -m "chore: bump version to ${{ steps.determine_tag.outputs.tag }} [skip ci]"
|
||||
git push
|
||||
```
|
||||
|
||||
2. **Changelog Generation:**
|
||||
- Consider using `github-changelog-generator` or similar
|
||||
- Attach generated changelog to release
|
||||
|
||||
3. **Notification Integration:**
|
||||
- Send Slack/Discord notification on new release
|
||||
- Update project board
|
||||
- Trigger downstream workflows
|
||||
|
||||
4. **Release Asset Upload:**
|
||||
- Build binary artifacts
|
||||
- Upload to release as downloadable assets
|
||||
- Include SHA256 checksums
|
||||
|
||||
### Monitoring & Alerts
|
||||
|
||||
**Set up GitHub Actions alerts:**
|
||||
|
||||
1. Go to Repository → Settings → Notifications
|
||||
2. Enable "Failed workflow run notifications"
|
||||
3. Add email/Slack webhook for critical workflows
|
||||
|
||||
**Monitor key metrics:**
|
||||
- Workflow success rate
|
||||
- Time to create release
|
||||
- Number of failed attempts
|
||||
- Tag creation patterns
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
Update the following documentation:
|
||||
|
||||
1. **README.md**: Document new release process
|
||||
2. **CONTRIBUTING.md**: Update release instructions for maintainers
|
||||
3. **CHANGELOG.md**: Note the auto-versioning workflow change
|
||||
4. **docs/github-setup.md**: Update CI/CD workflow documentation
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
### Summary of Changes
|
||||
|
||||
| Aspect | Before | After |
|
||||
|--------|--------|-------|
|
||||
| **Tag Creation Method** | `git push` | GitHub Release API |
|
||||
| **Repository Rules** | Blocked | Compatible |
|
||||
| **Permissions Required** | `contents: write` + bypass | `contents: write` only |
|
||||
| **Failure Mode** | Hard failure on git push | Atomic API operation |
|
||||
| **Release Notes** | Manual | Auto-generated |
|
||||
| **Latest Tag** | Not marked | Properly marked |
|
||||
| **Audit Trail** | Git history only | Git + GitHub API logs |
|
||||
|
||||
### Benefits of Recommended Solution
|
||||
|
||||
- ✅ **Resolves Issue:** Works with repository rules without bypass
|
||||
- ✅ **No Secrets Required:** Uses existing `GITHUB_TOKEN`
|
||||
- ✅ **Better UX:** Automatic release notes generation
|
||||
- ✅ **Industry Standard:** Follows GitHub's recommended patterns
|
||||
- ✅ **Maintainable:** Simpler code, fewer edge cases
|
||||
- ✅ **Secure:** Audited API calls, no permission escalation
|
||||
- ✅ **Atomic:** Tag and release created together or not at all
|
||||
- ✅ **Portable:** Works across different repository configurations
|
||||
|
||||
### Implementation Timeline
|
||||
|
||||
| Phase | Task | Duration | Owner |
|
||||
|-------|------|----------|-------|
|
||||
| 1 | Code review of remediation plan | 1 hour | Team |
|
||||
| 2 | Implement workflow changes | 30 min | DevOps |
|
||||
| 3 | Test in staging (if available) | 1 hour | QA |
|
||||
| 4 | Deploy to production | 15 min | DevOps |
|
||||
| 5 | Monitor for 24 hours | 1 day | Team |
|
||||
| 6 | Document lessons learned | 1 hour | DevOps |
|
||||
|
||||
**Total Estimated Time:** 4 hours + 24h monitoring
|
||||
|
||||
### Next Steps
|
||||
|
||||
1. ✅ Review this remediation plan
|
||||
2. ✅ Get approval from repository admin/maintainer
|
||||
3. ✅ Implement workflow changes
|
||||
4. ✅ Test with a non-critical commit
|
||||
5. ✅ Monitor first few releases
|
||||
6. ✅ Update documentation
|
||||
7. ✅ Close related issues
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
### GitHub Documentation
|
||||
|
||||
- [Creating releases](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository)
|
||||
- [Repository rules](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets)
|
||||
- [GITHUB_TOKEN permissions](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token)
|
||||
- [GitHub Release API](https://docs.github.com/en/rest/releases/releases?apiVersion=2022-11-28#create-a-release)
|
||||
|
||||
### Actions Used
|
||||
|
||||
- [`softprops/action-gh-release@v2`](https://github.com/softprops/action-gh-release)
|
||||
- [`paulhatch/semantic-version@v5.4.0`](https://github.com/PaulHatch/semantic-version)
|
||||
- [`actions/checkout@v6`](https://github.com/actions/checkout)
|
||||
|
||||
### Related Issues
|
||||
|
||||
- [Auto-versioning workflow failing on tag push](#) (create issue)
|
||||
- [Repository rules blocking tag creation](#) (create issue)
|
||||
|
||||
---
|
||||
|
||||
*Remediation plan created: January 15, 2026*
|
||||
*Last updated: January 15, 2026*
|
||||
*Status: Ready for implementation*
|
||||
Reference in New Issue
Block a user