Skip to content

Commit

Permalink
Various documentation, naming and logging improvements (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielemery authored Feb 29, 2024
1 parent a04e59d commit 5383689
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 40 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Rollback
name: Deploy Existing Release

on:
workflow_dispatch:
Expand All @@ -10,23 +10,29 @@ jobs:
packages: write
contents: write # Write is required to create/update releases
steps:
- name: Ensure pre-requisites for rollback are met
id: validate_rollback
- name: Ensure pre-requisites for deployment are met
id: validate_deployment
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { owner, repo } = context.repo;
/*
* We load 20 releases here, it's unlikely that we will want to roll back
* to a release older than that.
*/
* We load 20 releases here, it's unlikely that we will want to roll back
* to a release older than that.
*/
const PAGE_SIZE = 20;
const releases = await github.rest.repos.listReleases({ owner, repo });
const releases = await github.rest.repos.listReleases({
owner,
repo,
page: PAGE_SIZE,
});
// Pre-releases should not be considered for rollback
/*
* Pre-releases should not be considered for deployment here, the `publish-new-release` action
* should be used instead.
*/
const availableReleases = releases.data.filter((release) => !release.prerelease);
const targetRelease = availableReleases.find(
(release) => release.tag_name === "${{github.ref_name}}"
);
Expand All @@ -35,7 +41,7 @@ jobs:
}
console.log(
`Found release ${targetRelease.id}, proceeding with rollback: ${targetRelease.url}`
`Found release ${targetRelease.id}, proceeding with deployment: ${targetRelease.html_url}`
);
return targetRelease.id;
Expand Down Expand Up @@ -73,6 +79,6 @@ jobs:
await github.rest.repos.updateRelease({
owner,
repo,
release_id: ${{ steps.validate_rollback.outputs.result }},
release_id: ${{ steps.validate_deployment.outputs.result }},
make_latest: true
});
20 changes: 9 additions & 11 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
packages: write
contents: write # Write is required to create/update releases
contents: write # Write is required to create/update releases AND to write tags
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down Expand Up @@ -51,27 +51,25 @@ jobs:
const { owner, repo } = context.repo;
/*
* We should only need to load 2 releases, as either both will be latest, or one will be latest and
* the other will be pending. We're loading a few extras here in case we get into a weird state.
* the other will be pending. We're loading a few extras here so that if we get into a weird state
* we can provide a better error.
*/
const PAGE_SIZE = 5;
const releases = await github.rest.repos.listReleases({ owner, repo });
const PAGE_SIZE = 10;
const releases = await github.rest.repos.listReleases({
owner,
repo,
page: PAGE_SIZE,
});
const pendingReleases = releases.data.filter(release => release.prerelease);
if (pendingReleases.length > 1) {
throw new Error(`Found more than one pending release: ${pendingReleases.map(release => release.tag_name).join(', ')}`);
}
const latestRelease = releases.data.find(release => !release.prerelease);
if (!latestRelease) {
throw new Error('No latest release found');
}
if (pendingReleases.length === 1) {
console.log(`Found existing pending release: ${pendingReleases[0].tag_name}, replacing it`);
await github.rest.repos.deleteRelease({ owner, repo, release_id: pendingReleases[0].id });
}
console.log('No pending release found, creating one');
const newRelease = await github.rest.repos.createRelease({
owner,
repo,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Production Deploy
name: Publish New Release

on:
workflow_dispatch:
Expand All @@ -21,8 +21,12 @@ jobs:
* We should only need to load 1 release here, as pending should be at the top of the list.
* We're loading a few extras here in case we get into a weird state.
*/
const PAGE_SIZE = 5;
const releases = await github.rest.repos.listReleases({ owner, repo });
const PAGE_SIZE = 10;
const releases = await github.rest.repos.listReleases({
owner,
repo,
page: PAGE_SIZE,
});
const pendingReleases = releases.data.filter((release) => release.prerelease);
if (pendingReleases.length > 1) {
Expand All @@ -41,7 +45,7 @@ jobs:
}
console.log(
`Found pending release ${targetRelease.id}, proceeding with deployment: ${targetRelease.url}`
`Found pending release, proceeding with deployment: ${targetRelease.html_url}`
);
return targetRelease.id;
Expand Down
37 changes: 23 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,43 @@ Proof-of-concept for a deployment/rollback flow using github actions and release

- When a merge is done to the `main` branch, a pending release should be created / updated
- A tag should be created and used for the release based on the current date and time
- The release should be created with the tag and the release notes should be the commit messages since the last release
- The release should be created with the tag and notes describing the changes since the last release
- The release should be marked as a prerelease
- Any artifacts required for the deployment should be uploaded to the release
- Any artifacts required for the deployment should be uploaded to the release (none yet but could be in the future)
- The release should be automatically deployed to the `staging` (`dev`) environment
- When the release action is manually triggered
- The release should be marked as a non-draft
- A deployment should be done to the `production` environment
- When the rollback action is manually triggered
- The last release should be marked as a draft
- When the `publish-new-release` action is manually triggered
- A deployment should be done to the `production` environment using the selected pre-release
- The release should be set to `latest`
- When the `deploy-existing-release` is manually triggered
- A deployment should be done to the `production` environment using the selected release
- The current release should have it's `latest` status removed
- The target selected release should be set to `latest`

## Limitations

- We're only providing the option to deploy ALL pending releases rather than pick and choose an older one like we have with the current behaviour

## Implementation

- ~~https://github.com/marketplace/actions/get-release is used to get the id of the last release~~
- ~~https://github.com/softprops/action-gh-release is used to create the new release~~
- ~~https://github.com/irongut/EditRelease is used to edit the release if it already exists~~
- Instead of using multiple poorly maintained I instead opted to make heavy use of `actions/github-script@v7`
- Instead of using multiple poorly maintained actions I instead opted to make heavy use of `actions/github-script@v7`
- ~~`git log --pretty=oneline tagA...tagB` is used to get the commit messages since the last release~~
- It turned out github automatic release notes are good enough
- It turned out github automatic release notes are actually better, but rely heavily on PRs (which should suit us).

## Run Sample App Locally

### From Github Registry

```sh
docker build --build-arg APP_VERSION=local -t action-deployment-poc:local .
docker run -p 7890:80 action-deployment-poc:local
docker pull docker pull ghcr.io/danielemery/action-deployment-poc:latest
docker run -p 7890:80 ghcr.io/danielemery/action-deployment-poc:latest
```

# Initial setup
### With Local Build

- The latest state of the main branch should be deployed to production
- A date tag and release should be up to date with the latest state of the main branch
```sh
docker build --build-arg APP_VERSION=local -t action-deployment-poc:local .
docker run -p 7890:80 action-deployment-poc:local
```

0 comments on commit 5383689

Please sign in to comment.