Web Analytics

enterprise-approval-engine

⭐ 4 stars English by jamengual

Enterprise Approval Engine

en es

Enterprise-grade GitHub Action for policy-based approval workflows with per-group thresholds (X of N), OR logic between groups, and automatic semver tag creation.

Features

📋 See it in action: Example approval issue

Table of Contents

Quick Start

1. Create Configuration

Create .github/approvals.yml in your repository:

version: 1

policies: dev-team: approvers: [alice, bob, charlie] min_approvals: 2

platform-team: approvers: [team:platform-engineers] require_all: true

workflows: production-deploy: require: # OR logic: either path satisfies approval

  • policy: dev-team # 2 of 3 developers
  • policy: platform-team # ALL platform engineers
on_approved: create_tag: true close_issue: true

2. Request Approval Workflow

Create .github/workflows/request-approval.yml:

name: Request Deployment Approval

on: workflow_dispatch: inputs: version: description: 'Version to deploy (e.g., v1.2.3)' required: true type: string

jobs: request: runs-on: ubuntu-latest steps:

  • uses: actions/checkout@v4
  • uses: jamengual/enterprise-approval-engine@v1
id: approval with: action: request workflow: production-deploy version: ${{ inputs.version }} token: ${{ secrets.GITHUB_TOKEN }}

  • name: Output Results
run: | echo "Issue: ${{ steps.approval.outputs.issue_url }}" echo "Status: ${{ steps.approval.outputs.status }}"

3. Handle Approval Comments

Create .github/workflows/handle-approval.yml:

name: Handle Approval Comments

on: issue_comment: types: [created]

jobs: process: if: contains(github.event.issue.labels.*.name, 'approval-required') runs-on: ubuntu-latest steps:

  • uses: actions/checkout@v4
  • uses: jamengual/enterprise-approval-engine@v1
id: process with: action: process-comment issue_number: ${{ github.event.issue.number }} token: ${{ secrets.GITHUB_TOKEN }}

  • name: Trigger Deployment
if: steps.process.outputs.status == 'approved' run: | echo "Approved by: ${{ steps.process.outputs.approvers }}" echo "Tag created: ${{ steps.process.outputs.tag }}"


Action Reference

Actions

The action supports four operation modes via the action input:

| Action | Description | When to Use | |--------|-------------|-------------| | request | Create a new approval request issue | When starting a deployment/release workflow | | process-comment | Process an approval/denial comment | On issue_comment events | | check | Check the current approval status | To poll for approval completion | | close-issue | Handle issue close events | On issues: [closed] events |

Inputs

#### Core Inputs

| Input | Description | Required | Default | |-------|-------------|----------|---------| | action | Action to perform: request, check, process-comment, close-issue | Yes | - | | workflow | Workflow name from config (for request action) | For request | - | | version | Semver version for tag creation (e.g., 1.2.3 or v1.2.3) | No | - | | issue_number | Issue number (for check, process-comment, close-issue) | For check/process/close | - | | token | GitHub token for API operations | Yes | - |

#### Configuration Inputs

| Input | Description | Required | Default | |-------|-------------|----------|---------| | config_path | Path to approvals.yml config file | No | .github/approvals.yml | | config_repo | External repository for shared config (e.g., org/.github) | No | - |

#### Polling Inputs (for check action)

| Input | Description | Required | Default | |-------|-------------|----------|---------| | wait | Wait for approval (polling) instead of returning immediately | No | false | | timeout | Timeout for waiting (e.g., 24h, 1h30m, 30m) | No | 72h |

#### Team Support Inputs

| Input | Description | Required | Default | |-------|-------------|----------|---------| | app_id | GitHub App ID for team membership checks | No | - | | app_private_key | GitHub App private key for team membership checks | No | - |

#### Jira Integration Inputs

