From a40aaf912a0ea440162a6cb9d183982d01bf8e1c Mon Sep 17 00:00:00 2001 From: Lucas Burigo <281664+burigolucas@users.noreply.github.com> Date: Mon, 11 May 2026 15:36:01 +0200 Subject: [PATCH] chore(ci): update workflow to support manual releases --- .github/pull_request_template.md | 1 - .github/workflows/ci.yml | 60 +++- CONTRIBUTING.md | 483 ++++++++++++++++++++++++++++++- pyproject.toml | 2 +- 4 files changed, 529 insertions(+), 17 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2e7445f..eecc636 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -16,4 +16,3 @@ > Example: `feat: add role` - [ ] I have formatted my PR title correctly. -- [ ] I have added `[skip ci]` to the title (Maintainers only, if no release is needed). diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 806dbbc..1feb15b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,10 +14,21 @@ on: # Runs at 00:00 on the 1st and 15th of every month - cron: '0 0 1,15 * *' workflow_dispatch: + inputs: + trigger_release: + type: boolean + default: false + description: 'Create release PR' + auto_merge: + type: boolean + default: false + description: 'Enable auto-merge for release PR' concurrency: # Groups by PR number or branch ref. Unique enough to prevent collisions. - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{ github.event_name }} + # Releases get their own group and never cancel each other. + # Main branch workflows (including release publishes) never cancel each other. + group: ${{ github.event_name == 'workflow_dispatch' && inputs.trigger_release == true && format('release-{0}', github.workflow) || format('{0}-{1}-{2}', github.workflow, github.event.pull_request.number || github.ref, github.event_name) }} cancel-in-progress: ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }} jobs: @@ -25,12 +36,22 @@ jobs: setup: runs-on: ubuntu-latest if: | - (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) || - (github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != github.repository) || - (github.event_name != 'pull_request' && github.event_name != 'pull_request_target') + github.event_name == 'pull_request' || + github.event_name == 'pull_request_target' || + github.event_name == 'push' || + github.event_name == 'merge_group' || + github.event_name == 'schedule' || + github.event_name == 'workflow_dispatch' outputs: is_external: ${{ steps.check.outputs.external }} steps: + - name: Validate workflow_dispatch + if: github.event_name == 'workflow_dispatch' + run: | + if [[ "${{ github.ref }}" != "refs/heads/${{ github.event.repository.default_branch }}" ]]; then + echo "::error::workflow_dispatch can only run on main branch" + exit 1 + fi - id: check run: | if [[ "${{ github.event_name }}" == "pull_request_target" && "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]]; then @@ -48,25 +69,44 @@ jobs: steps: - run: echo "External PR approved by environment gate." - # Unified CI: Runs for internal events OR after external approval + # Release Gatekeeper: Only runs for workflow_dispatch with trigger_release. Requires manual approval. + release-approval: + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' && inputs.trigger_release == true + environment: release # Configure in Settings → Environments + steps: + - name: Audit release trigger + run: | + echo "::notice::Release triggered by ${{ github.actor }}" + echo "Event: ${{ github.event_name }}" + echo "Ref: ${{ github.ref }}" + echo "Inputs: trigger_release=${{ inputs.trigger_release }}, auto_merge=${{ inputs.auto_merge }}" + - run: echo "Release approved by environment gate" + + # Unified CI: Runs for internal events OR after external approval OR after release approval ci: - needs: [setup, external-approval] - # Run if it's NOT external, OR if it IS external and the approval job finished + needs: [setup, external-approval, release-approval] + # always() allows job to run even when dependencies are skipped, but we still require explicit success checks + # Run if setup succeeds AND (not external OR external-approval succeeds) AND (not release OR release-approval succeeds) if: | always() && + (needs.setup.result == 'success') && (needs.setup.outputs.is_external == 'false' || needs.external-approval.result == 'success') && - (needs.setup.result == 'success') - uses: redhat-cop/openshift_virtualization_migration_ci/.github/workflows/ci-ansible-collection.yml@main + (github.event_name != 'workflow_dispatch' || inputs.trigger_release != true || needs.release-approval.result == 'success') + uses: redhat-cop/openshift_virtualization_migration_ci/.github/workflows/ci-ansible-collection.yml@v2 permissions: contents: write packages: write security-events: write actions: read + pull-requests: write with: # Use head.sha for PRs to get the actual commit, otherwise fallback to global sha ref: ${{ github.event.pull_request.head.sha || github.sha }} repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - publish_to_automation_hub: false + trigger_release: ${{ inputs.trigger_release || false }} + auto_merge: ${{ inputs.auto_merge || false }} + publish_to_automation_hub: true secrets: automation_hub_token: ${{ secrets.AUTOMATION_HUB_TOKEN }} release_app_id: ${{ secrets.RELEASE_APP_ID }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f479c5..ab78e50 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,13 +1,486 @@ -# Welcome to the Ansible for OpenShift Virtualization Migration collection! +# Contributing to Ansible for OpenShift Virtualization Ops -Please check our [Contributor's Guide](https://github.com/redhat-cop/openshift-virtualization-migration-documentation/blob/main/CONTRIBUTING.md). +Thank you for your interest in contributing! Please check our [main documentation repository](https://github.com/redhat-cop/openshift-virtualization-migration-documentation) for comprehensive guides, deployment instructions, and additional resources. -The [documentation repository](https://github.com/redhat-cop/openshift-virtualization-migration-documentation) contains information on how to get started, links to other resources, deployment guides and more. +## Table of Contents -## Using Red Hat Developer Sandbox +1. [Getting Started](#getting-started) +2. [Pull Request Guidelines](#pull-request-guidelines) +3. [Commit Message Format](#commit-message-format) +4. [PR Title Requirements](#pr-title-requirements) +5. [Changelog Fragments](#changelog-fragments) +6. [Development Workflow](#development-workflow) +7. [Release Process (Maintainers Only)](#release-process-maintainers-only) -NOTE: As this repository is currently not public you will need to create a personal access token and add that to your Developer Sandbox profile. Please perform the steps in the contribution guide [Using Red Hat Developer Sandbox](https://github.com/redhat-cop/openshift-virtualization-migration-documentation/blob/main/CONTRIBUTING.md#contribute-using-red-hat-developer-sandbox) section. +## Getting Started + +### Using Red Hat Developer Sandbox + +Please perform the steps in the contribution guide [Using Red Hat Developer Sandbox](https://github.com/redhat-cop/openshift-virtualization-migration-documentation/blob/main/CONTRIBUTING.md#contribute-using-red-hat-developer-sandbox) section. Click the link below to launch your IDE hosted in the Developer Sandbox: [![Contribute](https://www.eclipse.org/che/contribute.svg)](https://workspaces.openshift.com/f?url=https://github.com/redhat-cop/openshift_virtualization_migration/openshift_virtualization_migration.git) + +## Pull Request Guidelines + +### External Contributors + +All pull requests from forks require manual approval from a maintainer before CI runs. This is a security measure to protect against malicious code execution. A maintainer will review and approve your PR, after which automated tests will run. + +### Internal Contributors + +Pull requests from branches within the main repository run CI automatically. + +### PR Checklist + +Before submitting your pull request: + +- [ ] PR title follows Conventional Commits format (see below) +- [ ] Changes are tested locally +- [ ] Documentation is updated if needed +- [ ] Changelog fragment added for significant changes (see below) +- [ ] All pre-commit hooks pass + +## Commit Message Format + +This project follows [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages and PR titles. This standard enables: + +- Automated changelog generation +- Semantic versioning +- Clear and scannable project history + +### Format + +``` +(): + +[optional body] + +[optional footer] +``` + +### Types + +- **feat**: A new feature (triggers minor version bump) +- **fix**: A bug fix (triggers patch version bump) +- **docs**: Documentation only changes +- **style**: Changes that don't affect code meaning (formatting, whitespace, etc.) +- **refactor**: Code change that neither fixes a bug nor adds a feature +- **perf**: Performance improvement +- **test**: Adding or correcting tests +- **chore**: Changes to build process or auxiliary tools +- **ci**: Changes to CI configuration files and scripts +- **build**: Changes that affect the build system or dependencies + +### Scope (Optional) + +The scope provides additional context about what part of the codebase is affected: + +- `roles`: Changes to Ansible roles +- `playbooks`: Changes to playbooks +- `modules`: Changes to modules +- `ci`: CI/CD changes +- `deps`: Dependency updates + +### Examples + +``` +feat(roles): add VM snapshot role +fix(vm_collect): handle missing configuration +docs: update installation instructions +chore(deps): update kubernetes.core to 5.3.0 +refactor(vm_backup_restore): simplify restore logic +test(vm_patching): add VM patching tests +``` + +### Breaking Changes + +For changes that break backward compatibility, add `!` after the type/scope or include `BREAKING CHANGE:` in the footer: + +``` +feat(api)!: remove deprecated endpoints + +BREAKING CHANGE: The /v1/legacy endpoint has been removed. Use /v2/resources instead. +``` + +Breaking changes trigger a major version bump. + +## PR Title Requirements + +**CRITICAL**: Your PR title MUST follow Conventional Commits format. When your PR is merged (via squash merge), the PR title becomes the commit message, which is used for: + +1. Generating the CHANGELOG +2. Determining the next version number +3. Creating release notes + +### Format Rules + +- Maximum length: **72 characters** +- Must match: `(): ` +- Type must be one of: feat, fix, docs, style, refactor, perf, test, chore, ci, build +- Description must be lowercase and concise + +### Examples + +✅ **GOOD**: +- `feat: add VM backup role` +- `fix(mtv_management): correct provider validation` +- `docs: update README with new requirements` +- `chore(deps): bump ansible-core to 2.20.0` + +❌ **BAD**: +- `Update files` (no type) +- `feat Add new feature` (missing colon) +- `FEAT: ADD NEW FEATURE` (wrong case) +- `feat: This is a very long PR title that exceeds the maximum character limit and will fail validation` (too long) + +### Validation + +A [GitHub Action](.github/workflows/pr-title-validation.yml) automatically validates PR titles. If your title doesn't match the format, the check will fail and you must update it before merging. + +## Changelog Fragments + +Changelog fragments are automatically generated based on the conventional commit messages. However, contributors may add additional information by crafting their own changelog fragment to help generate release notes. + +### When to Add Fragments [optional] + +Add a fragment for: +- New features (`feat`) +- Bug fixes (`fix`) +- Breaking changes +- Security fixes +- Significant refactoring +- Important documentation updates + +### When to Skip Fragments + +Small changes don't need fragments: +- Typo fixes +- CI configuration tweaks +- Minor README updates +- Code formatting +- Test-only changes + +### Creating Fragments + +Create a new YAML file in `changelogs/fragments/`: + +```bash +# Filename format: -.yml +cat > changelogs/fragments/123-add-vm-snapshot.yml < +molecule test + +# Run all pre-commit hooks +pre-commit run --all-files +``` + +### Pre-commit Hooks + +This repository uses pre-commit hooks for code quality. Install them to run checks automatically before each commit: + +```bash +pip install pre-commit +pre-commit install +``` + +The hooks will: +- Lint YAML files with yamllint +- Check for secrets with gitleaks +- Validate Ansible syntax with ansible-lint +- Check for merge conflict markers +- Detect broken symlinks +- Remove trailing whitespace + +If a hook fails, fix the issue and commit again. Some hooks can auto-fix issues (like trailing whitespace). + +### Making Changes + +1. Create a feature branch: `git checkout -b feat/my-feature` +2. Make your changes +3. Run tests locally +4. Commit with conventional commit messages +5. Push to your fork/branch +6. Create a pull request with a conventional commit title + +## Release Process (Maintainers Only) + +This section is for repository maintainers with release permissions. + +### Overview + +This project uses automated releases via GitHub Actions. The release workflow: + +1. Creates a release PR with updated version and CHANGELOG +2. Runs all tests on the release PR +3. Publishes to Ansible Galaxy and Red Hat Automation Hub when merged +4. Creates a GitHub release with collection artifacts + +### Prerequisites + +To trigger releases, you must have: + +- Access to the `release` GitHub Environment (configured in repository settings) +- Red Hat Automation Hub credentials (AUTOMATION_HUB_TOKEN secret) +- GitHub App credentials for releases (RELEASE_APP_ID, RELEASE_APP_PRIVATE_KEY secrets) + +### Version Determination + +The collection uses [Semantic Versioning (SemVer)](https://semver.org/): + +- **Major (X.0.0)**: Breaking changes (incompatible API changes) +- **Minor (0.X.0)**: New features (backward compatible) +- **Patch (0.0.X)**: Bug fixes (backward compatible) + +Version bumps are determined **automatically** from Conventional Commit messages in the changelog: + +- Commits with `BREAKING CHANGE:` or `!` → major bump +- Commits with `feat:` → minor bump +- Commits with `fix:` → patch bump + +### Triggering a Release + +#### Step 1: Verify Main Branch is Ready + +Before triggering a release: + +- [ ] All intended PRs for the release are merged to `main` +- [ ] CI is green on `main` branch +- [ ] No known critical bugs or regressions +- [ ] Documentation is up to date + +#### Step 2: Trigger Workflow + +1. Navigate to **Actions** → **CI** workflow +2. Click **Run workflow** (top right) +3. Configure inputs: + - **Branch**: `main` (required) + - **trigger_release**: ✅ Check this box + - **auto_merge**: ⬜ Leave unchecked (recommended for safety) +4. Click **Run workflow** + +#### Step 3: Approve Release (Environment Gate) + +1. The workflow will pause and wait for approval in the `release` environment +2. Navigate to the workflow run and click **Review deployments** +3. Review the approval request details: + - Who triggered the release + - What inputs were provided + - Current branch and commit +4. Approve or reject based on: + - Is `main` branch in a good state? + - Are all tests passing? + - Is this the right time for a release? +5. Click **Approve and deploy** if everything looks good + +#### Step 4: Review Release PR + +After approval, the workflow will: + +1. Calculate the new version based on commit messages +2. Generate updated CHANGELOG +3. Create a release PR titled `chore(release): X.Y.Z` + +Review the release PR carefully: + +- Check the version number is correct +- Review CHANGELOG entries for accuracy +- Ensure all expected changes are documented +- Verify CI passes on the release PR + +If the release PR has issues: +- Close the PR +- Fix issues on `main` +- Re-trigger the release workflow + +#### Step 5: Merge Release PR + +Once CI passes on the release PR: + +1. Review and approve the PR +2. Merge using **squash merge** (default) +3. The workflow automatically: + - Builds the collection artifact (.tar.gz) + - Publishes to Red Hat Automation Hub + - Publishes to Ansible Galaxy (if configured) + - Creates a GitHub release with the artifact attached + - Tags the release commit + +#### Auto-merge Option (Advanced) + +For experienced maintainers, you can enable auto-merge: + +1. When triggering the workflow, check **auto_merge: true** +2. The release PR will merge automatically if CI passes +3. **Use with caution** - you won't get a chance to review the PR before merge + +### Troubleshooting Releases + +#### Release PR Not Created + +**Symptoms**: Workflow runs but no PR appears + +**Solutions**: +- Check Actions logs for errors +- Verify you have permissions in the `release` environment +- Ensure `main` branch is up to date +- Check GitHub App credentials are configured + +#### Publishing Failed + +**Automation Hub**: +- Verify AUTOMATION_HUB_TOKEN secret is valid +- Check token has not expired +- Verify namespace and collection name match + +**Galaxy**: +- Verify galaxy.yml metadata is correct +- Check API token is configured + +#### Wrong Version Number + +**Symptoms**: Version calculated incorrectly + +**Cause**: Version is calculated from commit messages since last release + +**Solutions**: +1. Close the release PR +2. Manually update `galaxy.yml` version on `main` +3. Commit with message: `chore(release): X.Y.Z` +4. Re-run release workflow + +#### Tests Failing on Release PR + +**Solutions**: +1. Do NOT merge the release PR +2. Close the release PR +3. Fix the failing tests on `main` +4. Wait for CI to pass on `main` +5. Re-trigger the release workflow + +### Release Checklist + +Before triggering a release: + +- [ ] All planned PRs are merged to `main` +- [ ] CI is green on `main` branch +- [ ] No known critical bugs +- [ ] Documentation is current +- [ ] Dependencies are up to date +- [ ] Pre-commit hooks pass +- [ ] You have release environment access + +After triggering: + +- [ ] Review approval request details +- [ ] Approve release in GitHub environment +- [ ] Review release PR for correctness +- [ ] Verify CI passes on release PR +- [ ] Merge release PR +- [ ] Verify publication to Automation Hub +- [ ] Check GitHub release was created +- [ ] Announce release (if needed) + +### Release Schedule + +Releases are created **on-demand** by maintainers. Consider releasing when: + +- Critical bug fixes are merged +- Significant new features are ready +- Security fixes are available +- Monthly maintenance window (1st and 15th - when scheduled CI runs) +- User requests for specific fixes/features + +There is no fixed release schedule - releases happen when needed. + +### Security Considerations + +The release workflow includes multiple security controls: + +1. **Environment approval**: Only authorized maintainers can approve releases +2. **Branch restriction**: Releases can only be triggered from `main` branch +3. **Audit logging**: All release triggers are logged with actor information +4. **Secret protection**: Secrets are only exposed after approval +5. **Concurrency control**: Releases run sequentially, never in parallel + +Never share release credentials or bypass the approval process. + +## Questions? + +- **General questions**: Check the [documentation repository](https://github.com/redhat-cop/openshift-virtualization-migration-documentation) +- **Bug reports**: Open an issue with details and steps to reproduce +- **Feature requests**: Open an issue describing the use case +- **Security issues**: See CODE_OF_CONDUCT.md for reporting instructions + +Thank you for contributing to OpenShift Virtualization Ops! diff --git a/pyproject.toml b/pyproject.toml index 614d143..579c679 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ version_variables = [ assets = [] build_command = "./scripts/build.sh" build_command_env = [] -commit_message = "chore(release): {version} [skip ci]" +commit_message = "chore(release): {version}" commit_parser = "conventional" logging_use_named_masks = false major_on_zero = true