github / branch-deploy

Enabling Branch Deployments through IssueOps with GitHub Actions - If you find this project useful, give it a star! ⭐️
https://github.com/marketplace/actions/branch-deploy
MIT License
379 stars 44 forks source link

Parallell noop deployments from a single comment - .noop dev,uat,prd #310

Open walkerk1980 opened 1 week ago

walkerk1980 commented 1 week ago

Details

We're currently using branch-deploy for terraform deployments with multiple environment jobs.

  comment_parser:
    if: ${{ github.event.issue.pull_request }}
    runs-on: ubuntu-latest
    timeout-minutes: 3
    environment:
      name: oidc
    steps:
      - name: Parse Issue Comment for Command
        id: branch_deploy
        uses: github/branch-deploy@v9.8.1
        with:
          trigger: ".tf_apply"
          noop_trigger: ".tf_plan"
          permissions: write,admin
          environment_targets: ${{ inputs.target_environments }}
          production_environments: env.PRODUCTION_ENVIRONMENTS
          environment: dev
          skip_ci: dev,uat,tst,tqa,sup
          skip_reviews: dev,uat,tst,tqa,sup
          skip_completing: 'true' # we will complete the deployment manually
      - name: If environment is production, set the TF workspace output to default
        id: set_tf_workspace
        if: ${{ steps.branch_deploy.outputs.continue == 'true' }}
        run: |
          if [ "${{ steps.branch_deploy.outputs.environment }}" == "prd" ]; then
            echo "TF_WORKSPACE=default" >> $GITHUB_OUTPUT
          else
            echo "TF_WORKSPACE=${{ steps.branch_deploy.outputs.environment }}" >> $GITHUB_OUTPUT
          fi

We then use the outputs of this job to determine what environments to plan or apply against in subsequent jobs, pretty much like what is laid out in the examples.

  terraform_plan:
    if: ${{ needs.comment_parser.outputs.continue == 'true' && needs.comment_parser.outputs.noop == 'true' }}
    needs: comment_parser
    runs-on: ubuntu-latest
    timeout-minutes: 60
    environment:
      name: ${{ needs.comment_parser.outputs.environment }}

We have several non-production environments for each project and sometimes more than one production environment. The environments can vary per project but typically we're talking dev,uat,prd.

We aren't yet using the new enforce deployment order feature, but even if we adopt it, in terraform's case that only makes sense for us on .tf_apply (.deploy) rather than .tf_apply (.noop). We have no real reason to wait to plan against UAT for example because we haven't applied DEV.

In our current process when we open a PR to add a new feature/infra to a project in order to plan we have to create multiple comments to get plans running:

.tf_plan dev
.tf_plan uat
.tf_plan prd

It would be easier for us if we could comment like so:

.tf_plan dev,uat,prd

Then rather than use the "environment: string" output, we'd use the "environments: list" output to run against multiple environments.

We currently do something like this with issues and the actions/command-action

  terraform_drift_detection:
    if: ${{ needs.environments.outputs.environments != '[]' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') }}
    strategy:
      fail-fast: false
      matrix:
        environment: ${{ fromJson(needs.environments.outputs.environments) }}

Is there any chance this would be desirable as a feature?

GrantBirki commented 1 day ago

👋 Thank you for opening this issue! If I'm understanding correctly, it looks like instead of doing:

  1. .deploy dev
  2. .deploy uat
  3. .deploy prd

You want to just be able to do:

.deploy dev,uat,prd

Is there any chance this would be desirable as a feature?

When looking at this request, right away I can see how this would be useful or a QOL improvement when using this Action. However, in terms of "desirable" I think there are some things to consider. First, we should keep in mind that this Action aims to enable folks to deploy their projects without interfering with "how" people deploy (other than upholding the concept of branch deployments).

That being said, here are the few concerns that first come to mind with this:

  1. If multiple environments can be deployed at once, what sort of guard rails exist to stop a bad deployment? For example, if staging, dev, and production are all deployed at the same time, the project doing so loses the valuable ability to stop a bad deployment to a production environment. If the deployment was done via Terraform, you might be tempted to pull the emergency break and kill the Action workflow. This might save another environment from fully deploying but now you might have broken TF state, a stuck TF lock, etc.
  2. If multiple environments can be deployed at once, multiple environments can be brought down at once. This increases the blast radius of a bad deployment. For example, if one were to deploy us-production, eu-production, and apac-production all at once, it would result it a larger outage rather than if just us-production went down.
  3. If such a feature was added, it would be incompatible with the new enforced deployment order feature as an order cannot be enforced if all environments can be deployed at once.

Now, most of these points only exist if one was using a matrix workflow strategy and had all these deployments running in parallel. Rather than run in parallel, one might be able to run them in a logical order like dev -> uat -> prd.

Again, the way this Action is design is to help folks safely deploy changes and I do believe that this feature request can bring value to teams that have multiple environments that can support parallel deployments in a safe manner.


Enter... Pipelines

Another concept that we have here at GitHub is deployment pipelines. This is a deployment concept where you might deploy a project in stages (or steps) that might consist of one (or more) environments.

This is something that you could stitch together with this Action today. You could rename your trigger to something .deploy with an environment of main-pipeline. Then within your defined workflows, you could execute custom logic if someone types .deploy main-pipeline or .noop main-pipeline. The main-pipeline environment would actually encapsulate all your other environments such as dev, uat, and prd. So then you could have a workflow that either goes in steps (or in parallel) to deploy all of these environments.

As a bonus you could have dev deploy, wait a few minutes, then start uat, and so on. This would ensure that you have ample time to kill your workflow in-between stages of your pipeline in the event that something goes wrong. Extra bonus would be that you have your workflow gracefully exit if something goes wrong so you don't have to do any manual cancelling.

Pipelines Pseudocode Example

User types .deploy main-pipeline on a pull request:

  1. Actions job starts the deployment pipeline
  2. The first "step" starts to deploy to the dev environment (if it fails, the workflow stops)
  3. The second step deploys to the uat environment (if it fails, the workflow stops)
  4. The third step deploys to the prd environment (if it fails, the workflow stops)
  5. Maybe you have a custom step here to leave a comment on the pull request summarizing each env's deployment
  6. The workflow finishes

I'll leave this issue open and it is something that certainly might be implemented in the future. I'm unsure if I'll have the bandwidth to work on this myself but pull requests are always welcome! 🙇

Thank you for taking the time to open this issue as well ❤