| Input | Description | Required | Default | |-------|-------------|----------|---------| | jira_base_url | Jira Cloud base URL (e.g., https://yourcompany.atlassian.net) | No | - | | jira_user_email | Jira user email for API authentication | No | - | | jira_api_token | Jira API token for authentication | No | - | | jira_update_fix_version | Update Jira issues with Fix Version on approval | No | true | | include_jira_issues | Include Jira issues in approval request body | No | true |

#### Deployment Tracking Inputs

| Input | Description | Required | Default | |-------|-------------|----------|---------| | create_deployment | Create GitHub deployment for tracking | No | true | | deployment_environment | Target environment (e.g., production, staging) | No | production | | deployment_environment_url | URL to the deployed environment | No | - |

#### Other Inputs

| Input | Description | Required | Default | |-------|-------------|----------|---------| | issue_action | Issue event action for close-issue (closed, reopened) | No | - | | previous_tag | Previous tag to compare commits against (auto-detected if not specified) | No | - |

Outputs

#### Core Outputs

| Output | Description | Available For | |--------|-------------|---------------| | status | Approval status: pending, approved, denied, timeout, tag_deleted, skipped | All actions | | issue_number | Created or checked issue number | All actions | | issue_url | URL to the approval issue | All actions |

#### Approval Outputs

| Output | Description | Available For | |--------|-------------|---------------| | approvers | Comma-separated list of users who approved | process-comment, check | | denier | User who denied the request | process-comment, check | | satisfied_group | Name of the group that satisfied approval | process-comment, check | | tag | Created tag name | process-comment (on approval) | | tag_deleted | Tag that was deleted | close-issue |

#### Jira Outputs

| Output | Description | Available For | |--------|-------------|---------------| | jira_issues | Comma-separated list of Jira issue keys in this release | request | | jira_issues_json | JSON array of Jira issue details (key, summary, type, status) | request |

#### Deployment Outputs

| Output | Description | Available For | |--------|-------------|---------------| | deployment_id | GitHub deployment ID for status updates | request | | deployment_url | URL to the deployment in GitHub | request |

#### Release Notes Outputs

| Output | Description | Available For | |--------|-------------|---------------| | release_notes | Auto-generated release notes from commits and Jira issues | request | | commits_count | Number of commits in this release | request |


Configuration Reference

Policies

Policies define reusable groups of approvers. There are two formats:

#### Simple Format

policies:
  # Threshold-based: X of N must approve
  dev-team:
    approvers: [alice, bob, charlie]
    min_approvals: 2

# All must approve (AND logic) security: approvers: [team:security, security-lead] require_all: true

# Mixed teams and individuals production: approvers:

  • team:sre
  • tech-lead
  • product-owner
min_approvals: 2

#### Advanced Format (per-source thresholds)

For complex requirements like "2 from platform AND 1 from security":

policies:
  # Complex AND gate
  production-gate:
    from:
      
  • team: platform-engineers
min_approvals: 2 # 2 of the platform team
  • team: security
min_approvals: 1 # 1 of the security team
  • user: alice # alice must also approve
logic: and # ALL sources must be satisfied

# Flexible OR gate flexible-review: from:

  • team: security
require_all: true # All security team
  • team: platform
min_approvals: 2 # OR 2 platform members logic: or # ANY source is enough

# Executive approval: any one exec exec-approval: from:

  • user: ceo
  • user: cto
  • user: vp-engineering
logic: or

# User list with threshold leads: from:

  • users: [tech-lead, product-lead, design-lead]
min_approvals: 2

Source types:

Policy-level logic:

#### Inline Logic (mix AND/OR)

For complex expressions, use logic: on each source to specify how it connects to the next:

policies:
  # (2 security AND 2 platform) OR alice
  complex-gate:
    from:
      
  • team: security
min_approvals: 2 logic: and # AND with next source
  • team: platform
min_approvals: 2 logic: or # OR with next source
  • user: alice # alice alone can satisfy
# (security AND platform) OR (alice AND bob) OR manager multi-path: from:
  • team: security
min_approvals: 1 logic: and
  • team: platform
min_approvals: 1 logic: or # End first AND group
  • user: alice
logic: and
  • user: bob
logic: or # End second AND group
  • user: manager # Third path

Operator precedence: AND binds tighter than OR (standard boolean logic).

The expression A and B or C and D is evaluated as (A AND B) OR (C AND D).

Workflows

Workflows define approval requirements and actions:

workflows:
  my-workflow:
    description: "Optional description"

# Trigger conditions (for filtering) trigger: environment: production

# Approval requirements (OR logic between items) require:

  • policy: dev-team
  • policy: security
# Or inline approvers:
  • approvers: [alice, bob]
require_all: true

# Issue configuration issue: title: "Approval: {{version}}" body: | # Inline custom template (optional) ## My Custom Approval Issue Version: {{.Version}} Requested by: @{{.Requestor}} {{.GroupsTable}} body_file: "templates/my-template.md" # Or load from file labels: [production, deploy] assignees_from_policy: true

# Actions on approval on_approved: create_tag: true tag_prefix: "v" # Creates v1.2.3 close_issue: true comment: "Approved! Tag {{version}} created."

# Actions on denial on_denied: close_issue: true comment: "Denied by {{denier}}."

# Actions when issue is manually closed on_closed: delete_tag: true # Delete the tag if issue is closed comment: "Deployment cancelled. Tag {{tag}} deleted."

Tagging Configuration

Control how tags are created per workflow:

workflows:
  dev-deploy:
    require:
      
  • policy: dev-team
on_approved: tagging: enabled: true start_version: "0.1.0" # No 'v' prefix, start at 0.1.0 auto_increment: patch # Auto-bump: 0.1.0 -> 0.1.1 -> 0.1.2 env_prefix: "dev-" # Creates: dev-0.1.0, dev-0.1.1

staging-deploy: require:

  • policy: qa-team
on_approved: tagging: enabled: true start_version: "v1.0.0" # 'v' prefix (inferred from start_version) auto_increment: minor # v1.0.0 -> v1.1.0 -> v1.2.0 env_prefix: "staging-" # Creates: staging-v1.0.0

production-deploy: require:

  • policy: prod-team
on_approved: tagging: enabled: true start_version: "v1.0.0" # Manual version required (no auto_increment)

Tagging options:

| Option | Description | |--------|-------------| | enabled | Enable tag creation | | start_version | Starting version and format (e.g., "v1.0.0" or "1.0.0") | | prefix | Version prefix (inferred from start_version if not set) | | auto_increment | Auto-bump: major, minor, patch, or omit for manual | | env_prefix | Environment prefix (e.g., "dev-" creates "dev-v1.0.0") |

Custom Issue Templates

You can fully customize the issue body using Go templates. Use body for inline templates or body_file to load from a file.

Available template variables:

| Variable | Description | |----------|-------------| | {{.Title}} | Issue title | | {{.Description}} | Workflow description | | {{.Version}} | Semver version | | {{.Requestor}} | GitHub username who requested | | {{.Environment}} | Environment name | | {{.RunURL}} | Link to workflow run | | {{.RepoURL}} | Repository URL | | {{.CommitSHA}} | Full commit SHA | | {{.CommitURL}} | Link to commit | | {{.Branch}} | Branch name | | {{.GroupsTable}} | Pre-rendered approval status table | | {{.Timestamp}} | Request timestamp | | {{.PreviousVersion}} | Previous version/tag | | {{.CommitsCount}} | Number of commits in this release | | {{.HasJiraIssues}} | Boolean - whether Jira issues exist | | {{.JiraIssues}} | Array of Jira issue data | | {{.JiraIssuesTable}} | Pre-rendered Jira issues table | | {{.PipelineTable}} | Pre-rendered deployment pipeline table | | {{.PipelineMermaid}} | Pre-rendered Mermaid flowchart diagram | | {{.Vars.key}} | Custom variables |

Template functions:

| Function | Example | Description | |----------|---------|-------------| | slice | {{slice .CommitSHA 0 7}} | Substring (short SHA) | | title | {{.Environment \| title}} | Title case | | upper | {{.Version \| upper}} | Uppercase | | lower | {{.Version \| lower}} | Lowercase | | join | {{join .Groups ","}} | Join array | | contains | {{if contains .Branch "feature"}} | Check substring | | replace | {{replace .Version "v" ""}} | Replace string | | default | {{default "N/A" .Environment}} | Default value |

Example custom template file (.github/templates/deploy.md):

## {{.Title}}

Release Information

  • Version: {{.Version}}
  • Requested by: @{{.Requestor}}
{{- if .CommitSHA}} {{- end}} {{- if .CommitsCount}}
  • Changes: {{.CommitsCount}} commits since {{.PreviousVersion}}
{{- end}}

{{if .HasJiraIssues}}

Jira Issues

{{.JiraIssuesTable}} {{end}}

Approval Status

{{.GroupsTable}}


Approve: Comment approve | Deny: Comment deny

Defaults

Global defaults that apply to all workflows:

defaults:
  timeout: 72h                    # Default approval timeout
  allow_self_approval: false      # Whether requestors can approve their own requests
  issue_labels:                   # Labels added to all approval issues
    
  • approval-required

Semver

Configure version handling:

semver:
  prefix: "v"              # Tag prefix (v1.2.3)
  strategy: input          # Use version from input
  validate: true           # Validate semver format
  allow_prerelease: true   # Allow prerelease versions (e.g., v1.0.0-beta.1)
  auto:                    # Label-based auto-increment (when strategy: auto)
    major_labels: [breaking, major]
    minor_labels: [feature, minor]
    patch_labels: [fix, patch, bug]


Complete Configuration Reference

This section documents every configuration option available in approvals.yml.

Top-Level Structure

version: 1                    # Required: config version (always 1)
defaults: { ... }             # Optional: global defaults
policies: { ... }             # Required: reusable approval policies
workflows: { ... }            # Required: approval workflows
semver: { ... }               # Optional: version handling settings

defaults Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | timeout | duration | 72h | Timeout for blocking check action with wait: true. Use hours (e.g., 168h for 1 week). Not needed for event-driven workflows. | | allow_self_approval | bool | false | Whether the requestor can approve their own request | | issue_labels | string[] | [] | Labels added to all approval issues |

policies. Options (Simple Format)

| Key | Type | Default | Description | |-----|------|---------|-------------| | approvers | string[] | - | List of usernames or team:slug references | | min_approvals | int | 0 | Number of approvals required (0 = use require_all) | | require_all | bool | false | If true, ALL approvers must approve |

policies. Options (Advanced Format)

| Key | Type | Default | Description | |-----|------|---------|-------------| | from | source[] | - | List of approver sources with individual thresholds | | logic | string | "and" | How to combine sources: "and" or "or" |

Approver Source Options (from[]):

| Key | Type | Default | Description | |-----|------|---------|-------------| | team | string | - | Team slug (e.g., "platform" or "org/platform") | | user | string | - | Single username | | users | string[] | - | List of usernames | | min_approvals | int | 1 | Approvals required from this source | | require_all | bool | false | All from this source must approve | | logic | string | - | Logic to next source: "and" or "or" |

workflows. Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | description | string | - | Human-readable description | | trigger | map | - | Trigger conditions (for filtering) | | require | requirement[] | - | Required: Approval requirements (OR logic between items) | | issue | object | - | Issue creation settings | | on_approved | object | - | Actions when approved | | on_denied | object | - | Actions when denied | | on_closed | object | - | Actions when issue is manually closed | | pipeline | object | - | Progressive deployment pipeline config |

workflows..require[] Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | policy | string | - | Reference to a defined policy | | approvers | string[] | - | Inline approvers (alternative to policy) | | min_approvals | int | - | Override policy's min_approvals | | require_all | bool | - | Override policy's require_all |

workflows..issue Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | title | string | "Approval Required: {workflow}" | Issue title (supports {{version}}, {{environment}}, {{workflow}}) | | body | string | - | Custom issue body template (Go template syntax) | | body_file | string | - | Path to template file (relative to .github/) | | labels | string[] | [] | Additional labels for this workflow | | assignees_from_policy | bool | false | Auto-assign individual users from policies (max 10) |

workflows..on_approved Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | create_tag | bool | false | Create a git tag (uses input version) | | close_issue | bool | false | Close the issue after approval | | comment | string | - | Comment to post (supports {{version}}, {{satisfied_group}}) | | tagging | object | - | Advanced tagging configuration |

workflows..on_approved.tagging Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | enabled | bool | false | Enable tag creation | | start_version | string | "0.0.0" | Initial version (e.g., "v1.0.0" or "1.0.0") | | prefix | string | (inferred) | Version prefix (inferred from start_version) | | auto_increment | string | - | Auto-bump: "major", "minor", "patch", or omit for manual | | env_prefix | string | - | Environment prefix (e.g., "dev-" creates "dev-v1.0.0") |

workflows..on_denied Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | close_issue | bool | false | Close the issue after denial | | comment | string | - | Comment to post (supports {{denier}}) |

workflows..on_closed Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | delete_tag | bool | false | Delete the associated tag when issue is closed | | comment | string | - | Comment to post (supports {{tag}}, {{version}}) |

workflows..pipeline Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | stages | stage[] | - | Required: Ordered list of deployment stages | | track_prs | bool | false | Include merged PRs in issue body | | track_commits | bool | false | Include commits in issue body | | compare_from_tag | string | - | Tag pattern to compare from (e.g., "v*") | | show_mermaid_diagram | bool | true | Show visual Mermaid flowchart of pipeline stages | | release_strategy | object | - | Release candidate selection strategy |

workflows..pipeline.stages[] Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | name | string | - | Required: Stage name (e.g., "dev", "prod") | | environment | string | - | GitHub environment name | | policy | string | - | Approval policy for this stage | | approvers | string[] | - | Inline approvers (alternative to policy) | | on_approved | string | - | Comment to post when stage is approved | | create_tag | bool | false | Create a git tag at this stage | | is_final | bool | false | Close issue after this stage | | auto_approve | bool | false | Automatically approve without human intervention |

workflows..pipeline.release_strategy Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | type | string | "tag" | Strategy: "tag", "branch", "label", "milestone" | | branch | object | - | Branch strategy settings | | label | object | - | Label strategy settings | | milestone | object | - | Milestone strategy settings | | auto_create | object | - | Auto-create next release artifact |

release_strategy.branch Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | pattern | string | "release/{{version}}" | Branch naming pattern | | base_branch | string | "main" | Branch to compare against | | delete_after_release | bool | false | Delete branch after prod deployment |

release_strategy.label Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | pattern | string | "release:{{version}}" | Label naming pattern | | pending_label | string | - | Label for PRs awaiting release assignment | | remove_after_release | bool | false | Remove labels after prod deployment |

release_strategy.milestone Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | pattern | string | "v{{version}}" | Milestone naming pattern | | close_after_release | bool | false | Close milestone after prod deployment |

release_strategy.auto_create Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | enabled | bool | false | Enable auto-creation on final stage completion | | next_version | string | "patch" | Version increment: "patch", "minor", "major" | | create_issue | bool | false | Create new approval issue for next release | | comment | string | - | Comment to post about next release |

semver Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | prefix | string | "v" | Tag prefix | | strategy | string | "input" | Version strategy: "input", "auto" | | validate | bool | false | Validate semver format | | allow_prerelease | bool | false | Allow prerelease versions (e.g., v1.0.0-beta.1) | | auto | object | - | Label-based auto-increment settings |

semver.auto Options

| Key | Type | Default | Description | |-----|------|---------|-------------| | major_labels | string[] | [] | PR labels that trigger major bump | | minor_labels | string[] | [] | PR labels that trigger minor bump | | patch_labels | string[] | [] | PR labels that trigger patch bump |


Feature Details

Approval Keywords

Users can approve or deny requests by commenting on the issue:

Approval keywords: approve, approved, lgtm, yes, /approve

Denial keywords: deny, denied, reject, rejected, no, /deny

Team Support

To use GitHub team-based approvers, you need elevated permissions. The standard GITHUB_TOKEN cannot list team members. Use a GitHub App token:

jobs:
  process:
    runs-on: ubuntu-latest
    steps:
      
  • uses: actions/checkout@v4
# Generate GitHub App token
  • uses: actions/create-github-app-token@v2
id: app-token with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }}

