Skip to content

Commit

Permalink
patcher concepts: what is a patch
Browse files Browse the repository at this point in the history
  • Loading branch information
ceschae committed Nov 17, 2024
1 parent 31820fc commit 329c173
Showing 1 changed file with 187 additions and 1 deletion.
188 changes: 187 additions & 1 deletion docs/2.0/docs/patcher/concepts/patches.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,189 @@
# What is a Patcher Patch?

This page is under construction
A Patch is a set of instructions executed by Patcher that do code transformations.
This strategy is especially useful as a way to automate adoption of a breaking change with infrastructure as code, such as Terragrunt, OpenTofu, or Terraform.
This instruction sheet is delivered by means of a `yaml` file in a specific format:

```yaml
name: "<name-of-patch>"
description: "<description for patch>"
author: <your-name-here> <<your-email-address-here>>

# Optional dependencies. Terrapatch is a typical one
dependencies:
- name: terrapatch
version: "0.1.0"

# Steps necessary to resolve breaking change
steps:
- name: "<name-of-step>"
run: <command-to-run>
- name: "<name-of-second-step>"
run: <second-command-to-run>
# etc
```

[Check out an example of a patch in the CIS Service Catalog.](https://github.com/gruntwork-io/terraform-aws-service-catalog/blob/c3d5ede211fc3230a7d493ceea43622b337ee88a/.patcher/patches/v0.96.4/switch-to-cis-rds-module/patch.yaml)

## For Module Consumers

As an OpenTofu/Terraform module consumer, modules are imported to launch infrastructure by writing Terragrunt units (`terragrunt.hcl` files) or OpenTofu/Terraform code that calls a module.

It is best practice to reference a specific version of an OpenTofu/Terraform module.
Over time, module authors release new versions of the module, and the code that consumes those modules slowly gets out of date.
In some cases, the latest update of the underlying modules requires a breaking change to the consuming code, meaning the version can't just be bumped.
The consuming code needs to be edited to make use of the new version.
In this instance, using a patch with Patcher comes in handy.

Patches can be consumed with either a "push" strategy, when Patcher proactively opens a pull request with the latest update, or a "pull" strategy, when a repo is manually scanned to look at the current state of your infrastructure using the Patcher CLI tool.

Regardless of methodology, the concept remains the same.
Patcher will suggest changes to your codebase in order to keep your infrastructure up to date, however you see fit.

Here is an example of what your PR will look like, after being generated by Patcher:

** insert image of PR screenshot **

### Update Push Strategy

Let's take a look at the experience of the "push" strategy available through our [GitHub action](https://github.com/gruntwork-io/patcher-action).
Using the GitHub action, PRs will be opened against your codebase on a cadence specified by you, against environments, versions, etc. specified by you.
The intention with this GitHub action is to leave the repo owner in full control of your upgrade cadence.

<details>
<summary>The example configuration is long. Click to expand!</summary>

```yaml
name: Update Dependencies
on:
workflow_dispatch:
repository_dispatch:
types: [new_module_release]
schedule:
# 04:15 UTC on Mondays
- cron: "15 4 * * 1"
pull_request_target:
types:
- closed
branches:
- main

permissions:
contents: write

env:
ENV_FOLDER_NAME: .
PR_BRANCH_PREFIX: patcher-updates-

jobs:
trigger-next-env:
if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'updates-dev')
runs-on: ubuntu-latest
steps:
- shell: bash
id: dependency
env:
PR_BRANCH_PREFIX: ${{ env.PR_BRANCH_PREFIX }}
BRANCH: ${{ github.head_ref }}
run: |
dep=${BRANCH#"$PR_BRANCH_PREFIX"}
echo "dependency=$dep" >> "$GITHUB_OUTPUT"
- uses: peter-evans/repository-dispatch@main
with:
token: ${{ github.token }}
repository: ${{ github.repository }}
event-type: dev_updates_merged
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "branch": "${{ github.head_ref }}", "dependency": "${{ steps.dependency.outputs.dependency }}"}'

patcher-report:
if: github.event_name == 'repository_dispatch' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
outputs:
spec: ${{ steps.get-spec.outputs.spec }}
steps:
- uses: actions/checkout@v4
- uses: gruntwork-io/patcher-action@main
id: get-spec
with:
patcher_command: report
github_token: ${{ secrets.PIPELINES_READ_TOKEN }}
include_dirs: "*/**"
working_dir: ./
spec_file: patcher-spec.json

update-env:
needs: [patcher-report]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
dependency: ${{ fromJson(needs.patcher-report.outputs.spec).Dependencies }}
steps:
- uses: actions/checkout@v4

- name: Create the spec file
shell: bash
run: |
echo '${{ needs.patcher-report.outputs.spec }}' > patcher-spec.json
- uses: gruntwork-io/patcher-action@main
with:
patcher_command: update
github_token: ${{ secrets.PIPELINES_READ_TOKEN }}
working_dir: ${{ env.ENV_FOLDER_NAME }}
dependency: ${{ matrix.dependency.ID }}
spec_file: patcher-spec.json
pull_request_title: "[Patcher] Update ${{ matrix.dependency.ID }}"
pull_request_branch: "${{ env.PR_BRANCH_PREFIX }}${{ matrix.dependency.ID }}"
```
</details>
<br/>
Also, check out our guide on [promotion workflows](../guides/promotion-workflows), so that updates can proceed from `dev` to `stage` to `prod` (or any other environment sequence) to mitigate risks around upgrades.

### Update Pull Strategy

Let's take a look at the experience of "pulling" available updates by using the Patcher CLI tool.
The first step is to run `patcher update` within the repo in which updates are desired.
When `patcher update` is run, the default mode is to click through the updates **interactively**.
In this mode, available updates are found, and the details of those updates are presented to you:

** insert image here **

You can choose to run in `--non-interactive` mode, which will modify the codebase and present results about what the program did at the end.

Interactively nor not, by default, a PR will _not_ be opened with the changes.
However, the changes should be visible within the version control system. At that point, you may make a PR or apply the changes using your IaC system.

### Examples Running `patcher update`

Here's the easiest way to run this command:

```bash
$ cd <repo-root-directory>
# give me ALL the patches for ALL the things
$ patcher update ./
```

If more fine-grain controls are desired, the following example has those:

```bash
# run 'update' non-interactively, only up to the next safe version, and publish a PR with the changes
$ patcher update --update-strategy next-safe --non-interactive --publish --pr-branch grunty/update-via-patcher --pr-title "[Patcher] Update All Dependencies to Next Safe"
```

More details on the available options included in `patcher update` can be found in the [reference section](../../../reference/patcher/index.md#update).

## For Module Authors

Module authors periodically need to introduce breaking changes in their modules, causing a downstream, potentially painful, experience for module consumers.
With patches, module authors include a patch YAML file that automatically updates consuming code to incorporate the breaking changes associated with the updated module code.
Doing so allows module consumers to use patches to enable their modules consumers to automatically update consuming code to adopt breaking change.

In a Patcher ecosystem, the resolution to such a change is written once, in a patch, and distributed to all consumers.
Although your release will succeed with or without a patch, downstream consumers of your breakig change will praise you thoroughly for your advance work.

In theory, you may write whatever command execution steps you want to perform patch steps.
For example, there are many cases where validating tool versions are required, or using `sed` to find and replace certain values.
However, we _strongly_ recommend using `terrapatch`, a Gruntwork tool that surgically updates Terraform/OpenTofu HCL files.

0 comments on commit 329c173

Please sign in to comment.