/gitflow-workflow-action

Primary LanguageJavaScriptMIT LicenseMIT

gitflow-workflow-action

A GitHub actions that automate the Gitflow workflow where one has a develop branch for continuous development and a main (production) branch that would automatically deploy to production. In between that, a release branch is created from develop to perform release preparation tasks before being merged to main branch. Occasionally, a hotfix branch is created from main for hot fixes.

Gitflow

Usage

Create .github/workflows/release.yml:

on:
  workflow_dispatch:
    inputs:
      version:
        type: string
        required: true
        description: "Version to release"
  pull_request:
    types:
      - closed

name: Release

jobs:
  release_workflow:
    runs-on: ubuntu-latest
    steps:
      - name: gitflow-workflow-action release workflows
        uses: hoangvvo/gitflow-workflow-action@<TAG>
        with:
          develop_branch: "develop"
          main_branch: "main"
          merge_back_from_main: false
          version: ${{ inputs.version }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Inputs

Name Description Default
develop_branch Name of the develop branch develop
main_branch Name of the main branch main
merge_back_from_main If "true", there will be a merge back from main instead of the release branch to develop after a release is created. See this Stackoverflow discussion. "false"
version Version to release
version_increment If version is not specified, this value will be used to increment the version based on semver. Valid values are major, minor, patch, premajor, preminor, prepatch, prerelease. If version is specified, this value will be ignored.
dry_run If "true", the action will not create any PRs or releases. It will only print out the steps it would take and some outputs like pull_numbers_in_release. "false"
release_summary Specify the release summary to be put in the last section of the release PR ""

Alternatively, the following environment variables can be used: DEVELOP_BRANCH, MAIN_BRANCH, MERGE_BACK_FROM_MAIN, VERSION, DRY_RUN, RELEASE_SUMMARY.

Outputs

Depending on the workflow types, some outputs might be present:

Name Description
type Type of the release: release, hotfix, none
version Version of the release.
pull_number Pull request number. (only for workflow_dispatch)
pull_numbers_in_release Comma separated pull request numbers as shown in the What's changed section of the release. (only for workflow_dispatch)
release_branch Name of the release branch. (only after merging release PR)
release_url URL to the release page. (only after merging release PR)
latest_release_tag_name Name of the latest release tag (only for workflow_dispatch).

Workflows

There are two different workflows covered by this action:

Create "Deploy to production" PR

image

This applies when this workflow is triggered from the "Run workflow" window (workflow_dispatch).

The process of creating a release start with creating a PR with release note that contains all the new changes in the body. The new branch would be called release/x.y.z.

This basically "freezes" the develop branch for releases. Other PRs can be merged to develop during the release branch lifetime without affecting it.

Workflows for release lifecycle

This detects when the pull request created with the use case above.

This workflow does several things:

  • If the PR has a branch that is prefixed with release or hotfix and merged to main, it will create a release, merge back to develop branch, and trigger integrations. This is the process in Gitflow.

Note: It does not handle the deployment process. That is for your team to implement separately.

Integration: Post to Slack

It is often that an anouncement is made to a Slack channel after a release. To do so, specify SLACK_TOKEN env and slack input.

Alternatively, you can also use the environment variable SLACK_OPTIONS instead of slack input.

jobs:
  release_workflow:
    steps:
      - name: gitflow-workflow-action release workflows
        uses: hoangvvo/gitflow-workflow-action@<TAG>
        with:
          slack: >
            {
              "channel": "hoang-test",
              "username_mapping": {
                "hoangvvo": "U03B3E4UPV3"
              }
            }
        env:
          SLACK_TOKEN: ${{ secrets.SLACK_TOKEN }}

Example: Prefill release summary

Using the pull_numbers_in_release output and dry run mode, you can prefill the PR body with the release summary depending on your PR template.

Let's assume the PR template is:

## What does this PR do?

xxx <-- we want to extract this

## How should this be manually tested?

xxx

## What are the requirements to deploy to production?

xxx

## Any background context you want to provide beyond Shortcut?

xxx

## Screenshots (if appropriate)

xxx
jobs:
  release_workflow:
    runs-on: ubuntu-latest
    steps:
      # This step is only run for `generate_pr_summary` step
      # which is only run when the workflow is triggered from the "Run workflow" window
      - id: release_workflow_dry_run
        name: gitflow-workflow-action release workflows
        if: github.event_name == 'workflow_dispatch'
        uses: hoangvvo/gitflow-workflow-action@<TAG>
        with:
          develop_branch: "develop"
          main_branch: "main"
          dry_run: true

      - id: generate_pr_summary
        name: generate pr summary
        if: ${{ steps.release_workflow_dry_run.outputs.type == 'release' and steps.release_workflow_dry_run.outputs.pull_numbers_in_release }}
        uses: actions/github-script@v7
        with:
          scripts: |
            const pull_numbers_in_release = "${{ steps.release_workflow_dry_run.outputs.pull_numbers_in_release }}";
            const mergedPrNumbers = Array.from(new Set(pull_numbers_in_release.split(',').map(Number)));
            // Get the PRs and parse the release summary
            const mergedPrs = await Promise.all(mergedPrNumbers.map(async (prNumber) => {
              const pr = await github.rest.pulls.get({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: prNumber
              });
              if (!pr.data.body) {
                return;
              }
              const regex = /\#\# What does this PR do\?([\s\S]*?)\n\#\#/gm;
              let match = regex.exec(pr.data.body)?.[1]?.trim();
              // try to remove empty lines
              match = match?.split('\n').map(s => s.trim()).filter(Boolean).map(
                s => s.startsWith('-') || s.startsWith('*') ? s : `* ${s}`
              ).join('\n');
              return `${pr.data.title}\n${match}`;
            })).then((prs) => prs.filter(Boolean));
            const releaseSummary = mergedPrs.join('\n\n');
            return resultSummary;
          result-encoding: string

      - id: release_workflow
        name: gitflow-workflow-action release workflows
        uses: hoangvvo/gitflow-workflow-action@<TAG>
        with:
          develop_branch: "develop"
          main_branch: "main"
          version: ${{ inputs.version }}
          release_summary: ${{ steps.generate_pr_summary.outputs.result }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

License

MIT