# Use the app token for team membership checks

  • uses: jamengual/enterprise-approval-engine@v1
with: action: process-comment issue_number: ${{ github.event.issue.number }} token: ${{ steps.app-token.outputs.token }}

Required GitHub App permissions:

Progressive Deployment Pipelines

Track deployments through multiple environments with a single approval issue. As each stage is approved, the issue updates to show progress and automatically advances to the next stage.

#### Pipeline Configuration

# .github/approvals.yml or external config
version: 1

policies: developers: approvers: [dev1, dev2, dev3] min_approvals: 1

qa-team: approvers: [qa1, qa2] min_approvals: 1

tech-leads: approvers: [lead1, lead2] min_approvals: 1

production-approvers: approvers: [sre1, sre2, security-lead] require_all: true

workflows: deploy: description: "Deploy through all environments (dev → qa → stage → prod)" require:

  • policy: developers # Initial approval to start pipeline
pipeline: track_prs: true # Include PRs in the issue body track_commits: true # Include commits in the issue body stages:
  • name: dev
environment: development policy: developers on_approved: "✅ DEV deployment approved! Proceeding to QA..."
  • name: qa
environment: qa policy: qa-team on_approved: "✅ QA deployment approved! Proceeding to STAGING..."
  • name: stage
environment: staging policy: tech-leads on_approved: "✅ STAGING deployment approved! Ready for PRODUCTION..."
  • name: prod
