From a9dfada60b8ad081dcd32045c4d679dcf9678f6f Mon Sep 17 00:00:00 2001 From: Chris O'Donnell Date: Wed, 28 Aug 2019 21:59:15 -0400 Subject: [PATCH] feat: add new output variables - add output variables - slightly refactor entrypoint - improve readme documentation - add github action step to test things --- .github/workflows/ci-cd.yml | 9 ++ README.md | 197 ++++++++++++++++++++++++++++-------- action.yml | 11 ++ entrypoint.js | 74 +++++++------- 4 files changed, 212 insertions(+), 79 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 280e6c7..95dfbe9 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -27,6 +27,15 @@ jobs: - name: semantic-release if: github.ref == 'refs/heads/master' + id: semantic uses: ./ env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: test output + run: | + echo "$OUTPUTS" + echo new release? $NEW_RELEASE_PUBLISHED + env: + NEW_RELEASE_PUBLISHED: ${{ steps.semantic.outputs.new-release-published }} + OUTPUTS: ${{ toJson(steps.semantic.outputs) }} diff --git a/README.md b/README.md index e5cb66b..9cd231c 100644 --- a/README.md +++ b/README.md @@ -4,62 +4,118 @@ > your repo. Exports [environment variables](#outputs) for you to use in subsequent actions > containing version numbers. -## Why +## Usage -It would have been fairly easy to run this as a "host action," aka something that simply runs -directly on the VM. +See [action.yml](action.yml) + +- Use major version + ([recommended by GitHub](https://github.com/actions/toolkit/blob/master/docs/action-versioning.md)): + `uses: codfish/semantic-release-action@v1` +- Use latest version: `uses: codfish/semantic-release-action@master` +- Use specific version: `uses: codfish/semantic-release-action@v1.1.0` + +Basic Usage: ```yml steps: - - name: Semantic Release - run: npx semantic-release + - uses: actions/checkout@master + + - uses: codfish/semantic-release-action@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }} ``` -If you're just publishing an node package, then this could still work well for you. The problem I -found with this is when I was in projects where I had subsequent steps/actions in which I wanted to -know whether a new version was cut. +Using output variables set by `semantic-release-action`: -> **Use Case:** For instance, in an application where I'm using `semantic-release` to manage GitHub -> releases, but also building and pushing docker images. Dockerhub has a -> [nice GitHub integration](https://docs.docker.com/docker-hub/builds/) to handle this for us, but -> some other registries do not. If I need to cut a new release, then update a docker registry by -> adding a new tagged build, I'll want to run `semantic-release` and then build a docker image, tag -> it with a version and push it up. In my case I like to push up tags for `latest`, the semver (i.e. -> `1.8.3`), and just the major the version (i.e. `1`). +```yml +steps: + - uses: actions/checkout@master -I want to know 1) if semantic-release cut a new version and 2) what the version is. + # you'll need to add an `id` in order to access output variables + - uses: codfish/semantic-release-action@master + id: semantic + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} -There's a number of ways to handle this, but the most elegant way I found to do it was to abstract -it into it's own custom action. It abstracts away whatever logic you need to figure out what that -new release number is. + - run: echo ${{ steps.semantic.outputs.release-version }} -This also scales well, just in case I want to add some flexibility and functionality to this action, -I can easily leverage it across any project. + - run: echo "$OUTPUTS" + env: + OUTPUTS: ${{ toJson(steps.semantic.outputs) }} -## Usage + - uses: codfish/some-other-action@master + with: + release-version: ${{ steps.semantic.outputs.release-version }} +``` + +Only run an action if a new version was created: + +```yml +steps: + - uses: actions/checkout@master + + # you'll need to add an `id` in order to access output variables + - uses: codfish/semantic-release-action@master + id: semantic + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + # exit gracefully if no version was published + - name: docker push version + run: | + if [ "$NEW_RELEASE_PUBLISHED" == "false" ]; then exit 0; fi; + docker tag $DOCKER_REGISTRY/your-image $DOCKER_REGISTRY/your-image:$TAG + docker push $DOCKER_REGISTRY/your-image:$TAG + env: + DOCKER_REGISTRY: ${{ secrets.DOCKER_REGISTRY }} + TAG: v$RELEASE_VERSION +``` + +**NOTE**: In theory we'll be able to handle this more gracefully in the future. +[We're not able to access the `steps` context within an `if` expression yet](https://github.com/actions/toolkit/issues/96). + +**DOES NOT WORK YET:** + +```yml +steps: + - uses: actions/checkout@master + + - uses: codfish/semantic-release-action@master + id: semantic + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - if: steps.semantic.outputs.new-release-published == 'true' + run: echo "A new release was published!!" +``` + +Using environment variables set by `semantic-release-action`: ```yml -jobs: - release: - runs-on: ubuntu-latest +steps: + - uses: actions/checkout@master - steps: - - uses: actions/checkout@master + - uses: codfish/semantic-release-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - uses: codfish/actions/semantic-release@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + - run: | + echo $RELEASE_VERSION + echo $RELEASE_MAJOR + echo $RELEASE_MINOR + echo $RELEASE_PATCH ``` -If you're not publishing to npm and only want to use this action for GitHub releases, you'd only -need to provide a `GITHUB_TOKEN` env var. You'll also need to include a `.releaserc.js` file in your -repo, instructing `semantic-release` to not look to publish to `npm`. +If you're _not_ publishing to npm and only want to use this action for GitHub releases, you need to +include a `.releaserc.js` file in your repo, instructing `semantic-release` to not publish to the +`npm` registry. -**Example:** +Example `.releaserc.js` if you're not publishing to npm: ```js module.exports = { @@ -73,18 +129,71 @@ module.exports = { }; ``` -### Inputs +## Why -None. Might be a good idea to accept optional configuration values via inputs that would merge, yet -override any `.releaserc.js` config file values. +It's fairly easy to run `semantic-release` as a "host action," aka something that simply runs +directly on the VM. + +```yml +steps: + - run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} +``` + +If you're just publishing a node package, then this could still work well for you. The problem I +found with this is when I was in projects where I had subsequent steps/actions in which I wanted to +know whether a new version was cut. + +> **Use Case:** For instance, in an application where I'm using `semantic-release` to manage GitHub +> releases, but also building and pushing docker images. Dockerhub has a +> [nice GitHub integration](https://docs.docker.com/docker-hub/builds/) to handle this for us, but +> some other registries do not. If I need to cut a new release, then update a docker registry by +> adding a new tagged build, I'll want to run `semantic-release` and then build a docker image, tag +> it with a version and push it up. In my case I like to push up tags for `latest`, the semver (i.e. +> `v1.8.3`), and just the major the version (i.e. `v1`). + +I want to know 1) if semantic-release cut a new version and 2) what the version is. + +There's a number of ways to handle this, but the most elegant way I found to do it was to abstract +it into it's own custom action. It abstracts away whatever logic you need to figure out what that +new release number is. + +This also scales well, just in case I want to add some flexibility and functionality to this action, +I can easily leverage it across any project. + +## Inputs + +**Docs:** https://help.github.com/en/articles/metadata-syntax-for-github-actions#inputs + +None yet. Might be a good idea to accept optional configuration values via inputs that would merge, +yet override any `.releaserc.js` config file values. i.e. `branch`, etc. ### Outputs +`semantic-release-action` sets both output variables and environment variables because why not? +Allows users the ability to use whichever they want. I don't know or understand every use case there +might be so this is a way to cover more cases. + +**Docs:** https://help.github.com/en/articles/metadata-syntax-for-github-actions#outputs + +**Output Variables**: + +| Output Variable | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| new-release-published | Either `'true'` when a new release was published or `'false'` when no release was published. Allows other actions to run or not-run based on this | +| release-version | The new releases' semantic version, i.e. `1.8.3` | +| release-major | The new releases' major version number, i.e. `1` | +| release-minor | The new releases' minor version number, i.e. `8` | +| release-patch | The new releases' patch version number, i.e. `3` | + **Environment Variables**: -- `SEMANTIC_RELEASE`: Either `'true'` or `'false'`. This allows other actions to run or not-run - based on this. -- `SEMANTIC_RELEASE_VERSION`: The new release's semantic version, i.e. `1.8.3`. -- `SEMANTIC_RELEASE_MAJOR`: The new release's major version number, i.e. `1`. -- `SEMANTIC_RELEASE_MINOR`: The new release's minor version number, i.e. `8`. -- `SEMANTIC_RELEASE_PATCH`: The new release's patch version number, i.e. `3`. +| Environment Variable | Description | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| NEW_RELEASE_PUBLISHED | Either `'true'` when a new release was published or `'false'` when no release was published. Allows other actions to run or not-run based on this | +| RELEASE_VERSION | The new releases' semantic version, i.e. `1.8.3` | +| RELEASE_MAJOR | The new releases' major version number, i.e. `1` | +| RELEASE_MINOR | The new releases' minor version number, i.e. `8` | +| RELEASE_PATCH | The new releases' patch version number, i.e. `3` | diff --git a/action.yml b/action.yml index a16389c..3b093aa 100644 --- a/action.yml +++ b/action.yml @@ -4,6 +4,17 @@ author: 'codfish' branding: icon: 'arrow-up-right' color: 'orange' +outputs: + new-release-published: + description: "Either `'true'` when a new release was published or `'false'` when no release was published" + release-version: + description: "The new releases' semantic version, i.e. `1.8.3`" + release-major: + description: "The new releases' major version number, i.e. `1`" + release-minor: + description: "The new releases' minor version number, i.e. `8`" + release-patch: + description: "The new releases' patch version number, i.e. `3`" runs: using: 'docker' image: 'Dockerfile' diff --git a/entrypoint.js b/entrypoint.js index 175fdcc..4fcd275 100644 --- a/entrypoint.js +++ b/entrypoint.js @@ -1,41 +1,45 @@ const core = require('@actions/core'); const semanticRelease = require('semantic-release'); -async function main() { - try { - const result = await semanticRelease(); - - if (result) { - const { lastRelease, nextRelease, commits } = result; - - core.debug( - `Published ${nextRelease.type} release version ${nextRelease.version} containing ${commits.length} commits.`, - ); - - if (lastRelease.version) { - core.debug(`The last release was "${lastRelease.version}".`); - } - - // outputs - const { version } = nextRelease; - const major = version.split('.')[0]; - const minor = version.split('.')[1]; - const patch = version.split('.')[2]; - - core.exportVariable('SEMANTIC_RELEASE', true); - core.exportVariable('SEMANTIC_RELEASE_VERSION', version); - core.exportVariable('SEMANTIC_RELEASE_MAJOR', major); - core.exportVariable('SEMANTIC_RELEASE_MINOR', minor); - core.exportVariable('SEMANTIC_RELEASE_PATCH', patch); - - // TODO: core.setOutput as well? What are outputs, how do they differ from env vars? - } else { - core.exportVariable('SEMANTIC_RELEASE', false); - core.debug('No release published'); - } - } catch (err) { - core.setFailed(`Action failed with error ${err}`); +async function run() { + const result = await semanticRelease(); + + if (!result) { + core.debug('No release published'); + + // set outputs + core.exportVariable('NEW_RELEASE_PUBLISHED', 'false'); + core.setOutput('new-release-published', 'false'); + return; } + + const { lastRelease, nextRelease, commits } = result; + + core.debug( + `Published ${nextRelease.type} release version ${nextRelease.version} containing ${commits.length} commits.`, + ); + + if (lastRelease.version) { + core.debug(`The last release was "${lastRelease.version}".`); + } + + // outputs + const { version } = nextRelease; + const major = version.split('.')[0]; + const minor = version.split('.')[1]; + const patch = version.split('.')[2]; + + // set outputs + core.exportVariable('NEW_RELEASE_PUBLISHED', 'true'); + core.exportVariable('RELEASE_VERSION', version); + core.exportVariable('RELEASE_MAJOR', major); + core.exportVariable('RELEASE_MINOR', minor); + core.exportVariable('RELEASE_PATCH', patch); + core.setOutput('new-release-published', 'true'); + core.setOutput('release-version', version); + core.setOutput('release-major', major); + core.setOutput('release-minor', minor); + core.setOutput('release-patch', patch); } -main(); +run().catch(core.setFailed);