├── screenshot.jpg
├── package.json
├── LICENSE
├── .gitignore
├── action.yml
├── index.js
└── README.md
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kibibit/kb-badger-action/main/screenshot.jpg
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@kibibit/kb-badger-action",
3 | "version": "1.0.0",
4 | "description": "When an Environment is deployed successfully, add a badge to PR body with links to environment",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "ncc build index.js --license LICENSE",
8 | "test": "echo \"Error: no test specified\" && exit 1"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/Kibibit/kb-badger.git"
13 | },
14 | "author": "thatkookooguy ",
15 | "license": "MIT",
16 | "bugs": {
17 | "url": "https://github.com/Kibibit/kb-badger/issues"
18 | },
19 | "homepage": "https://github.com/Kibibit/kb-badger#readme",
20 | "publishConfig": {
21 | "access": "public"
22 | },
23 | "dependencies": {
24 | "@actions/core": "^1.2.7",
25 | "@actions/github": "^4.0.0",
26 | "lodash": "^4.17.21",
27 | "url-join": "^4.0.1"
28 | },
29 | "devDependencies": {
30 | "@types/node": "^15.0.1",
31 | "@vercel/ncc": "^0.28.4"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 kibibit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | jspm_packages/
42 |
43 | # Snowpack dependency directory (https://snowpack.dev/)
44 | web_modules/
45 |
46 | # TypeScript cache
47 | *.tsbuildinfo
48 |
49 | # Optional npm cache directory
50 | .npm
51 |
52 | # Optional eslint cache
53 | .eslintcache
54 |
55 | # Microbundle cache
56 | .rpt2_cache/
57 | .rts2_cache_cjs/
58 | .rts2_cache_es/
59 | .rts2_cache_umd/
60 |
61 | # Optional REPL history
62 | .node_repl_history
63 |
64 | # Output of 'npm pack'
65 | *.tgz
66 |
67 | # Yarn Integrity file
68 | .yarn-integrity
69 |
70 | # dotenv environment variables file
71 | .env
72 | .env.test
73 |
74 | # parcel-bundler cache (https://parceljs.org/)
75 | .cache
76 | .parcel-cache
77 |
78 | # Next.js build output
79 | .next
80 | out
81 |
82 | # Nuxt.js build / generate output
83 | .nuxt
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and not Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | # Stores VSCode versions used for testing VSCode extensions
107 | .vscode-test
108 |
109 | # yarn v2
110 | .yarn/cache
111 | .yarn/unplugged
112 | .yarn/build-state.yml
113 | .yarn/install-state.gz
114 | .pnp.*
115 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Kb Pull Request Deployment Badges'
2 | description: 'Add deployment links to PR body as badges'
3 | branding:
4 | icon: 'bell'
5 | color: 'blue'
6 | inputs:
7 | github-token:
8 | description: The GitHub token used to create an authenticated client
9 | default: ${{ github.token }}
10 | required: false
11 | position:
12 | description: |
13 | Where should the tags be positioned? If tag definitions are found,
14 | they will just be replaced. This allows you to position them anywhere
15 | if you add them to your PR template
16 | default: top
17 | required: false
18 | separator:
19 | description: Should we add a seperator between badges and pr body content?
20 | default: 'true'
21 | required: false
22 | style:
23 | description: Badge style. Can be one of `plastic` | `flat` | `flat-square` | `for-the-badge` | `social`
24 | required: false
25 | badge-left:
26 | description: Left-Hand side text of badge
27 | required: false
28 | default: demo
29 | badge-right:
30 | description: Right-Hand side text of badge
31 | required: false
32 | default: application
33 | badge-color:
34 | description: Right-Hand side css color
35 | required: false
36 | default: informational
37 | badge-logo:
38 | description: Badge Logo
39 | required: false
40 | default: ''
41 | badge-path:
42 | description: Path to navigate to in deployment
43 | required: false
44 | default: ''
45 | badge2-left:
46 | description: Left-Hand side text of badge
47 | required: false
48 | badge2-right:
49 | description: Right-Hand side text of badge
50 | required: false
51 | badge2-color:
52 | description: Right-Hand side css color
53 | required: false
54 | badge2-logo:
55 | description: Badge Logo
56 | required: false
57 | badge2-path:
58 | description: Path to navigate to in deployment
59 | required: false
60 | badge3-left:
61 | description: Left-Hand side text of badge
62 | required: false
63 | badge3-right:
64 | description: Right-Hand side text of badge
65 | required: false
66 | badge3-color:
67 | description: Right-Hand side css color
68 | required: false
69 | badge3-logo:
70 | description: Badge Logo
71 | required: false
72 | badge3-path:
73 | description: Path to navigate to in deployment
74 | required: false
75 | outputs:
76 | time: # id of output
77 | description: The time the action finished
78 | runs:
79 | using: 'node12'
80 | main: 'index.js'
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const core = require('@actions/core');
3 | const github = require('@actions/github');
4 | const _ = require('lodash');
5 | const urljoin = require('url-join');
6 |
7 | const style = core.getInput('style');
8 | const badgeTemplate = _.template(
9 | [
10 | '[![kb-badger-action--<%= name %>]',
11 | '(https://img.shields.io/badge/<%= left %>-<%= right %>-<%= color %>',
12 | `?logo=<%= logo %>${ style ? '&style=' + style : '' })]`,
13 | '(<%= url %>)'
14 | ].join('')
15 | );
16 |
17 | (async () => {
18 | try {
19 | const token = core.getInput('github-token', { required: true });
20 | const shouldAddSeperator = core.getInput('separator').toLowerCase() === 'true';
21 | const position = core.getInput('position');
22 | const octokit = github.getOctokit(token);
23 | const { context } = github;
24 | const DEPLOYMENT_URL = github.context.payload.deployment_status.deployment_url;
25 | const owner = context.repo.owner;
26 | const repo = context.repo.repo;
27 | console.log(`processing deployment: ${ DEPLOYMENT_URL }`);
28 | const deploymentData = await getDeploymentData();
29 |
30 | // create the 3 badges data array
31 | const badgesData = [];
32 | ['badge3', 'badge2', 'badge'].forEach((name) => {
33 | const badge = getBadgeDefinition(name);
34 | if (badge) {
35 | badgesData.push(badge);
36 | }
37 | });
38 |
39 | // for each pull request reference containing the commit that got deployed,
40 | // change or add the badges based on the configuration
41 | for (const prRef of deploymentData.prRefs) {
42 | if (prRef) {
43 | const prId = +prRef.replace('refs/pull/', '').replace('/head', '');
44 | console.log(`Found Pull-Request pointing to the deployment: ${ owner }/${ repo }/${ prId }`);
45 | const pr = await octokit.pulls.get({
46 | owner,
47 | repo,
48 | pull_number: prId
49 | });
50 | let body = pr.data.body;
51 |
52 | badgesData.forEach((badgeData, index) => {
53 | const compiledBadge = badgeTemplate(badgeData);
54 | console.log(badgeData);
55 | if (pr.data.body.includes(badgeData.badgeId)) {
56 | console.log('Badge exists in PR body. Replacing with newer version');
57 | // replace badge
58 | body = body.replace(badgeData.badgeCatchRegex, compiledBadge);
59 | } else {
60 | console.log(`Badge Not found. Adding new one to body at ${ position }`);
61 | const seperator = shouldAddSeperator ?
62 | `${position === 'top' ? '\n' : '\n\n'}-----${position !== 'top' ? '\n' : '\n\n'}` :
63 | '\n\n';
64 |
65 | const newBody = [
66 | compiledBadge,
67 | index === 0 ? seperator : ' ',
68 | `${body}`
69 | ];
70 |
71 | if (position === 'top') {
72 | // add badge
73 | body = newBody.join('');
74 | } else {
75 | // add badge
76 | body = newBody.reverse().join('');
77 | }
78 | }
79 | });
80 |
81 | await octokit.pulls.update({
82 | owner,
83 | repo,
84 | pull_number: prId,
85 | body
86 | });
87 | }
88 | }
89 |
90 | const time = (new Date()).toTimeString();
91 | core.setOutput("time", time);
92 |
93 | async function getDeploymentData() {
94 | const deployment_id = +DEPLOYMENT_URL.replace(/^.*deployments\//, '');
95 | const deployment = await octokit.repos.getDeployment({
96 | owner,
97 | repo,
98 | deployment_id
99 | });
100 | const envUrl = deployment.data.payload.web_url;
101 | const commitRef = deployment.data.ref;
102 | const refs = await octokit.git.listMatchingRefs({
103 | owner,
104 | repo,
105 | ref: undefined
106 | });
107 | const prRefs = refs.data
108 | .filter((ref) => ref.object.sha === commitRef && ref.ref.startsWith('refs/pull/'))
109 | .map((prRef) => prRef.ref);
110 |
111 | return {
112 | envUrl,
113 | prRefs
114 | };
115 | }
116 |
117 | function getBadgeDefinition(name) {
118 | const left = encodeURIComponent(core.getInput(`${name}-left`).replace('-', '--').replace('_', '__'));
119 | const right = encodeURIComponent(core.getInput(`${name}-right`).replace('-', '--').replace('_', '__'));
120 | const color = core.getInput(`${name}-color`);
121 | const urlPath = core.getInput(`${name}-path`);
122 | const logo = core.getInput(`${name}-logo`);
123 | const badgeId = `kb-badger-action--${name}`;
124 | const badgeCatchRegex = new RegExp(`\\[!\\[${badgeId}\\]\\(.*?\\)]\\(.*?\\)`);
125 |
126 | if (!left) {
127 | return;
128 | }
129 |
130 | return {
131 | name,
132 | left,
133 | right,
134 | color,
135 | url: urljoin(deploymentData.envUrl, urlPath),
136 | logo,
137 | badgeCatchRegex,
138 | badgeId
139 | };
140 | }
141 |
142 | } catch (error) {
143 | core.setFailed(error.message);
144 | }
145 | })();
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | @kibibit/kb-badger-action
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Add deployment baddges to Pull-Requests
18 |
19 |
20 |
21 | When an Environment is deployed successfully, add a badge to PR body with links to the environment
22 |
23 |
24 |
25 | ---
26 |
27 | Uses [shields.io](https://shields.io/) to generate shields. Check out their documentation for color support and more.
28 |
29 | Use [simple-icons](https://simpleicons.org/) for logo names. Logos are referenced using names as they appear on the simple-icons site. If the name includes spaces, replace them with dashes (e.g: `visual-studio-code`)
30 |
31 | ## Inputs
32 |
33 | ### General
34 |
35 | | Input Name | Description | Default | Required |
36 | | :-------------: | :------------- | :-------------: | :-------------: |
37 | | github-token | The GitHub token used to create an authenticated client | `${{ github.token }}` | ✔️ |
38 | | position | Where should the tags be positioned? If tag definitions are found, they will just be replaced. This allows you to position them anywhere if you add them to your PR template | `top` | ❌ |
39 | | separator | Should we add a seperator between badges and pr body content? | `true` | ❌ |
40 | | style | Badge style. Can be one of `plastic` \| `flat` \| `flat-square` \| `for-the-badge` \| `social` | `flat` | ❌ |
41 |
42 | ### Badge Settings
43 |
44 | | Input Name | Description | Default | Required |
45 | | :-------------: | :------------- | :-------------: | :-------------: |
46 | | badge-left | Left-Hand side text of badge | `demo` | ❌ |
47 | | badge-right | Right-Hand side text of badge | `application` | ❌ |
48 | | badge-color | Right-Hand side shield color | `informational` | ❌ |
49 | | badge-logo | Badge Logo | `undefined` | ❌ |
50 | | badge-path | Path to navigate to in deployment. If not set, the deployment url will be used as-is | `undefined` | ❌ |
51 |
52 | These inputs are repeated 3 tims to support 3 simultanous badges.
53 |
54 | - badge - badge-left, badge-right...
55 | - badge2 - badge2-left, badge2-right...
56 | - badge3 - badge3-left, badge3-right...
57 |
58 | ## Outputs
59 |
60 | After adding the PR badge, the action outputs the time it finished
61 |
62 | ## Example usage
63 | ```yaml
64 | name: Add PR Deploy Badges
65 | # https://docs.github.com/en/actions/reference/events-that-trigger-workflows
66 | on: [deployment_status]
67 |
68 | jobs:
69 | badge:
70 | runs-on: ubuntu-latest
71 | permissions:
72 | pull-requests: write
73 | # only runs this job on successful deploy
74 | if: github.event.deployment_status.state == 'success'
75 | steps:
76 | - name: TEST KB-BADGER-ACTION
77 | uses: kibibit/kb-badger-action@v1.91
78 | with:
79 | style: for-the-badge
80 | github-token: ${{secrets.GITHUB_TOKEN}}
81 | badge-left: demo
82 | badge-right: application
83 | badge-logo: heroku
84 | badge-path: api
85 | badge2-left: demo
86 | badge2-right: api-docs
87 | badge2-color: 85EA2D
88 | badge2-logo: swagger
89 | badge2-path: api/docs
90 | ```
91 |
92 | ## Contributors ✨
93 |
94 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
95 |
96 |
97 |
98 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome!
110 |
111 |
112 |
113 | ## Stay in touch
114 |
115 | - Author - [Neil Kalman](https://github.com/thatkookooguy)
116 | - Website - [https://github.com/kibibit](https://github.com/kibibit)
117 | - StackOverflow - [thatkookooguy](https://stackoverflow.com/users/1788884/thatkookooguy)
118 | - Twitter - [@thatkookooguy](https://twitter.com/thatkookooguy)
119 | - Twitter - [@kibibit_opensrc](https://twitter.com/kibibit_opensrc)
120 |
--------------------------------------------------------------------------------