environment: production policy: production-approvers on_approved: "🚀 PRODUCTION deployment complete!" create_tag: true # Create tag when PROD is approved is_final: true # Close issue after this stage on_approved: close_issue: true comment: | 🎉 Deployment Complete!

Version {{version}} has been deployed to all environments.

#### Pipeline Workflow Example

# .github/workflows/request-pipeline.yml
name: Request Pipeline Deployment

on: workflow_dispatch: inputs: version: description: 'Version to deploy' required: true type: string

permissions: contents: write issues: write pull-requests: read # Required for PR tracking

jobs: request: runs-on: ubuntu-latest steps:

  • uses: actions/checkout@v4
with: fetch-depth: 0 # Needed for commit/PR comparison

  • uses: jamengual/enterprise-approval-engine@v1
id: approval with: action: request workflow: deploy version: ${{ inputs.version }} token: ${{ secrets.GITHUB_TOKEN }}

  • name: Output Results
run: | echo "## Pipeline Deployment Started" >> $GITHUB_STEP_SUMMARY echo "- Issue: #${{ steps.approval.outputs.issue_number }}" >> $GITHUB_STEP_SUMMARY echo "- URL: ${{ steps.approval.outputs.issue_url }}" >> $GITHUB_STEP_SUMMARY

#### How It Works

## 🚀 Deployment Pipeline: v1.2.0

Pipeline Flow

​``mermaid flowchart LR DEV(⏳ DEV) QA(⬜ QA) STAGE(⬜ STAGE) PROD(⬜ PROD) DEV --> QA --> STAGE --> PROD

classDef completed fill:#28a745,stroke:#1e7e34,color:#fff classDef current fill:#ffc107,stroke:#d39e00,color:#000 classDef pending fill:#6c757d,stroke:#545b62,color:#fff class DEV current class QA,STAGE,PROD pending ​`

