mirror of
https://github.com/ButlerLogic/action-autotag.git
synced 2024-11-24 09:11:08 +07:00
Merge pull request #15 from ButlerLogic/master
Updating stable version to 1.1.0
This commit is contained in:
commit
88cc59cb86
63
.github/workflows/test.yml
vendored
Normal file
63
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
name: Autotag Action Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
package:
|
||||
name: Test Suite - Package Strategy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout updated source code
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# If the version has changed, create a new git tag for it.
|
||||
- name: Tag Package
|
||||
id: package_autotagger
|
||||
uses: butlerlogic/action-autotag@master
|
||||
with:
|
||||
tag_prefix: test_package_
|
||||
commit_message_template: "{{number}}) {{message}} ({{author}})\n"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Rollback Package Test Release
|
||||
id: package_rollback
|
||||
if: success() && ${{ steps.package_autotagger.outputs.tagcreated }} = "yes"
|
||||
uses: author/action-rollback@master
|
||||
with:
|
||||
tag: ${{ steps.package_autotagger.outputs.tagrequested }}
|
||||
delete_orphan_tag: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
docker:
|
||||
name: Test Suite - Docker Strategy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout updated source code
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# If the version has changed, create a new git tag for it.
|
||||
- name: Tag Dockerfile
|
||||
id: docker_autotagger
|
||||
uses: butlerlogic/action-autotag@master
|
||||
with:
|
||||
strategy: docker
|
||||
tag_prefix: test_docker_
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Rollback Docker Test Release
|
||||
id: docker_rolback
|
||||
if: success() && ${{ steps.docker_autotagger.outputs.tagcreated }} = "yes"
|
||||
uses: author/action-rollback@master
|
||||
env:
|
||||
TAG: ${{ steps.docker_autotagger.outputs.tagrequested }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag: ${{ steps.docker_autotagger.outputs.tagrequested }}
|
||||
delete_orphan_tag: true
|
||||
|
||||
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -1,16 +1,9 @@
|
||||
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
*.log*
|
||||
.*
|
||||
!.gitignore
|
||||
!.github
|
||||
!.dockerignore
|
||||
_*
|
||||
node_modules
|
||||
|
6
Dockerfile
Normal file
6
Dockerfile
Normal file
@ -0,0 +1,6 @@
|
||||
FROM node:13-alpine
|
||||
LABEL version=1.1.0
|
||||
ADD ./app /app
|
||||
WORKDIR /app
|
||||
RUN cd /app && npm i
|
||||
CMD ["node", "/app/main.js"]
|
2
LICENSE
2
LICENSE
@ -1,7 +1,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Corey Butler and contributors
|
||||
Copyright (c) 2020 Corey Butler and contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
148
README.md
148
README.md
@ -1,8 +1,14 @@
|
||||
# Autotag
|
||||
|
||||
This action will read a `package.json` file and compare the `version` attribute to the project's known tags. If a corresponding tag does not exist, it will be created.
|
||||
This action will auto-generate a Github tag whenever a new version is detected. The following "detection strategies" are available:
|
||||
|
||||
This tag works well in combination with:
|
||||
1. **package**: Monitor a `package.json` for new versions.
|
||||
1. **docker**: Monitor a `Dockerfile` for a `LABEL version=x.x.x` value.
|
||||
1. **regex**: Use a JavaScript [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) with any file for your own custom extraction.
|
||||
|
||||
When a version is detected, it is compared to the current list of tags in the Github repository. If a tag does not exist, it will be created.
|
||||
|
||||
This action works well in combination with:
|
||||
|
||||
- [actions/create-release](https://github.com/actions/create-release) (Auto-release)
|
||||
- [author/action-publish](https://github.com/author/action-publish) (Auto-publish JavaScript/Node modules)
|
||||
@ -44,7 +50,7 @@ This **order** is important!
|
||||
|
||||
## Configuration
|
||||
|
||||
The `GITHUB_TOKEN` must be passed in. Without this, it is not possible to create a new tag. Make sure the autotag action looks like the following example:
|
||||
The `GITHUB_TOKEN` **must** be provided. Without this, it is not possible to create a new tag. Make sure the autotag action looks like the following example:
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@stable
|
||||
@ -58,62 +64,108 @@ The action will automatically extract the token at runtime. **DO NOT MANUALLY EN
|
||||
|
||||
There are several options to customize how the tag is created.
|
||||
|
||||
1. `package_root`
|
||||
#### strategy
|
||||
|
||||
By default, autotag will look for the `package.json` file in the project root. If the file is located in a subdirectory, this option can be used to point to the correct file.
|
||||
This is the strategy used to identify the version number/tag from within the code base.
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
package_root: "/path/to/subdirectory"
|
||||
```
|
||||
1. _package_: Monitor a `package.json` for new versions. Use this for JavaScript projects based on Node modules (npm, yarn, etc).
|
||||
1. _docker_: Monitor a `Dockerfile` for a `LABEL version=x.x.x` value. USe this for container projects.
|
||||
1. _regex*_: Use a JavaScript [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) with any file for your own custom extraction.
|
||||
|
||||
1. `tag_prefix`
|
||||
*An example "
|
||||
|
||||
By default, `package.json` uses [semantic versioning](https://semver.org/), such as `1.0.0`. A prefix can be used to add text before the tag name. For example, if `tag_prefix` is set to `v`, then the tag would be labeled as `v1.0.0`.
|
||||
#### root `(required)`
|
||||
_Formerly `package_root`_
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
tag_prefix: "v"
|
||||
```
|
||||
By default, autotag will look for the `package.json` file in the project root. If the file is located in a subdirectory, this option can be used to point to the correct file.
|
||||
|
||||
1. `tag_suffix`
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
root: "/path/to/subdirectory"
|
||||
```
|
||||
|
||||
Text can also be applied to the end of the tag by setting `tag_suffix`. For example, if `tag_suffix` is ` (beta)`, the tag would be `1.0.0 (beta)`. Please note this example violates semantic versioning and is merely here to illustrate how to add text to the end of a tag name if you _really_ want to.
|
||||
> **EXCEPTION**: This property is not required if the regex_pattern property is defined. In that case, this property is assumed to be "regex".
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
tag_suffix: " (beta)"
|
||||
```
|
||||
#### tag_prefix
|
||||
|
||||
1. `tag_message`
|
||||
By default, [semantic versioning](https://semver.org/) is used, such as `1.0.0`. A prefix can be used to add text before the tag name. For example, if `tag_prefix` is set to `v`, then the tag would be labeled as `v1.0.0`.
|
||||
|
||||
This is the annotated commit message associated with the tag. By default, a
|
||||
changelog will be generated from the commits between the latest tag and the new tag (HEAD). Setting this option will override it witha custom message.
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
tag_prefix: "v"
|
||||
```
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
tag_message: "Custom message goes here."
|
||||
```
|
||||
#### tag_suffix
|
||||
|
||||
1. `version`
|
||||
Text can be applied to the end of the tag by setting `tag_suffix`. For example, if `tag_suffix` is ` (beta)`, the tag would be `1.0.0 (beta)`. Please note this example violates semantic versioning and is merely here to illustrate how to add text to the end of a tag name if you _really_ want to.
|
||||
|
||||
Explicitly set the version instead of automatically detecting from `package.json`.
|
||||
Useful for non-JavaScript projects where version may be output by a previous action.
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
tag_suffix: " (beta)"
|
||||
```
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
version: "${{ steps.previous_step.outputs.version }}"
|
||||
```
|
||||
#### tag_message
|
||||
|
||||
This is the annotated commit message associated with the tag. By default, a changelog will be generated from the commits between the latest tag and the current reference (HEAD). Setting this option will override the message.
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
tag_message: "Custom message goes here."
|
||||
```
|
||||
|
||||
#### commit_message_template
|
||||
|
||||
By default, a changelog is generated, containing the commit messages since the last release. The message is generated by applying a commit message template to each commit's data attributes.
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
commit_message_template: "({{sha}} by {{author}}) {{message}}"
|
||||
```
|
||||
|
||||
Optional data points:
|
||||
|
||||
1. `number` The commit number (relevant to the overall list)
|
||||
1. `message` The commit message.
|
||||
1. `author` The author of the commit.
|
||||
1. `sha` The SHA value representing the commit.
|
||||
|
||||
The default is `{{number}}) {{message}} ({{author}})\nSHA: {{sha}}\n`.
|
||||
|
||||
#### version
|
||||
|
||||
Explicitly set the version instead of using automatic detection.
|
||||
|
||||
Useful for projects where the version number may be output by a previous action.
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
version: "${{ steps.previous_step.outputs.version }}"
|
||||
```
|
||||
|
||||
#### regex_pattern
|
||||
|
||||
An optional attribute containing the regular expression used to extract the version number.
|
||||
|
||||
```yaml
|
||||
- uses: butlerlogic/action-autotag@1.0.0
|
||||
with:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
regex_pattern: "version=([0-9\.]+)"
|
||||
```
|
||||
|
||||
This attribute is used as the first argument of a [RegExp](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/RegExp) object. The first "group" (i.e. what's in parenthesis) will be used as the version number. For an example, see this [working example](regexr.com/51i6n).
|
||||
|
||||
## Developer Notes
|
||||
|
||||
@ -123,6 +175,8 @@ If you are building an action that runs after this one, be aware this action pro
|
||||
1. `tagsha`: The SHA of the new tag.
|
||||
1. `taguri`: The URI/URL of the new tag reference.
|
||||
1. `tagmessage`: The messge applied to the tag reference (this is what shows up on the tag screen on Github).
|
||||
1. `tagcreated`: `yes` or `no`.
|
||||
1. `tagrequested`: The name of the requested tag. This will be populated even if the tag is not created. This will usually be the same as `tagname` and/or `version` for successful executions.
|
||||
1. `version` will be the version attribute found in the `package.json` file.
|
||||
|
||||
---
|
||||
@ -135,10 +189,10 @@ This action was written and is primarily maintained by [Corey Butler](https://gi
|
||||
|
||||
If you use this or find value in it, please consider contributing in one or more of the following ways:
|
||||
|
||||
1. Click the "Sponsor" button at the top of the page.
|
||||
1. Click the "Sponsor" button at the top of the page and make a contribution.
|
||||
1. Star it!
|
||||
1. [Tweet about it!](https://twitter.com/intent/tweet?hashtags=github,actions&original_referer=http%3A%2F%2F127.0.0.1%3A91%2F&text=I%20am%20automating%20my%20workflow%20with%20the%20Autotagger%20Github%20action!&tw_p=tweetbutton&url=https%3A%2F%2Fgithub.com%2Fmarketplace%2Factions%2Fautotagger&via=goldglovecb)
|
||||
1. Fix an issue.
|
||||
1. Add a feature (post a proposal in an issue first!).
|
||||
|
||||
Copyright © 2019 ButlerLogic, Corey Butler, and Contributors.
|
||||
Copyright © 2020 Butler Logic, Corey Butler, and Contributors.
|
||||
|
31
action.yml
31
action.yml
@ -1,14 +1,21 @@
|
||||
name: "Autotagger"
|
||||
description: "Automatically generate new tags when the package.json version changes."
|
||||
author: "ButlerLogic"
|
||||
description: "Automatically generate new tags for new versions. Supports several tagging strategies."
|
||||
author: "Butler Logic"
|
||||
branding:
|
||||
icon: "tag"
|
||||
color: "black"
|
||||
color: "blue"
|
||||
inputs:
|
||||
package_root:
|
||||
description: Autotag will look for the package.json file in in this location.
|
||||
root:
|
||||
description: Autotag will look for the appropriate file in in this location (relative to project root).
|
||||
required: false
|
||||
default: './'
|
||||
strategy:
|
||||
description: Options include 'package' (for package.json), 'docker' (for Dockerfile), and 'regex' to extract from an arbitrary file. This does not need to be specified if the "regex_pattern" property is provided.
|
||||
required: false
|
||||
default: 'package'
|
||||
package_root:
|
||||
description: (DEPRECATED. Use 'root' instead.) Autotag will look for the package.json file in in this location.
|
||||
required: false
|
||||
tag_prefix:
|
||||
description: By default, package.json uses semantic versioning, such as "1.0.0". A prefix can be used to add text before the tag name. For example, if tag_prefx is set to "v", then the tag would be labeled as "v1.0.0".
|
||||
required: false
|
||||
@ -18,9 +25,15 @@ inputs:
|
||||
tag_message:
|
||||
description: This is the annotated commit message associated with the tag. By default, a changelog will be generated from the commits between the latest tag and the new tag (HEAD). This will override that with a hard-coded message.
|
||||
required: false
|
||||
commit_message_template:
|
||||
description: "The commit message template (per commit). Default is `{{number}}) {{message}} ({{author}})\nSHA: {{sha}}\n`"
|
||||
required: false
|
||||
version:
|
||||
description: Explicitly set the version here instead of automatically detecting from `package.json`. Useful for non-JavaScript projects where version may be output by a previous action.
|
||||
required: false
|
||||
regex_pattern:
|
||||
description: An optional attribute containing the regular expression used to extract the version number.
|
||||
required: false
|
||||
outputs:
|
||||
tagname:
|
||||
description: Returns the new tag value. Empty if a tag is not created.
|
||||
@ -30,8 +43,12 @@ outputs:
|
||||
description: The URI/URL of the new tag reference.
|
||||
tagmessage:
|
||||
description: The messge applied to the tag reference (this is what shows up on the tag screen on Github).
|
||||
tagcreated:
|
||||
description: A "yes" or "no", indicating a new tag was created.
|
||||
tagrequested:
|
||||
description: The name of the requested tag. This will be populated even if the tag is not created.
|
||||
version:
|
||||
description: The version, as defined in package.json or explicitly set in the input.
|
||||
runs:
|
||||
using: "node12"
|
||||
main: "lib/main.js"
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
|
16
app/lib/docker.js
Normal file
16
app/lib/docker.js
Normal file
@ -0,0 +1,16 @@
|
||||
import Regex from './regex.js'
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import core from '@actions/core'
|
||||
|
||||
export default class Dockerfile extends Regex {
|
||||
constructor (root = null) {
|
||||
root = path.join(process.env.GITHUB_WORKSPACE, root)
|
||||
|
||||
if (fs.statSync(root).isDirectory()) {
|
||||
root = path.join(root, 'Dockerfile')
|
||||
}
|
||||
|
||||
super(root, /LABEL[\s\t]+version=[\t\s+]?[\"\']?([0-9\.]+)[\"\']?/i)
|
||||
}
|
||||
}
|
23
app/lib/package.js
Normal file
23
app/lib/package.js
Normal file
@ -0,0 +1,23 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export default class Package {
|
||||
constructor (root = './') {
|
||||
root = path.join(process.env.GITHUB_WORKSPACE, root)
|
||||
|
||||
if (fs.statSync(root).isDirectory()) {
|
||||
root = path.join(root, 'package.json')
|
||||
}
|
||||
|
||||
if (!fs.existsSync(root)) {
|
||||
throw new Error(`package.json does not exist at ${root}.`)
|
||||
}
|
||||
|
||||
this.root = root
|
||||
this.data = JSON.parse(fs.readFileSync(root))
|
||||
}
|
||||
|
||||
get version () {
|
||||
return this.data.version
|
||||
}
|
||||
}
|
34
app/lib/regex.js
Normal file
34
app/lib/regex.js
Normal file
@ -0,0 +1,34 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export default class Regex {
|
||||
constructor (root = './', pattern) {
|
||||
root = path.resolve(root)
|
||||
|
||||
if (fs.statSync(root).isDirectory()) {
|
||||
throw new Error(`${root} is a directory. The Regex tag identification strategy requires a file.`)
|
||||
}
|
||||
|
||||
if (!fs.existsSync(root)) {
|
||||
throw new Error(`"${root}" does not exist.`)
|
||||
}
|
||||
|
||||
this.content = fs.readFileSync(root).toString()
|
||||
|
||||
let content = pattern.exec(this.content)
|
||||
if (!content) {
|
||||
this._version = null
|
||||
// throw new Error(`Could not find pattern matching "${pattern.toString()}" in "${root}".`)
|
||||
} else {
|
||||
this._version = content[1]
|
||||
}
|
||||
}
|
||||
|
||||
get version () {
|
||||
return this._version
|
||||
}
|
||||
|
||||
get versionFound () {
|
||||
return this._version !== null
|
||||
}
|
||||
}
|
33
app/lib/setup.js
Normal file
33
app/lib/setup.js
Normal file
@ -0,0 +1,33 @@
|
||||
import core from '@actions/core'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export default class Setup {
|
||||
static debug () {
|
||||
// Metadate for debugging
|
||||
core.debug(
|
||||
` Available environment variables:\n -> ${Object.keys(process.env)
|
||||
.map(i => i + ' :: ' + process.env[i])
|
||||
.join('\n -> ')}`
|
||||
)
|
||||
|
||||
const dir = fs
|
||||
.readdirSync(path.resolve(process.env.GITHUB_WORKSPACE), { withFileTypes: true })
|
||||
.map(entry => {
|
||||
return `${entry.isDirectory() ? '> ' : ' - '}${entry.name}`
|
||||
})
|
||||
.join('\n')
|
||||
|
||||
core.debug(` Working Directory: ${process.env.GITHUB_WORKSPACE}:\n${dir}`)
|
||||
}
|
||||
|
||||
static requireAnyEnv () {
|
||||
for (const arg of arguments) {
|
||||
if (!process.env.hasOwnProperty(arg)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('At least one of the following environment variables is required: ' + Array.slice(arguments).join(', '))
|
||||
}
|
||||
}
|
161
app/lib/tag.js
Normal file
161
app/lib/tag.js
Normal file
@ -0,0 +1,161 @@
|
||||
import core from '@actions/core'
|
||||
import os from 'os'
|
||||
import gh from '@actions/github'
|
||||
|
||||
// Get authenticated GitHub client (Ocktokit): https://github.com/actions/toolkit/tree/master/packages/github#usage
|
||||
const github = new gh.GitHub(process.env.GITHUB_TOKEN || process.env.INPUT_GITHUB_TOKEN)
|
||||
// Get owner and repo from context of payload that triggered the action
|
||||
const { owner, repo } = gh.context.repo
|
||||
|
||||
export default class Tag {
|
||||
constructor (prefix, version, postfix) {
|
||||
this.prefix = prefix
|
||||
this.version = version
|
||||
this.postfix = postfix
|
||||
this._tags = null
|
||||
this._message = null
|
||||
this._exists = null
|
||||
this._sha = ''
|
||||
this._uri = ''
|
||||
this._ref = ''
|
||||
}
|
||||
|
||||
get name () {
|
||||
return `${this.prefix.trim()}${this.version.trim()}${this.postfix.trim()}`
|
||||
}
|
||||
|
||||
set message (value) {
|
||||
if (value && value.length > 0) {
|
||||
this._message = value
|
||||
}
|
||||
}
|
||||
|
||||
get sha () {
|
||||
return this._sha || ''
|
||||
}
|
||||
|
||||
get uri () {
|
||||
return this._uri || ''
|
||||
}
|
||||
|
||||
get ref () {
|
||||
return this._ref || ''
|
||||
}
|
||||
|
||||
async getMessage () {
|
||||
if (this._message !== null) {
|
||||
return this._message
|
||||
}
|
||||
|
||||
try {
|
||||
let tags = await this.getTags()
|
||||
|
||||
if (tags.length === 0) {
|
||||
return `Version ${this.version}`
|
||||
}
|
||||
|
||||
const changelog = await github.repos.compareCommits({ owner, repo, base: tags.shift().name, head: 'master' })
|
||||
const tpl = (core.getInput('commit_message_template', { required: false }) || '').trim()
|
||||
|
||||
return changelog.data.commits
|
||||
.map(
|
||||
(commit, i) => {
|
||||
if (tpl.length > 0) {
|
||||
return tpl
|
||||
.replace(/\{\{\s?(number)\s?\}\}/gi, i + 1)
|
||||
.replace(/\{\{\s?(message)\s?\}\}/gi, commit.commit.message)
|
||||
.replace(/\{\{\s?(author)\s?\}\}/gi, commit.hasOwnProperty('author') ? (commit.author.hasOwnProperty('login') ? commit.author.login : '') : '')
|
||||
.replace(/\{\{\s?(sha)\s?\}\}/gi, commit.sha)
|
||||
.trim() + '\n'
|
||||
} else {
|
||||
return `${i === 0 ? '\n' : ''}${i + 1}) ${commit.commit.message}${
|
||||
commit.hasOwnProperty('author')
|
||||
? commit.author.hasOwnProperty('login')
|
||||
? ' (' + commit.author.login + ')'
|
||||
: ''
|
||||
: ''
|
||||
}\n(SHA: ${commit.sha})\n`
|
||||
}
|
||||
})
|
||||
.join('\n')
|
||||
} catch (e) {
|
||||
core.warning('Failed to generate changelog from commits: ' + e.message + os.EOL)
|
||||
return `Version ${this.version}`
|
||||
}
|
||||
}
|
||||
|
||||
async getTags () {
|
||||
if (this._tags !== null) {
|
||||
return this._tags.data
|
||||
}
|
||||
|
||||
this._tags = await github.repos.listTags({ owner, repo, per_page: 100 })
|
||||
|
||||
return this._tags.data
|
||||
}
|
||||
|
||||
async exists () {
|
||||
if (this._exists !== null) {
|
||||
return this._exists
|
||||
}
|
||||
const currentTag = this.name
|
||||
const tags = await this.getTags()
|
||||
|
||||
for (const tag of tags) {
|
||||
if (tag.name === currentTag) {
|
||||
this._exists = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
this._exists = false
|
||||
return false
|
||||
}
|
||||
|
||||
async push () {
|
||||
let tagexists = await this.exists()
|
||||
|
||||
if (!tagexists) {
|
||||
// Create tag
|
||||
const newTag = await github.git.createTag({
|
||||
owner,
|
||||
repo,
|
||||
tag: this.name,
|
||||
message: await this.getMessage(),
|
||||
object: process.env.GITHUB_SHA,
|
||||
type: 'commit'
|
||||
})
|
||||
|
||||
this._sha = newTag.data.sha
|
||||
core.warning(`Created new tag: ${newTag.data.tag}`)
|
||||
|
||||
// Create reference
|
||||
let newReference
|
||||
|
||||
try {
|
||||
newReference = await github.git.createRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/tags/${newTag.data.tag}`,
|
||||
sha: newTag.data.sha
|
||||
})
|
||||
} catch (e) {
|
||||
core.warning({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/tags/${newTag.data.tag}`,
|
||||
sha: newTag.data.sha
|
||||
})
|
||||
|
||||
throw e
|
||||
}
|
||||
|
||||
this._uri = newReference.data.url
|
||||
this._ref = newReference.data.ref
|
||||
|
||||
core.warning(`Reference ${newReference.data.ref} available at ${newReference.data.url}` + os.EOL)
|
||||
} else {
|
||||
core.warning('Cannot push tag (it already exists).')
|
||||
}
|
||||
}
|
||||
}
|
87
app/main.js
Normal file
87
app/main.js
Normal file
@ -0,0 +1,87 @@
|
||||
import core from '@actions/core'
|
||||
import os from 'os'
|
||||
import Setup from './lib/setup.js'
|
||||
import Package from './lib/package.js'
|
||||
import Tag from './lib/tag.js'
|
||||
import Regex from './lib/regex.js'
|
||||
import Dockerfile from './lib/docker.js'
|
||||
|
||||
async function run () {
|
||||
try {
|
||||
Setup.debug()
|
||||
Setup.requireAnyEnv('GITHUB_TOKEN', 'INPUT_GITHUB_TOKEN')
|
||||
|
||||
// Configure the default output
|
||||
core.setOutput('tagcreated', 'no')
|
||||
|
||||
// Identify the tag parsing strategy
|
||||
const root = core.getInput('root', { required: false }) || core.getInput('package_root', { required: false }) || './'
|
||||
const strategy = (core.getInput('regex_pattern', { required: false }) || '').trim().length > 0 ? 'regex' : ((core.getInput('strategy', { required: false }) || 'package').trim().toLowerCase())
|
||||
core.warning(`Attempting to use ${strategy} version extraction strategy.`)
|
||||
|
||||
// Extract the version number using the supplied strategy
|
||||
let version = core.getInput('root', { required: false })
|
||||
version = version === null || version.trim().length === 0 ? null : version
|
||||
|
||||
switch (strategy) {
|
||||
case 'docker':
|
||||
version = (new Dockerfile(root)).version
|
||||
break
|
||||
|
||||
case 'package':
|
||||
// Extract using the package strategy (this is the default strategy)
|
||||
version = (new Package(root)).version
|
||||
break
|
||||
|
||||
case 'regex':
|
||||
version = (new Regex(root, new RegExp(core.getInput('regex_pattern', { required: false }), 'i'))).version
|
||||
break
|
||||
|
||||
default:
|
||||
core.setFailed(`"${strategy}" is not a recognized tagging strategy. Choose from: 'package' (package.json), 'docker' (uses Dockerfile), or 'regex' (JS-based RegExp).`)
|
||||
return
|
||||
}
|
||||
|
||||
core.setOutput('version', version)
|
||||
core.debug(` Detected version ${version}`)
|
||||
|
||||
// Configure a tag using the identified version
|
||||
const tag = new Tag(
|
||||
core.getInput('tag_prefix', { required: false }),
|
||||
version,
|
||||
core.getInput('tag_suffix', { required: false })
|
||||
)
|
||||
|
||||
core.warning(`Attempting to create ${tag.name} tag.`)
|
||||
core.setOutput('tagrequested', tag.name)
|
||||
|
||||
// Check for existance of tag and abort (short circuit) if it already exists.
|
||||
if (await tag.exists()) {
|
||||
core.warning(`"${tag.name}" tag already exists.` + os.EOL)
|
||||
core.setOutput('tagname', '')
|
||||
return
|
||||
}
|
||||
|
||||
// The tag setter will autocorrect the message if necessary.
|
||||
tag.message = core.getInput('tag_message', { required: false }).trim()
|
||||
await tag.push()
|
||||
|
||||
core.setOutput('tagname', tag.name)
|
||||
core.setOutput('tagsha', tag.sha)
|
||||
core.setOutput('taguri', tag.uri)
|
||||
core.setOutput('tagmessage', tag.message)
|
||||
core.setOutput('tagref', tag.ref)
|
||||
core.setOutput('tagcreated', 'yes')
|
||||
} catch (error) {
|
||||
core.warning(error.message)
|
||||
core.warning(error.stack)
|
||||
core.setOutput('tagname', '')
|
||||
core.setOutput('tagsha', '')
|
||||
core.setOutput('taguri', '')
|
||||
core.setOutput('tagmessage', '')
|
||||
core.setOutput('tagref', '')
|
||||
core.setOutput('tagcreated', 'no')
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
0
package-lock.json → app/package-lock.json
generated
0
package-lock.json → app/package-lock.json
generated
26
app/package.json
Normal file
26
app/package.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "autotag-action",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"description": "Automatically create a tag whenever the version changes in package.json",
|
||||
"main": "lib/main.js",
|
||||
"scripts": {
|
||||
"start": "node ./main.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/butlerlogic/action-autotag.git"
|
||||
},
|
||||
"keywords": [
|
||||
"actions",
|
||||
"node",
|
||||
"setup"
|
||||
],
|
||||
"author": "ButlerLogic",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.2",
|
||||
"@actions/github": "^2.1.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
174
lib/main.js
174
lib/main.js
@ -1,174 +0,0 @@
|
||||
const core = require('@actions/core')
|
||||
const { GitHub, context } = require('@actions/github')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
core.debug(
|
||||
` Available environment variables:\n -> ${Object.keys(process.env)
|
||||
.map(i => i + ' :: ' + process.env[i])
|
||||
.join('\n -> ')}`
|
||||
)
|
||||
|
||||
const dir = fs
|
||||
.readdirSync(path.resolve(process.env.GITHUB_WORKSPACE), { withFileTypes: true })
|
||||
.map(entry => {
|
||||
return `${entry.isDirectory() ? '> ' : ' - '}${entry.name}`
|
||||
})
|
||||
.join('\n')
|
||||
|
||||
core.debug(` Working Directory: ${process.env.GITHUB_WORKSPACE}:\n${dir}`)
|
||||
|
||||
if (!process.env.hasOwnProperty('GITHUB_TOKEN')) {
|
||||
if (!process.env.hasOwnProperty('INPUT_GITHUB_TOKEN')) {
|
||||
core.setFailed('Invalid or missing GITHUB_TOKEN.')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const pkg_root = core.getInput('package_root', { required: false })
|
||||
const pkgfile = path.join(process.env.GITHUB_WORKSPACE, pkg_root, 'package.json')
|
||||
if (!fs.existsSync(pkgfile)) {
|
||||
core.setFailed('package.json does not exist.')
|
||||
return
|
||||
}
|
||||
|
||||
const pkg = require(pkgfile)
|
||||
core.setOutput('version', pkg.version)
|
||||
core.debug(` Detected version ${pkg.version}`)
|
||||
|
||||
// Get authenticated GitHub client (Ocktokit): https://github.com/actions/toolkit/tree/master/packages/github#usage
|
||||
const github = new GitHub(process.env.GITHUB_TOKEN || process.env.INPUT_GITHUB_TOKEN)
|
||||
|
||||
// Get owner and repo from context of payload that triggered the action
|
||||
const { owner, repo } = context.repo
|
||||
|
||||
// // Check for existing tag
|
||||
// const git = new github.GitHub(process.env.INPUT_GITHUB_TOKEN || process.env.GITHUB_TOKEN)
|
||||
// const owner = process.env.GITHUB_REPOSITORY.split('/').shift()
|
||||
// const repo = process.env.GITHUB_REPOSITORY.split('/').pop()
|
||||
|
||||
let tags
|
||||
try {
|
||||
tags = await github.repos.listTags({
|
||||
owner,
|
||||
repo,
|
||||
per_page: 100,
|
||||
})
|
||||
} catch (e) {
|
||||
tags = {
|
||||
data: [],
|
||||
}
|
||||
}
|
||||
|
||||
const tagPrefix = core.getInput('tag_prefix', { required: false })
|
||||
const tagSuffix = core.getInput('tag_suffix', { required: false })
|
||||
|
||||
const getTagName = version => {
|
||||
return `${tagPrefix}${version}${tagSuffix}`
|
||||
}
|
||||
|
||||
// Check for existance of tag and abort (short circuit) if it already exists.
|
||||
for (let tag of tags.data) {
|
||||
if (tag.name === getTagName(pkg.version)) {
|
||||
core.warning(`"${tag.name.trim()}" tag already exists.` + os.EOL)
|
||||
core.setOutput('tagname', '')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Create the new tag name
|
||||
const tagName = getTagName(pkg.version)
|
||||
|
||||
let tagMsg = core.getInput('tag_message', { required: false }).trim()
|
||||
if (tagMsg.length === 0 && tags.data.length > 0) {
|
||||
try {
|
||||
latestTag = tags.data.shift()
|
||||
|
||||
let changelog = await github.repos.compareCommits({
|
||||
owner,
|
||||
repo,
|
||||
base: latestTag.name,
|
||||
head: 'master',
|
||||
})
|
||||
|
||||
tagMsg = changelog.data.commits
|
||||
.map(
|
||||
commit =>
|
||||
`**1) ${commit.commit.message}**${
|
||||
commit.hasOwnProperty('author')
|
||||
? commit.author.hasOwnProperty('login')
|
||||
? ' (' + commit.author.login + ')'
|
||||
: ''
|
||||
: ''
|
||||
}\n(SHA: ${commit.sha})\n`
|
||||
)
|
||||
.join('\n')
|
||||
} catch (e) {
|
||||
core.warning('Failed to generate changelog from commits: ' + e.message + os.EOL)
|
||||
tagMsg = tagName
|
||||
}
|
||||
}
|
||||
|
||||
let newTag
|
||||
try {
|
||||
tagMsg = tagMsg.trim().length > 0 ? tagMsg : `Version ${pkg.version}`
|
||||
|
||||
newTag = await github.git.createTag({
|
||||
owner,
|
||||
repo,
|
||||
tag: tagName,
|
||||
message: tagMsg,
|
||||
object: process.env.GITHUB_SHA,
|
||||
type: 'commit'
|
||||
})
|
||||
|
||||
core.warning(`Created new tag: ${newTag.data.tag}`)
|
||||
} catch (e) {
|
||||
core.setFailed(e.message)
|
||||
return
|
||||
}
|
||||
|
||||
let newReference
|
||||
try {
|
||||
newReference = await github.git.createRef({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/tags/${newTag.data.tag}`,
|
||||
sha: newTag.data.sha,
|
||||
})
|
||||
|
||||
core.warning(`Reference ${newReference.data.ref} available at ${newReference.data.url}` + os.EOL)
|
||||
} catch (e) {
|
||||
core.warning({
|
||||
owner,
|
||||
repo,
|
||||
ref: `refs/tags/${newTag.data.tag}`,
|
||||
sha: newTag.data.sha,
|
||||
})
|
||||
|
||||
core.setFailed(e.message)
|
||||
return
|
||||
}
|
||||
|
||||
// Store values for other actions
|
||||
if (typeof newTag === 'object' && typeof newReference === 'object') {
|
||||
core.setOutput('tagname', tagName)
|
||||
core.setOutput('tagsha', newTag.data.sha)
|
||||
core.setOutput('taguri', newReference.data.url)
|
||||
core.setOutput('tagmessage', tagMsg.trim())
|
||||
core.setOutput('tagref', newReference.data.ref)
|
||||
}
|
||||
} catch (error) {
|
||||
core.warning(error.message)
|
||||
core.setOutput('tagname', '')
|
||||
core.setOutput('tagsha', '')
|
||||
core.setOutput('taguri', '')
|
||||
core.setOutput('tagmessage', '')
|
||||
core.setOutput('tagref', '')
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
32
package.json
32
package.json
@ -1,25 +1,21 @@
|
||||
{
|
||||
"name": "autotag-action",
|
||||
"version": "1.0.2",
|
||||
"private": true,
|
||||
"description": "Automatically create a tag whenever the version changes in package.json",
|
||||
"main": "lib/main.js",
|
||||
"name": "action-autotag-test",
|
||||
"version": "1.1.0",
|
||||
"description": "This is a test file for the action.",
|
||||
"main": "index.js",
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"scripts": {
|
||||
"test": "jest"
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/butlerlogic/action-autotag.git"
|
||||
"url": "git+https://github.com/ButlerLogic/action-autotag.git"
|
||||
},
|
||||
"keywords": [
|
||||
"actions",
|
||||
"node",
|
||||
"setup"
|
||||
],
|
||||
"author": "ButlerLogic",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.2",
|
||||
"@actions/github": "^2.1.0"
|
||||
}
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ButlerLogic/action-autotag/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ButlerLogic/action-autotag#readme"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user