diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ed30c6a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,13 @@ +name: CI + +on: + pull_request: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Test building the docker image + run: docker build . diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..b35b024 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,82 @@ +name: Main + +on: + push: + branches: ["main"] + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + packages: write + contents: write # Write is required to create/update releases + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Generate version based on date + run: echo "RELEASE_VERSION=$(date '+%Y-%m-%d_%H_%M')" >> $GITHUB_ENV + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.RELEASE_VERSION }} + build-args: | + APP_VERSION=${{ env.RELEASE_VERSION }} + + - name: Create tag + uses: rickstaa/action-create-tag@v1 + id: "tag_create" + with: + tag: ${{ env.RELEASE_VERSION }} + + - name: Upsert pending release + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + 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. + */ + const PAGE_SIZE = 5; + const releases = await github.rest.repos.listReleases({ owner, repo }); + + 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, + prerelease: true, + tag_name: "${{ env.RELEASE_VERSION }}", + name: "${{ env.RELEASE_VERSION }}", + generate_release_notes: true, + }); + console.log(`Created pending release: ${newRelease.data.tag_name}`); diff --git a/README.md b/README.md index b16d944..300391e 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,23 @@ Proof-of-concept for a deployment/rollback flow using github actions and release - The last release should be marked as a draft - A deployment should be done to the `production` environment using the selected release +## 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` +- ~~`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 + ## Run Sample App Locally ```sh docker build --build-arg APP_VERSION=local -t action-deployment-poc:local . docker run -p 7890:80 action-deployment-poc:local ``` + +# Initial setup + +- 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