Deployment Progress

| Stage | Status | Approver | Time | |-------|--------|----------|------| | DEV | ⏳ Awaiting | - | - | | QA | ⬜ Pending | - | - | | STAGE | ⬜ Pending | - | - | | PROD | ⬜ Pending | - | - |

Current Stage: DEV

The Mermaid diagram provides an at-a-glance view with color-coded nodes:

To disable the Mermaid diagram, set show_mermaid_diagram: false in the pipeline config.

| Stage | Status | Approver | Time |
|-------|--------|----------|------|
| DEV | ✅ Deployed | @developer1 | Dec 9 10:30 |
| QA | ✅ Deployed | @qa-lead | Dec 9 14:15 |
| STAGE | ⏳ Awaiting | - | - |
| PROD | ⬜ Pending | - | - |

Current Stage: STAGE

  • PR and Commit Tracking: Release managers see exactly what's being deployed:
### Pull Requests in this Release

| PR | Title | Author | |----|-------|--------| | #42 | Add user authentication | @alice | | #45 | Fix payment processing bug | @bob |

Commits

  • Completion: When the final stage is approved:
  • Tag is created (if create_tag: true)
  • Completion comment is posted
  • Issue is automatically closed
  • #### Pipeline Stage Options

    | Option | Description | |--------|-------------| | name | Stage name (displayed in table) | | environment | GitHub environment name | | policy | Approval policy for this stage | | approvers | Inline approvers (alternative to policy) | | on_approved | Message to post when stage is approved | | create_tag | Create a git tag at this stage | | is_final | Close the issue after this stage | | auto_approve | Automatically approve this stage without human intervention | | approval_mode | Override workflow approval mode for this stage |

    #### Approval Modes

    Choose how approvers interact with approval requests:

    | Mode | Description | |------|-------------| | comments | (Default) Approvers comment /approve or approve on the issue | | sub_issues | Creates a sub-issue for each stage - close to approve | | hybrid | Mix modes per stage - use approval_mode on each stage |

    Sub-Issue Approval Example:

    workflows:
      deploy:
        approval_mode: sub_issues
        sub_issue_settings:
          title_template: "⏳ Approve: {{stage}} for {{version}}"  # Changes to ✅ when approved
          labels: [approval-stage]
          protection:
            only_assignee_can_close: true   # Prevents unauthorized approvals
            prevent_parent_close: true       # Parent can't close until all approved
        pipeline:
          stages:
            
    • name: dev
    policy: developers
    • name: prod
    policy: production-approvers
    With sub-issues, the parent issue shows a table of approval sub-issues:

    ### 📋 Approval Sub-Issues

    | Stage | Sub-Issue | Status | Assignees | |-------|-----------|--------|----------| | DEV | #124 | ⏳ Awaiting | @alice, @bob | | PROD | #125 | ⏳ Awaiting | @sre1, @sre2 |

    Hybrid Mode (per-stage override):

    workflows:
      deploy:
        approval_mode: comments  # Default for this workflow
        pipeline:
          stages:
            
    • name: dev
    policy: developers # Uses comments (workflow default)
    • name: prod
    policy: production-approvers approval_mode: sub_issues # Override for production only

    #### Enhanced Comments UX

    The action includes enhanced comment-based approval UX:

    • Emoji Reactions: Automatic reactions on approval comments
    • 👍 Approved
    • 👎 Denied
    • 👀 Seen (processing)
    • Quick Actions Section: Issue body includes a command reference table:
    ### ⚡ Quick Actions

    | Action | Command | Description | |--------|---------|-------------| | ✅ Approve | /approve | Approve the DEV stage | | ❌ Deny | /deny [reason] | Deny with optional reason | | 📊 Status | /status | Show current approval status |

    Configure via comment_settings:

    workflows:
      deploy:
        comment_settings:
          react_to_comments: true     # Add emoji reactions (default: true)
          show_quick_actions: true    # Show Quick Actions section (default: true)

    #### Auto-Approve for Lower Environments

    Use auto_approve: true on pipeline stages that should be automatically approved without human intervention. This is ideal for lower environments like dev or integration where you want to speed up the pipeline while still maintaining approval gates for production.

    Example with auto-approve:

    workflows:
      deploy:
        description: "Deploy through environments"
        pipeline:
          stages:
            
    • name: dev
    environment: development auto_approve: true # Automatically approved on_approved: "🤖 DEV auto-deployed"
    • name: integration
    environment: integration auto_approve: true # Automatically approved on_approved: "🤖 INTEGRATION auto-deployed"
    • name: staging
    environment: staging policy: qa-team # Requires manual approval on_approved: "✅ STAGING approved"
    • name: production
    environment: production policy: production-approvers # Requires manual approval create_tag: true is_final: true

    How it works:

    Use cases:

    • Development environments: Deploy immediately without waiting for approval
    • Integration testing: Let CI/CD pipeline progress automatically through test environments
    • Canary deployments: Auto-approve canary stage, require approval for full rollout
    #### Pipeline Config Options

    | Option | Default | Description | |--------|---------|-------------| | track_prs | false | Include merged PRs in the issue body | | track_commits | false | Include commits since last tag | | compare_from_tag | - | Custom tag pattern to compare from | | show_mermaid_diagram | true | Show visual Mermaid flowchart of pipeline stages |

    Note: PR tracking requires pull-requests: read permission in your workflow.

    Release Candidate Strategies

    In enterprise environments, PRs merged to main aren't always immediate release candidates. The approval engine supports three strategies for selecting which PRs belong to a release:

    #### Strategy Types

    | Strategy | Description | Use Case | |----------|-------------|----------| | tag | PRs between two git tags (default) | Simple releases, trunk-based development | | branch | PRs merged to a release branch | GitFlow, release branches | | label | PRs with a specific release label | Flexible selection, batched releases | | milestone | PRs assigned to a GitHub milestone | Roadmap-aligned releases |

    #### Configuration

    # .github/approvals.yml
    workflows:
      deploy:
        description: "Production deployment pipeline"
        pipeline:
          track_prs: true
          track_commits: true

    # Configure release selection strategy release_strategy: type: milestone # or: tag, branch, label

    # Milestone strategy settings milestone: pattern: "v{{version}}" # e.g., "v1.2.0" close_after_release: true # Close milestone on prod completion

    # Auto-create next release artifact on completion auto_create: enabled: true next_version: patch # or: minor, major create_issue: true # Create new approval issue

    stages:

    • name: dev
    policy: developers
    • name: prod
    policy: production-approvers is_final: true

    #### Branch Strategy

    Use release branches for GitFlow-style development:

    release_strategy:
      type: branch
      branch:
        pattern: "release/{{version}}"  # Creates release/v1.2.0
        base_branch: main               # Compare against main
        delete_after_release: true      # Cleanup after prod deploy

    auto_create: enabled: true next_version: minor

    How it works:

    #### Label Strategy

    Use labels for flexible PR selection:

    release_strategy:
      type: label
      label:
        pattern: "release:{{version}}"      # e.g., "release:v1.2.0"
        pending_label: "pending-release"    # Applied to merged PRs awaiting release
        remove_after_release: true          # Remove label after prod deploy

    auto_create: enabled: true next_version: patch

    How it works:

    #### Milestone Strategy

    Use milestones for roadmap-aligned releases:

    release_strategy:
      type: milestone
      milestone:
        pattern: "Release {{version}}"       # e.g., "Release 1.2.0"
        close_after_release: true            # Close milestone on completion

    auto_create: enabled: true next_version: minor create_issue: true # Auto-create next approval issue

    How it works:

    • Create milestone: "Release 1.2.0"
    • Assign PRs to the milestone during development
    • Request approval for v1.2.0
    • Approval issue shows all PRs in the milestone
    • After prod, milestone is closed and next milestone is created
    #### Auto-Creation on Completion

    When the final stage (prod) is approved, automatically prepare for the next release:

    auto_create:
      enabled: true
      next_version: patch      # Calculate next: patch, minor, or major
      create_issue: true       # Create new approval issue immediately
      comment: |               # Custom message (optional)
        🚀 Next release prepared: {{version}}

    This creates:

    • Branch strategy: New release branch from main
    • Label strategy: New release label
    • Milestone strategy: New milestone
    #### Cleanup Options

    Each strategy has optional cleanup actions that run when the final stage (prod) is approved. All cleanup options default to false - cleanup is opt-in:

    | Strategy | Cleanup Option | Description | |----------|----------------|-------------| | Branch | delete_after_release | Delete the release branch | | Label | remove_after_release | Remove release labels from PRs | | Milestone | close_after_release | Close the milestone |

    release_strategy:
      type: branch
      branch:
        pattern: "release/{{version}}"
        delete_after_release: false   # Keep branch for reference (default)

    type: milestone milestone: pattern: "v{{version}}" close_after_release: true # Close milestone when done

    #### Hotfix Deployments

    For emergency hotfixes that need to bypass normal release workflows, create a separate workflow:

    # .github/approvals.yml
    workflows:
      # Standard releases - full pipeline with milestone tracking
      deploy:
        description: "Standard release pipeline (dev → qa → stage → prod)"
        pipeline:
          release_strategy:
            type: milestone
            milestone:
              pattern: "v{{version}}"
              close_after_release: true
            auto_create:
              enabled: true
              next_version: minor
          stages:
            
    • name: dev
    policy: developers
    • name: qa
    policy: qa-team
    • name: stage
    policy: tech-leads
    • name: prod
    policy: production-approvers is_final: true

    # Hotfixes - skip stages, direct to prod hotfix: description: "Emergency hotfix - direct to production" pipeline: release_strategy: type: tag # Simple tag-based, no cleanup needed # No auto_create - hotfixes are one-off stages:

    • name: prod
    policy: production-approvers create_tag: true is_final: true on_approved: close_issue: true comment: "🚨 Hotfix {{version}} deployed to production"

    Trigger hotfix vs regular release:

    # Regular release - goes through all stages
    gh workflow run request-approval.yml -f workflow_name=deploy -f version=v1.3.0

    Hotfix - goes straight to prod

    gh workflow run request-approval.yml -f workflow_name=hotfix -f version=v1.2.1

    Hotfix patterns:

    | Scenario | Strategy | Cleanup | Auto-Create | |----------|----------|---------|-------------| | Emergency fix | tag | None | Disabled | | Patch release | milestone | close_after_release: false | Disabled | | Multiple hotfixes | branch | delete_after_release: false | Disabled |

    #### Release Strategy Benefits

    | Strategy | Pros | Cons | |----------|------|------| | Tag | Simple, no extra workflow | All merged PRs included | | Branch | Clear release scope, isolation | Branch management overhead | | Label | Flexible selection, easy to change | Manual labeling required | | Milestone | Roadmap visibility, planning integration | Requires milestone discipline |

    Recommendation:

    Jira Integration

    Automatically extract Jira issues from commits and branch names. The action supports two modes:

    #### Links-Only Mode (No Auth Required)

    Just provide jira_base_url to extract issue keys and display them as clickable links:

    - uses: jamengual/enterprise-approval-engine@v1
      with:
        action: request
        workflow: production-deploy
        version: v1.2.0
        token: ${{ secrets.GITHUB_TOKEN }}
        jira_base_url: https://yourcompany.atlassian.net  # That's it!

    This extracts issue keys (e.g., PROJ-123) from commit messages and branch names, displaying them as links in the approval issue:

    ### Jira Issues
    

    #### Full Mode (With API Access)

    Add credentials to also fetch issue details and update Fix Versions:

    - uses: jamengual/enterprise-approval-engine@v1
      with:
        action: request
        workflow: production-deploy
        version: v1.2.0
        token: ${{ secrets.GITHUB_TOKEN }}
        # Jira configuration
        jira_base_url: https://yourcompany.atlassian.net
        jira_user_email: ${{ secrets.JIRA_EMAIL }}
        jira_api_token: ${{ secrets.JIRA_API_TOKEN }}
        jira_update_fix_version: 'true'

    This displays rich issue information:

    ### Jira Issues in this Release

    | Key | Summary | Type | Status | |-----|---------|------|--------| | PROJ-123 | Fix login bug | Bug | Done | | PROJ-456 | Add dark mode | Feature | In Progress |

    Comparison of modes:

    | Mode | Auth Required | Features | |------|---------------|----------| | Links-only | No | Issue keys as clickable links | | Full | Yes | Links + summary, status, type emojis, Fix Version updates |

    Jira outputs:

    - name: Use Jira Outputs
      run: |
        echo "Issues: ${{ steps.approval.outputs.jira_issues }}"
        # Output: PROJ-123,PROJ-456

    echo "Details: ${{ steps.approval.outputs.jira_issues_json }}" # Output: [{"key":"PROJ-123","summary":"Fix login bug",...}]

    Deployment Tracking

    Create GitHub deployments for visibility in GitHub's deployment dashboard. This works independently of the environment: key in workflow YAML.

    - uses: jamengual/enterprise-approval-engine@v1
      id: approval
      with:
        action: request
        workflow: production-deploy
        version: v1.2.0
        token: ${{ secrets.GITHUB_TOKEN }}
        # Deployment tracking
        create_deployment: 'true'
        deployment_environment: production
        deployment_environment_url: https://myapp.example.com

    • name: Update Deployment Status
    if: steps.approval.outputs.status == 'approved' run: | # Use the deployment_id to update status after actual deployment echo "Deployment ID: ${{ steps.approval.outputs.deployment_id }}"

    Where deployments appear:

    Note: This creates deployments via the GitHub Deployments API, which is separate from GitHub's native Environment Protection Rules. You can use both together or independently.

    External Config Repository

    Store approval configs in a shared repository for centralized policy management:

    - uses: jamengual/enterprise-approval-engine@v1
      with:
        action: request
        workflow: production-deploy
        token: ${{ secrets.GITHUB_TOKEN }}
        config_repo: myorg/.github  # Shared config repo

    Config resolution order:

    Example organization structure:

    myorg/.github/
    ├── myapp_approvals.yml      # App-specific config
    ├── backend_approvals.yml    # Backend repos config
    └── approvals.yml            # Default for all repos

    Blocking Approvals

    For workflows that require waiting for approval before continuing:

    name: Deploy with Blocking Approval

    on: workflow_dispatch: inputs: version: required: true type: string

    jobs: request-approval: runs-on: ubuntu-latest outputs: issue_number: ${{ steps.request.outputs.issue_number }} steps:

    • uses: actions/checkout@v4
    • uses: jamengual/enterprise-approval-engine@v1
    id: request with: action: request workflow: production-deploy version: ${{ inputs.version }} token: ${{ secrets.GITHUB_TOKEN }}

    wait-for-approval: needs: request-approval runs-on: ubuntu-latest outputs: status: ${{ steps.check.outputs.status }} tag: ${{ steps.check.outputs.tag }} steps:

    • uses: actions/checkout@v4
    • uses: jamengual/enterprise-approval-engine@v1
    id: check with: action: check issue_number: ${{ needs.request-approval.outputs.issue_number }} wait: 'true' # Poll until approved/denied timeout: '4h' # Max wait time token: ${{ secrets.GITHUB_TOKEN }}

    deploy: needs: [request-approval, wait-for-approval] if: needs.wait-for-approval.outputs.status == 'approved' runs-on: ubuntu-latest steps:

    • name: Deploy
    run: | echo "Deploying ${{ needs.wait-for-approval.outputs.tag }}"

    Note: Blocking workflows keep the runner active, which consumes GitHub Actions minutes. For cost-sensitive scenarios, use the event-driven approach (separate process-comment workflow).

    Tag Deletion on Issue Close

    Optionally delete tags when approval issues are manually closed:

    workflows:
      dev-deploy:
        on_closed:
          delete_tag: true   # Delete tag when issue is closed
          comment: "Cancelled. Tag {{tag}} deleted."

    production-deploy: on_closed: delete_tag: false # NEVER delete production tags

    Handle close events:

    # .github/workflows/handle-close.yml
    name: Handle Issue Close

    on: issues: types: [closed]

    jobs: handle: if: contains(github.event.issue.labels.*.name, 'approval-required') runs-on: ubuntu-latest steps:

    • uses: actions/checkout@v4
    • uses: jamengual/enterprise-approval-engine@v1
    id: close with: action: close-issue issue_number: ${{ github.event.issue.number }} issue_action: ${{ github.event.action }} token: ${{ secrets.GITHUB_TOKEN }}

    • name: Report
    run: | echo "Status: ${{ steps.close.outputs.status }}" echo "Deleted tag: ${{ steps.close.outputs.tag_deleted }}"


    Complete Examples

    Full-Featured Request Workflow

    name: Request Production Deployment

    on: workflow_dispatch: inputs: version: description: 'Version to deploy' required: true type: string environment: description: 'Target environment' required: true type: choice options: [staging, production]

    permissions: contents: write issues: write deployments: write

    jobs: request: runs-on: ubuntu-latest outputs: issue_number: ${{ steps.approval.outputs.issue_number }} issue_url: ${{ steps.approval.outputs.issue_url }} deployment_id: ${{ steps.approval.outputs.deployment_id }} jira_issues: ${{ steps.approval.outputs.jira_issues }} steps:

    • uses: actions/checkout@v4
    with: fetch-depth: 0 # Needed for commit comparison

    • uses: jamengual/enterprise-approval-engine@v1
    id: approval with: action: request workflow: ${{ inputs.environment }}-deploy version: ${{ inputs.version }} token: ${{ secrets.GITHUB_TOKEN }} # Jira integration jira_base_url: https://mycompany.atlassian.net jira_user_email: ${{ secrets.JIRA_EMAIL }} jira_api_token: ${{ secrets.JIRA_API_TOKEN }} # Deployment tracking create_deployment: 'true' deployment_environment: ${{ inputs.environment }} deployment_environment_url: https://${{ inputs.environment }}.myapp.com

    • name: Summary
    run: | echo "## Approval Request Created" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "- Issue: #${{ steps.approval.outputs.issue_number }}" >> $GITHUB_STEP_SUMMARY echo "- URL: ${{ steps.approval.outputs.issue_url }}" >> $GITHUB_STEP_SUMMARY echo "- Jira Issues: ${{ steps.approval.outputs.jira_issues }}" >> $GITHUB_STEP_SUMMARY echo "- Commits: ${{ steps.approval.outputs.commits_count }}" >> $GITHUB_STEP_SUMMARY

    Process Comments with Team Support

    name: Handle Approval Comments

    on: issue_comment: types: [created]

    permissions: contents: write issues: write

    jobs: process: if: | github.event.issue.pull_request == null && contains(github.event.issue.labels.*.name, 'approval-required') runs-on: ubuntu-latest steps:

    • uses: actions/checkout@v4
    # Generate GitHub App token for team membership checks
    • uses: actions/create-github-app-token@v2
    id: app-token with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }}

    • uses: jamengual/enterprise-approval-engine@v1
    id: process with: action: process-comment issue_number: ${{ github.event.issue.number }} token: ${{ steps.app-token.outputs.token }} # Jira integration to update Fix Version on approval jira_base_url: https://mycompany.atlassian.net jira_user_email: ${{ secrets.JIRA_EMAIL }} jira_api_token: ${{ secrets.JIRA_API_TOKEN }}

    • name: Trigger Deployment
    if: steps.process.outputs.status == 'approved' uses: actions/github-script@v7 with: script: | await github.rest.actions.createWorkflowDispatch({ owner: context.repo.owner, repo: context.repo.repo, workflow_id: 'deploy.yml', ref: 'main', inputs: { version: '${{ steps.process.outputs.tag }}' } });

    Multi-Environment Promotion

    # .github/approvals.yml
    version: 1

    policies: dev-team: approvers: [dev1, dev2, dev3] min_approvals: 1

    qa-team: approvers: [qa1, qa2] min_approvals: 1

    prod-team: approvers: [team:sre, tech-lead] min_approvals: 2

    workflows: dev-deploy: require:

    • policy: dev-team
    on_approved: tagging: enabled: true auto_increment: patch env_prefix: "dev-" close_issue: true

    staging-deploy: require:

    • policy: qa-team
    on_approved: tagging: enabled: true auto_increment: minor env_prefix: "staging-" close_issue: true

    production-deploy: require:

    • policy: prod-team
    on_approved: create_tag: true close_issue: true on_closed: delete_tag: false # Never delete production tags

    Using Outputs in Subsequent Jobs

    name: Deploy with Approval

    on: workflow_dispatch: inputs: version: required: true

    jobs: approval: runs-on: ubuntu-latest outputs: status: ${{ steps.check.outputs.status }} tag: ${{ steps.check.outputs.tag }} approvers: ${{ steps.check.outputs.approvers }} jira_issues: ${{ steps.request.outputs.jira_issues }} steps:

    • uses: actions/checkout@v4
    • uses: jamengual/enterprise-approval-engine@v1
    id: request with: action: request workflow: production-deploy version: ${{ inputs.version }} token: ${{ secrets.GITHUB_TOKEN }} jira_base_url: https://mycompany.atlassian.net

    • uses: jamengual/enterprise-approval-engine@v1
    id: check with: action: check issue_number: ${{ steps.request.outputs.issue_number }} wait: 'true' timeout: '2h' token: ${{ secrets.GITHUB_TOKEN }}

    deploy: needs: approval if: needs.approval.outputs.status == 'approved' runs-on: ubuntu-latest environment: production steps:

    • name: Deploy
    run: | echo "Deploying ${{ needs.approval.outputs.tag }}" echo "Approved by: ${{ needs.approval.outputs.approvers }}" echo "Jira Issues: ${{ needs.approval.outputs.jira_issues }}"

    notify: needs: [approval, deploy] if: always() runs-on: ubuntu-latest steps:

    • name: Notify Slack
    run: | if [ "${{ needs.approval.outputs.status }}" == "approved" ]; then echo "Deployment of ${{ needs.approval.outputs.tag }} completed!" else echo "Deployment was ${{ needs.approval.outputs.status }}" fi


    Schema Validation

    Validate your configuration using the JSON schema:

    # .github/approvals.yml
    

    yaml-language-server: $schema=https://raw.githubusercontent.com/jamengual/enterprise-approval-engine/main/schema.json

    version: 1

    policies: # ... your config

    Or validate in CI:

    - name: Validate Config
      run: |
        npm install -g ajv-cli
        ajv validate -s schema.json -d .github/approvals.yml


    GitHub Enterprise Server

    The action fully supports GitHub Enterprise Server. It automatically detects GHES environments using the GITHUB_SERVER_URL and GITHUB_API_URL` environment variables.

    No additional configuration is required - the action will automatically use the correct API endpoints.

    Rate Limiting:

    The action includes automatic retry with exponential backoff for rate limit errors. Configuration:

    ---

    License

    MIT License

    --- Tranlated By Open Ai Tx | Last indexed: 2025-12-22 ---