├── assets
├── terraform-changes.png
└── plan-output-job-summary.png
├── test-data
├── tf_noresources.json
├── tf_nochanges.json
└── tf_test4.json
├── .github
└── workflows
│ ├── conventional-commits.yml
│ ├── release.yml
│ ├── reusable-terraform-test.yml
│ ├── codeql-analysis.yml
│ └── action-test.yml
├── catalog-info.yaml
├── package.json
├── action.yml
├── .gitignore
├── README.md
├── CHANGELOG.md
├── LICENSE
├── index.js
└── dist
└── licenses.txt
/assets/terraform-changes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liatrio/terraform-change-pr-commenter/HEAD/assets/terraform-changes.png
--------------------------------------------------------------------------------
/assets/plan-output-job-summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liatrio/terraform-change-pr-commenter/HEAD/assets/plan-output-job-summary.png
--------------------------------------------------------------------------------
/test-data/tf_noresources.json:
--------------------------------------------------------------------------------
1 | {
2 | "format_version": "1.2",
3 | "terraform_version": "1.10.5",
4 | "planned_values": {
5 | "root_module": {}
6 | },
7 | "configuration": {
8 | "root_module": {}
9 | },
10 | "timestamp": "2025-05-15T22:58:52Z",
11 | "applyable": false,
12 | "complete": true,
13 | "errored": false
14 | }
15 |
--------------------------------------------------------------------------------
/.github/workflows/conventional-commits.yml:
--------------------------------------------------------------------------------
1 | name: Conventional Commits
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - opened
7 | - edited
8 | - synchronize
9 |
10 | jobs:
11 | validate:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: liatrio/github-actions/conventional-pr-title@master
15 | with:
16 | token: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches: [main]
5 | release:
6 | types: [published]
7 | jobs:
8 | release:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v4
12 | - uses: actions/setup-node@v4
13 | with:
14 | node-version: "20"
15 |
16 | - name: Install Dependencies
17 | run: yarn install
18 |
19 | - name: yarn build and semantic-release
20 | run: yarn run build && npx semantic-release
21 | env:
22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23 |
--------------------------------------------------------------------------------
/.github/workflows/reusable-terraform-test.yml:
--------------------------------------------------------------------------------
1 | name: Reusable Terraform Test Workflow
2 |
3 | permissions:
4 | contents: read
5 | pull-requests: write
6 |
7 | on:
8 | workflow_call:
9 | inputs:
10 | test-mode:
11 | description: 'Test mode for reusable workflow'
12 | required: false
13 | type: string
14 | default: 'reusable'
15 |
16 | jobs:
17 | reusable-test:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: actions/checkout@v4
21 | - uses: actions/setup-node@v4
22 | with:
23 | node-version: "20"
24 | - run: yarn install
25 | - run: yarn run build
26 |
27 | - name: Test PR Commenter via workflow_call
28 | uses: ./
29 | with:
30 | json-file: test-data/tf_test.json
31 | comment-header: "Terraform Plan via Reusable Workflow (workflow_call event)"
32 | expand-comment: "true"
33 | include-workflow-link: "true"
34 | include-job-link: "true"
--------------------------------------------------------------------------------
/catalog-info.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: backstage.io/v1alpha1
2 | kind: Component
3 | metadata:
4 | # Component names should be simple and URL-friendly
5 | name: terraform-change-pr-commenter
6 | description: >-
7 | GitHub Action that parses Terraform plan JSON files, summarizes the changes,
8 | and posts concise comments (and optional workflow summaries) to pull requests.
9 |
10 | # Annotations — see https://backstage.io/docs/features/software-catalog/well-known-annotations
11 | annotations:
12 | github.com/project-slug: liatrio/terraform-change-pr-commenter
13 |
14 | tags:
15 | - javascript
16 | - terraform
17 | - github-action
18 | - ci
19 | - iac
20 |
21 | links:
22 | - url: https://github.com/liatrio/terraform-change-pr-commenter
23 | title: Source Repository
24 | icon: github
25 | - url: https://github.com/marketplace/actions/terraform-change-pr-commenter
26 | title: GitHub Marketplace Listing
27 | icon: marketplace
28 |
29 |
30 | spec:
31 | # component category, e.g. service, library, tool, website …
32 | type: library
33 | # Review lifecycle and pick one of the three that make the most sense.
34 | # https://backstage.io/docs/features/software-catalog/descriptor-format/#speclifecycle-required
35 | lifecycle: production
36 | owner: platform-engineering
37 | system: developer-platform
38 | dependsOn: []
39 | consumesApis: []
40 | providesApis: []
41 |
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "terraform-change-pr-commenter",
3 | "version": "1.14.0",
4 | "description": "GitHub Action to read changes from Terraform plan JSON, summarize changes, and post them in a GitHub Pull Request Comment",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "ncc build index.js --out dist --license licenses.txt",
8 | "semantic-release": "semantic-release"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/liatrio/terraform-change-pr-commenter.git"
13 | },
14 | "engines": {
15 | "node": "20"
16 | },
17 | "release": {
18 | "branches": [
19 | "main"
20 | ],
21 | "plugins": [
22 | "@semantic-release/commit-analyzer",
23 | "@semantic-release/release-notes-generator",
24 | "@semantic-release/changelog",
25 | [
26 | "@semantic-release/npm",
27 | {
28 | "npmPublish": false
29 | }
30 | ],
31 | [
32 | "@semantic-release/git",
33 | {
34 | "assets": [
35 | "package.json",
36 | "yarn.lock",
37 | "dist",
38 | "CHANGELOG.md",
39 | "README.md"
40 | ],
41 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
42 | }
43 | ],
44 | "@semantic-release/github"
45 | ]
46 | },
47 | "keywords": [],
48 | "author": "Liatrio",
49 | "license": "Apache-2.0",
50 | "bugs": {
51 | "url": "https://github.com/liatrio/terraform-change-pr-commenter/issues"
52 | },
53 | "homepage": "https://github.com/liatrio/terraform-change-pr-commenter#readme",
54 | "devDependencies": {
55 | "@semantic-release/changelog": "^6.0.1",
56 | "@semantic-release/git": "^10.0.1",
57 | "@vercel/ncc": "^0.33.1",
58 | "semantic-release": "^24.2.6"
59 | },
60 | "dependencies": {
61 | "@actions/core": "^1.9.1",
62 | "@actions/github": "^5.0.0"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: Terraform Change PR Commenter v2
2 | description: Parse changes from Terraform Plan JSON and post them for PR review
3 | branding:
4 | icon: "git-pull-request"
5 | color: "green"
6 | inputs:
7 | json-file:
8 | description: File location for the Terraform Plan JSON file
9 | required: false
10 | default: tfplan.json
11 | github-token:
12 | description: GitHub Token
13 | required: false
14 | default: "${{github.token}}"
15 | expand-comment:
16 | description: If true, expand the details comment by default
17 | required: false
18 | default: "false"
19 | include-plan-job-summary:
20 | description: If true, add the results of the plan to the workflow job summary
21 | required: false
22 | default: "false"
23 | comment-header:
24 | description: Header to use for the comment
25 | required: false
26 | default: "Terraform Plan Changes"
27 | comment-footer:
28 | description: Footer to use for the comment
29 | required: false
30 | default: ""
31 | include-workflow-link:
32 | description: If true, include a link to the workflow in the comment
33 | required: false
34 | default: "false"
35 | include-job-link:
36 | description: If true, include a link to the job run in the comment
37 | required: false
38 | default: "false"
39 | quiet:
40 | description: Skips the comment if there are no changes
41 | required: false
42 | default: "false"
43 | hide-previous-comments:
44 | description: Hides privious comments on the PR
45 | required: false
46 | default: "false"
47 | log-changed-resources:
48 | description: Log the changed resources in action output
49 | required: false
50 | default: "true"
51 | include-tag-only-resources:
52 | description: If true, include resources with only tag changes as a separate section
53 | required: false
54 | default: "false"
55 | include-unchanged-resources:
56 | description: If true, include unchanged resources in the output
57 | required: false
58 | default: "false"
59 | runs:
60 | using: node20
61 | main: dist/index.js
62 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dist release output
2 | dist
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 | lerna-debug.log*
11 |
12 | # Diagnostic reports (https://nodejs.org/api/report.html)
13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
14 |
15 | # Runtime data
16 | pids
17 | *.pid
18 | *.seed
19 | *.pid.lock
20 |
21 | # Directory for instrumented libs generated by jscoverage/JSCover
22 | lib-cov
23 |
24 | # Coverage directory used by tools like istanbul
25 | coverage
26 | *.lcov
27 |
28 | # nyc test coverage
29 | .nyc_output
30 |
31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
32 | .grunt
33 |
34 | # Bower dependency directory (https://bower.io/)
35 | bower_components
36 |
37 | # node-waf configuration
38 | .lock-wscript
39 |
40 | # Compiled binary addons (https://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # TypeScript v1 declaration files
48 | typings/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Microbundle cache
60 | .rpt2_cache/
61 | .rts2_cache_cjs/
62 | .rts2_cache_es/
63 | .rts2_cache_umd/
64 |
65 | # Optional REPL history
66 | .node_repl_history
67 |
68 | # Output of 'npm pack'
69 | *.tgz
70 |
71 | # Yarn Integrity file
72 | .yarn-integrity
73 |
74 | # dotenv environment variables file
75 | .env
76 | .env.test
77 |
78 | # parcel-bundler cache (https://parceljs.org/)
79 | .cache
80 |
81 | # Next.js build output
82 | .next
83 |
84 | # Nuxt.js build / generate output
85 | .nuxt
86 |
87 | # Gatsby files
88 | .cache/
89 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
90 | # https://nextjs.org/blog/next-9-1#public-directory-support
91 | # public
92 |
93 | # vuepress build output
94 | .vuepress/dist
95 |
96 | # Serverless directories
97 | .serverless/
98 |
99 | # FuseBox cache
100 | .fusebox/
101 |
102 | # DynamoDB Local files
103 | .dynamodb/
104 |
105 | # TernJS port file
106 | .tern-port
107 |
108 | .DS_Store
109 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ main ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ main ]
20 | schedule:
21 | - cron: '18 6 * * 6'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'javascript', 'actions' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # CodeQL also supports 'actions' for GitHub Actions
38 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
39 |
40 | steps:
41 | - name: Checkout repository
42 | uses: actions/checkout@v4
43 |
44 | # Initializes the CodeQL tools for scanning.
45 | - name: Initialize CodeQL
46 | uses: github/codeql-action/init@v3
47 | with:
48 | languages: ${{ matrix.language }}
49 | # If you wish to specify custom queries, you can do so here or in a config file.
50 | # By default, queries listed here will override any specified in a config file.
51 | # Prefix the list here with "+" to use these queries and those in the config file.
52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
53 |
54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
55 | # If this step fails, then you should remove it and run the build manually (see below)
56 | - name: Autobuild
57 | uses: github/codeql-action/autobuild@v3
58 |
59 | # ℹ️ Command-line programs to run using the OS shell.
60 | # 📚 https://git.io/JvXDl
61 |
62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
63 | # and modify them (or add more) to build your code if your project
64 | # uses a compiled language
65 |
66 | #- run: |
67 | # make bootstrap
68 | # make release
69 |
70 | - name: Perform CodeQL Analysis
71 | uses: github/codeql-action/analyze@v3
72 |
--------------------------------------------------------------------------------
/.github/workflows/action-test.yml:
--------------------------------------------------------------------------------
1 | name: Action Tester Workflow
2 |
3 | on:
4 | pull_request:
5 | pull_request_target:
6 | workflow_call:
7 | inputs:
8 | test-mode:
9 | description: 'Test mode for workflow_call testing'
10 | required: false
11 | type: string
12 | default: 'reusable'
13 |
14 | permissions:
15 | contents: read
16 | pull-requests: write
17 |
18 | jobs:
19 | plan:
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v4
23 | - uses: actions/setup-node@v4
24 | with:
25 | node-version: "20"
26 | - run: npm install -g yarn
27 | - run: yarn install
28 | - run: yarn run build
29 |
30 | - name: Test PR Commenter
31 | uses: ./
32 | with:
33 | json-file: |
34 | test-data/tf_test.json
35 | test-data/tf_test2.json
36 | test-data/tf_test3.json
37 |
38 | - name: Test PR comment hiding
39 | uses: ./
40 | with:
41 | json-file: |
42 | test-data/tf_test.json
43 | test-data/tf_test2.json
44 | test-data/tf_test3.json
45 | hide-previous-comments: true
46 | comment-header: "Same as previous but previous comment should be hidden."
47 |
48 | - name: Test PR Comment Expand feature
49 | uses: ./
50 | with:
51 | json-file: test-data/tf_test.json
52 | expand-comment: "true"
53 | include-plan-job-summary: "true"
54 | comment-header: "BIG HEADER"
55 | comment-footer: "BIG FOOTER"
56 | include-workflow-link: "true"
57 | quiet: "true"
58 | include-job-link: "true"
59 |
60 | - name: Test PR Commenter with no changes
61 | uses: ./
62 | with:
63 | json-file: test-data/tf_nochanges.json
64 |
65 | - name: Test PR Commenter with no resources
66 | uses: ./
67 | with:
68 | json-file: test-data/tf_noresources.json
69 |
70 | - name: Test PR Commenter with large plan
71 | uses: ./
72 | with:
73 | json-file: test-data/tf_test_large.json
74 | comment-header: "Large Plan Test"
75 | # We expect the comment to be truncated, so we don't need to check the full content
76 | # Just ensure the action runs without error and the summary is present
77 | include-plan-job-summary: true
78 |
79 | - name: Test PR comment hiding with default header
80 | id: hide_default_header
81 | uses: ./
82 | with:
83 | json-file: test-data/tf_test.json
84 | hide-previous-comments: true
85 | - name: Assert PR comment hiding with default header
86 | run: |
87 | node -e "
88 | const output = process.env.COMMENT_BODY;
89 | if (!output.includes('Terraform Plan Changes for `test-data/tf_test.json`')) {
90 | console.error('Assertion failed: Missing header for tf_test.json');
91 | process.exit(1);
92 | }
93 | "
94 | env:
95 | COMMENT_BODY: ${{ steps.hide_default_header.outputs.comment-body }}
96 |
97 | - name: Test multiline json files with hide previous comments and default header
98 | id: multiline_hide_default
99 | uses: ./
100 | with:
101 | json-file: |
102 | test-data/tf_test.json
103 | test-data/tf_test2.json
104 | hide-previous-comments: true
105 | - name: Assert multiline json files with hide previous comments and default header
106 | run: |
107 | node -e "
108 | const output = process.env.COMMENT_BODY;
109 | if (!output.includes('Terraform Plan Changes for `test-data/tf_test.json`')) {
110 | console.error('Assertion failed: Missing header for tf_test.json');
111 | process.exit(1);
112 | }
113 | if (!output.includes('Terraform Plan Changes for `test-data/tf_test2.json`')) {
114 | console.error('Assertion failed: Missing header for tf_test2.json');
115 | process.exit(1);
116 | }
117 | "
118 | env:
119 | COMMENT_BODY: ${{ steps.multiline_hide_default.outputs.comment-body }}
120 |
121 |
122 |
123 | - name: Test multiple json files
124 | id: multiple_files
125 | uses: ./
126 | with:
127 | json-file: |
128 | test-data/tf_test.json
129 | test-data/tf_test2.json
130 |
131 | - name: Assert multiple json files output
132 | run: |
133 | node -e "
134 | const output = process.env.COMMENT_BODY;
135 | if (!output.includes('Terraform Plan Changes for `test-data/tf_test.json`')) {
136 | console.error('Assertion failed: Missing header for tf_test.json');
137 | process.exit(1);
138 | }
139 | if (!output.includes('Terraform Plan Changes for `test-data/tf_test2.json`')) {
140 | console.error('Assertion failed: Missing header for tf_test2.json');
141 | process.exit(1);
142 | }
143 | "
144 | env:
145 | COMMENT_BODY: ${{ steps.multiple_files.outputs.comment-body }}
146 |
147 | matrix-plan:
148 | runs-on: ubuntu-latest
149 | strategy:
150 | matrix:
151 | json:
152 | - tf_test4
153 | steps:
154 | - uses: actions/checkout@v4
155 | - uses: actions/setup-node@v4
156 | with:
157 | node-version: "20"
158 | - run: npm install -g yarn
159 | - run: yarn install
160 | - run: yarn run build
161 |
162 | - name: Test PR Commenter with matrix job
163 | uses: ./
164 | with:
165 | json-file: test-data/${{ matrix.json }}.json
166 | comment-header: Plan Summary for ${{ github.job }} (${{ matrix.json }})
167 | quiet: true
168 | expand-comment: true
169 | hide-previous-comments: true
170 | include-workflow-link: true
171 | include-job-link: true
172 | include-plan-job-summary: true
173 | include-tag-only-resources: true
174 | include-unchanged-resources: true
175 |
176 | # Additional tests for pull_request_target event
177 | plan-pr-target:
178 | if: github.event_name == 'pull_request_target'
179 | runs-on: ubuntu-latest
180 | steps:
181 | - uses: actions/checkout@v4
182 | - uses: actions/setup-node@v4
183 | with:
184 | node-version: "20"
185 | - run: npm install -g yarn
186 | - run: yarn install
187 | - run: yarn run build
188 |
189 | - name: Test PR Commenter (pull_request_target)
190 | uses: ./
191 | with:
192 | json-file: test-data/tf_test.json
193 | hide-previous-comments: true
194 | comment-header: "PR Target Event Test"
195 |
196 | # Test workflow_call functionality by calling a reusable workflow
197 | test-reusable-workflow:
198 | if: github.event_name == 'pull_request'
199 | uses: ./.github/workflows/reusable-terraform-test.yml
200 | with:
201 | test-mode: 'workflow_call_test'
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terraform Change Pull Request Commenter Action
2 | This GitHub Action reads changes from your Terraform plan JSON output, summarizes the changes, and posts them in a single GitHub Pull Request comment.
3 |
4 | We recommend using this in your Infrastructure as Code delivery workflow to make any change visible and acclerate the PR review process.
5 |
6 | ## Why is this GitHub Action different than the other Terraform Plan commenters?
7 |
8 | Implementing this Action is _super_ simple and the comments are consise and easy to read. Other implementations may be heavily opinionated or require adding multiple jobs to your workflow.
9 |
10 | ## Features
11 |
12 | - Display changes in a Terraform plan without posting larger sections of the plan change log. This approach will, in most cases, avoid the situation where plan contents are too large for a single PR comment.
13 | - Collapsed as a summary by default, when expanded, the comment is broken up into sections for deletion, creation, and resource changes. The changes are also color-coded to help draw attention to each proposed modification.
14 | - This JavaScript GitHub Action runs directly on a host runner and executes faster than a Docker container Action.
15 | - Works with both direct pull request workflows (supports `pull_request` and `pull_request_target` events) and reusable workflows (`workflow_call` events).
16 | - Possibility to add the output to your workflow summary.
17 | - Possibility to hide previous comments generated by this action.
18 | - Possibility to not create any comments in case there are no infrastructure changes.
19 | - Customize the header and the footer of the generated output.
20 |
21 | ### Example Comment
22 | 
23 |
24 | ## Inputs
25 |
26 | ### `json-file`
27 |
28 | **Optional** Defaults to `tfplan.json`
29 |
30 | - The location of the JSON file created by running `terraform show -no-color -json tfplan.plan > tfplan.json` (Or whatever you choose to name your plan or json outputs)
31 |
32 | - Multiple files can be provided using a text block.
33 |
34 | ### `github-token`
35 |
36 | **Optional** Boolean defaults to `${{github.token}}`
37 |
38 | - Used to authenticate with the GitHub API.
39 |
40 | ### `expand-comment`
41 |
42 | **Optional** Boolean defaults to `false`
43 |
44 | - Will expand the changes in comments by default rather than having them collapsed beneath the summary
45 |
46 | ### `include-plan-job-summary`
47 |
48 | **Optional** Defaults to `false`
49 |
50 | - Will write the plan output to the workflow summary.
51 |
52 | - The workflow summary will still be set when running this action outside of a PR context.
53 |
54 | ### `comment-header`
55 |
56 | **Optional** Defaults to `Terraform Plan Changes`
57 |
58 | - Will set the header of the PR comment and/or workflow summary. The filename will always be appended to this header.
59 |
60 | ### `comment-footer`
61 |
62 | **Optional** Defaults to `""`
63 |
64 | - Will set a footer of the PR comment and/or workflow summary.
65 |
66 | ### `include-workflow-link`
67 |
68 | **Optional** Defaults to `false`
69 |
70 | - Will include a link back to the workflow in the PR comment and/or workflow summary.
71 |
72 | ### `quiet`
73 |
74 | **Optional** Defaults to `false`
75 |
76 | - Will not create a PR comment when there are no infrastructure changes.
77 |
78 | ### `hide-previous-comments`
79 |
80 | **Optional** Defaults to `false`
81 |
82 | - Will hide/minimize all previous comments generated by this action.
83 | - When using this feature within a matrix, ensure you set `comment-header` to a unique value per matrix job (e.g., `comment-header: Plan Summary for ${{ matrix.module }}`). This ensures the action can properly identify and manage comments from each matrix job independently.
84 |
85 | ### `log-changed-resources`
86 |
87 | **Optional** Defaults to `true`
88 |
89 | - Logs all the changed resources found in the plan to the action output.
90 |
91 | ## Example usage
92 |
93 | > ‼️ Do not forget to add pull-requests write permission to your github action definition. [More info](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defining-access-for-the-github_token-scopes)
94 |
95 | ```yaml
96 | permissions:
97 | pull-requests: write
98 | ```
99 | Single plan file:
100 | ```yaml
101 | uses: liatrio/terraform-change-pr-commenter@v1.10.0
102 | with:
103 | json-file: my-tfplan.json
104 | expand-comment: 'true'
105 | ```
106 | Multiple plan files:
107 | ```yaml
108 | uses: liatrio/terraform-change-pr-commenter@v1.10.0
109 | with:
110 | json-file: |
111 | core-infra-tfplan.json
112 | shared-infra-tfplan.json
113 | ```
114 | Include plan output to the Actions workflow job summary:
115 | ```yaml
116 | uses: liatrio/terraform-change-pr-commenter@v1.10.0
117 | with:
118 | json-file: my-tfplan.json
119 | expand-comment: 'true'
120 | include-plan-job-summary: 'true'
121 | ```
122 | **Note:**
123 | - When `include-plan-job-summary = true`, if the action is executed in non-Pull Request workflows, the plan output will also be posted to the job summary of that run. If you do not wish to have this behavior, apply conditional logic to your workflow file.
124 |
125 | ### Usage with Reusable Workflows
126 |
127 | This action works seamlessly with reusable workflows. When called through a `workflow_call` event, it will still properly comment on the originating pull request.
128 |
129 | **Important:** For `pull_request_target` and `workflow_call` events, the action requires that the originating event was triggered by a pull request so that the necessary PR context is available. If a reusable workflow is called from a non-PR event (like `push`, `schedule`, or `workflow_dispatch`), the action will skip comment creation and only generate workflow summaries if enabled.
130 |
131 | **reusable-terraform.yml:**
132 | ```yaml
133 | name: Reusable Terraform Workflow
134 |
135 | on:
136 | workflow_call:
137 | inputs:
138 | environment:
139 | required: true
140 | type: string
141 |
142 | jobs:
143 | terraform:
144 | runs-on: ubuntu-latest
145 | steps:
146 | - uses: actions/checkout@v4
147 | - uses: hashicorp/setup-terraform@v2
148 | with:
149 | terraform_wrapper: false
150 |
151 | - name: Terraform Plan
152 | run: |
153 | terraform init
154 | terraform plan -out=tfplan
155 | terraform show -json tfplan > tfplan.json
156 |
157 | - name: Comment Plan Changes
158 | uses: liatrio/terraform-change-pr-commenter@v1.10.0
159 | with:
160 | json-file: tfplan.json
161 | comment-header: "Terraform Plan for ${{ inputs.environment }}"
162 | expand-comment: 'true'
163 | ```
164 |
165 | **main-workflow.yml:**
166 | ```yaml
167 | name: Infrastructure Deployment
168 |
169 | on:
170 | pull_request:
171 |
172 | permissions:
173 | pull-requests: write
174 |
175 | jobs:
176 | terraform-dev:
177 | uses: ./.github/workflows/reusable-terraform.yml
178 | with:
179 | environment: "development"
180 |
181 | terraform-prod:
182 | uses: ./.github/workflows/reusable-terraform.yml
183 | with:
184 | environment: "production"
185 | ```
186 |
187 | #### Example Job Summary Output
188 | 
189 |
190 | ## Example usage with OpenTofu
191 |
192 | To use this action with OpenTofu you need to initialize OpenTofu without the wrapper, like discussed in the `known issues` below.
193 |
194 | **You also need to convert the planfile to a JSON planfile using the `tofu show -json` command.**
195 |
196 | ```yaml
197 | - uses: opentofu/setup-opentofu@v1
198 | with:
199 | tofu_wrapper: false
200 |
201 | - name: Create planfile
202 | run: tofu plan -no-color -out=./.planfile
203 |
204 | - name: Convert planfile to JSON planfile
205 | run: tofu show -json ./.planfile >> ./my-planfile.json
206 |
207 | - name: Create PR comment
208 | uses: liatrio/terraform-change-pr-commenter@v1.10.0
209 | with:
210 | json-file: my-planfile.json
211 | ```
212 |
213 | ## Terraform Configuration / Known Issues
214 | #### Known issue when including the [Terraform Wrapper script](https://github.com/hashicorp/setup-terraform#inputs)
215 | - Execution may error with `Error: Unexpected token c in JSON at position 1`
216 | - **Cause**: Terraform wrapper enabled (default behavior) causes invalid JSON in Terraform output.
217 | - **Fix**: Exclude the Terraform Wrapper when setting up Terraform (*GitHub Actions example*)
218 | ```yaml
219 | - name: Setup Terraform
220 | uses: hashicorp/setup-terraform@v2
221 | with:
222 | terraform_wrapper: false
223 | ```
224 |
225 | ## Contributing or Submitting Issues
226 |
227 | ### Contributions are welcome!
228 | If you'd like to suggest changes, feel free to submit a Pull Request or [open an issue](https://github.com/liatrio/terraform-change-pr-commenter/issues/new).
229 |
230 | Otherwise if things aren't working as expected, please [open a new issue](https://github.com/liatrio/terraform-change-pr-commenter/issues/new). Please include code references, a description of the issue, and expected behavior.
231 |
232 | ---
233 | 
234 | 
235 | [](https://github.com/semantic-release/semantic-release)
236 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # [1.14.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.13.0...v1.14.0) (2025-07-01)
2 |
3 |
4 | ### Features
5 |
6 | * **action:** add support for `pull_request_target` event ([#95](https://github.com/liatrio/terraform-change-pr-commenter/issues/95)) ([fdfdbae](https://github.com/liatrio/terraform-change-pr-commenter/commit/fdfdbae2c1dbf02ed6a6334a334b99d2ed28f531)), closes [#94](https://github.com/liatrio/terraform-change-pr-commenter/issues/94)
7 |
8 | # [1.13.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.12.0...v1.13.0) (2025-06-27)
9 |
10 |
11 | ### Features
12 |
13 | * Truncate PR comments if too long ([#93](https://github.com/liatrio/terraform-change-pr-commenter/issues/93)) ([5587334](https://github.com/liatrio/terraform-change-pr-commenter/commit/55873345015c06f59316309a71ea9433104ed5f9)), closes [#91](https://github.com/liatrio/terraform-change-pr-commenter/issues/91)
14 |
15 | # [1.12.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.11.0...v1.12.0) (2025-06-27)
16 |
17 |
18 | ### Features
19 |
20 | * Simplify comment-header logic and improve multi-file support ([#92](https://github.com/liatrio/terraform-change-pr-commenter/issues/92)) ([de714c9](https://github.com/liatrio/terraform-change-pr-commenter/commit/de714c9edf0859d9851946cd93864c3b789e626a))
21 |
22 | # [1.11.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.10.1...v1.11.0) (2025-06-16)
23 |
24 |
25 | ### Features
26 |
27 | * allow workflow calls as triggers ([#89](https://github.com/liatrio/terraform-change-pr-commenter/issues/89)) ([bd5d6aa](https://github.com/liatrio/terraform-change-pr-commenter/commit/bd5d6aafb8c90a8cc0e3d78c32a0f33fcffa27de))
28 |
29 | ## [1.10.1](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.10.0...v1.10.1) (2025-05-30)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * handle comment if no resources ([#88](https://github.com/liatrio/terraform-change-pr-commenter/issues/88)) ([4666dab](https://github.com/liatrio/terraform-change-pr-commenter/commit/4666dabe39891932028ca1b53a2172590009bad5))
35 |
36 | # [1.10.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.9.0...v1.10.0) (2025-04-29)
37 |
38 |
39 | ### Features
40 |
41 | * Add tags tracking, unchanged resources, and matrix support for jobs ([#87](https://github.com/liatrio/terraform-change-pr-commenter/issues/87)) ([b2f5356](https://github.com/liatrio/terraform-change-pr-commenter/commit/b2f535616c169467e523e6ff35ba684606ee2521))
42 |
43 | # [1.9.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.8.0...v1.9.0) (2025-04-24)
44 |
45 |
46 | ### Features
47 |
48 | * Add GitHub Actions to CodeQL analysis matrix ([#86](https://github.com/liatrio/terraform-change-pr-commenter/issues/86)) ([d410b01](https://github.com/liatrio/terraform-change-pr-commenter/commit/d410b015e84293a722b73dd529393aeaf2b2cacf))
49 |
50 | # [1.8.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.7.1...v1.8.0) (2025-04-24)
51 |
52 |
53 | ### Features
54 |
55 | * Add job link option, resolves [#84](https://github.com/liatrio/terraform-change-pr-commenter/issues/84) ([#85](https://github.com/liatrio/terraform-change-pr-commenter/issues/85)) ([0b450ba](https://github.com/liatrio/terraform-change-pr-commenter/commit/0b450ba53b357f81b54bf117c9dab15e2fd6cda8))
56 |
57 | ## [1.7.1](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.7.0...v1.7.1) (2024-12-12)
58 |
59 |
60 | ### Bug Fixes
61 |
62 | * `hide-previous-comment` now can handel matrix strategy runs when given a unique `comment-header` ([#78](https://github.com/liatrio/terraform-change-pr-commenter/issues/78)) ([95ca081](https://github.com/liatrio/terraform-change-pr-commenter/commit/95ca081372ca566e0c8577d24353842679b527ee))
63 |
64 | # [1.7.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.6.0...v1.7.0) (2024-09-03)
65 |
66 |
67 | ### Features
68 |
69 | * adds an input for showing the full list of changed resources in the action output ([#68](https://github.com/liatrio/terraform-change-pr-commenter/issues/68)) ([815d669](https://github.com/liatrio/terraform-change-pr-commenter/commit/815d669c00d3bcb18303a846c49ef49cd2ab19db))
70 |
71 | # [1.6.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.5.0...v1.6.0) (2024-09-03)
72 |
73 |
74 | ### Features
75 |
76 | * ability to hide previous comments & fix for summary issue ([#65](https://github.com/liatrio/terraform-change-pr-commenter/issues/65)) ([08ba12c](https://github.com/liatrio/terraform-change-pr-commenter/commit/08ba12cd559192754993f85f0e45de37f095e248))
77 |
78 | # [1.5.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.4.5...v1.5.0) (2024-05-08)
79 |
80 |
81 | ### Features
82 |
83 | * additional feature additions ([#62](https://github.com/liatrio/terraform-change-pr-commenter/issues/62)) ([1465fa2](https://github.com/liatrio/terraform-change-pr-commenter/commit/1465fa28232ee9e3b51c5db1fa43f2c2f4b971e4))
84 | * multiple feature addition [#58](https://github.com/liatrio/terraform-change-pr-commenter/issues/58) ([#60](https://github.com/liatrio/terraform-change-pr-commenter/issues/60)) ([01f0321](https://github.com/liatrio/terraform-change-pr-commenter/commit/01f0321246312425041b0ee8eaa5ba9404f6f1ae))
85 |
86 |
87 | ### Reverts
88 |
89 | * Revert " feat: multiple feature addition #58 (#60)" (#63) ([ab68bb6](https://github.com/liatrio/terraform-change-pr-commenter/commit/ab68bb614b5eb42957331703bf7afa80e1188186)), closes [#58](https://github.com/liatrio/terraform-change-pr-commenter/issues/58) [#60](https://github.com/liatrio/terraform-change-pr-commenter/issues/60) [#63](https://github.com/liatrio/terraform-change-pr-commenter/issues/63)
90 |
91 | ## [1.4.5](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.4.4...v1.4.5) (2024-02-02)
92 |
93 |
94 | ### Bug Fixes
95 |
96 | * Update actions to use actions with versions with node20 ([#56](https://github.com/liatrio/terraform-change-pr-commenter/issues/56)) ([2365114](https://github.com/liatrio/terraform-change-pr-commenter/commit/236511422a9364c7adb215a172eec7173ce18374))
97 | * Update release.yml ([#55](https://github.com/liatrio/terraform-change-pr-commenter/issues/55)) ([d527888](https://github.com/liatrio/terraform-change-pr-commenter/commit/d5278883cce02e2713a70a70b80adbf7390a7476))
98 |
99 | ## [1.4.5](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.4.4...v1.4.5) (2024-02-02)
100 |
101 |
102 | ### Bug Fixes
103 |
104 | * Update release.yml ([#55](https://github.com/liatrio/terraform-change-pr-commenter/issues/55)) ([d527888](https://github.com/liatrio/terraform-change-pr-commenter/commit/d5278883cce02e2713a70a70b80adbf7390a7476))
105 |
106 | ## [1.4.4](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.4.3...v1.4.4) (2024-02-02)
107 |
108 |
109 | ### Bug Fixes
110 |
111 | * update line 24 to node20 ([#53](https://github.com/liatrio/terraform-change-pr-commenter/issues/53)) ([d84a976](https://github.com/liatrio/terraform-change-pr-commenter/commit/d84a976ce8bee75080d34f46b39d1c28f2cfec3a))
112 |
113 | ## [1.4.2](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.4.1...v1.4.2) (2024-02-01)
114 |
115 |
116 | ### Bug Fixes
117 |
118 | * Node bump to 20 from 16 ([#51](https://github.com/liatrio/terraform-change-pr-commenter/issues/51)) ([2da4521](https://github.com/liatrio/terraform-change-pr-commenter/commit/2da45213eef1edc22a05b22bb198b0d94e0cf2a0))
119 |
120 | # [1.4.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.3.3...v1.4.0) (2023-01-09)
121 |
122 |
123 | ### Features
124 |
125 | * add job summary ([#36](https://github.com/liatrio/terraform-change-pr-commenter/issues/36)) ([01d3b49](https://github.com/liatrio/terraform-change-pr-commenter/commit/01d3b49e93cf0319d28dee2b3fbab268b6566df9))
126 |
127 | ## [1.3.2](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.3.1...v1.3.2) (2022-06-09)
128 |
129 |
130 | ### Bug Fixes
131 |
132 | * [#30](https://github.com/liatrio/terraform-change-pr-commenter/issues/30) empty plan would failed the action ([#31](https://github.com/liatrio/terraform-change-pr-commenter/issues/31)) ([c4ea052](https://github.com/liatrio/terraform-change-pr-commenter/commit/c4ea0520ce5c086465dda4da3f75fca6527a60e0))
133 |
134 | ## [1.3.1](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.3.0...v1.3.1) (2022-04-29)
135 |
136 |
137 | ### Bug Fixes
138 |
139 | * operator for replace was broken in last fixes ([#25](https://github.com/liatrio/terraform-change-pr-commenter/issues/25)) ([e82bf94](https://github.com/liatrio/terraform-change-pr-commenter/commit/e82bf94e915ad440bd0c08e69dcd9ef748a74ed2))
140 |
141 | # [1.3.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.2.0...v1.3.0) (2022-04-29)
142 |
143 |
144 | ### Features
145 |
146 | * added support for 'replace', and 'unchanged' ([#24](https://github.com/liatrio/terraform-change-pr-commenter/issues/24)) ([a3c45b0](https://github.com/liatrio/terraform-change-pr-commenter/commit/a3c45b0c735a3c4467729d0f94730c2af583a5e2))
147 |
148 | # [1.2.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.1.0...v1.2.0) (2022-04-01)
149 |
150 |
151 | ### Features
152 |
153 | * adding comment block expand flag ([55d2573](https://github.com/liatrio/terraform-change-pr-commenter/commit/55d25736974196a554e7ed4d864224b54af0123d))
154 |
155 | # [1.1.0](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.0.2...v1.1.0) (2022-02-23)
156 |
157 |
158 | ### Features
159 |
160 | * Support for multiple plans ([#13](https://github.com/liatrio/terraform-change-pr-commenter/issues/13)) ([9dc08b0](https://github.com/liatrio/terraform-change-pr-commenter/commit/9dc08b01a7f2000f9a8721c4be3479079642580e))
161 |
162 | ## [1.0.2](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.0.1...v1.0.2) (2022-02-13)
163 |
164 |
165 | ### Bug Fixes
166 |
167 | * using yarn build to remove npm dependency vulnerabilities ([3777d7b](https://github.com/liatrio/terraform-change-pr-commenter/commit/3777d7bb2204009d82f659632a95372e71c08dfc))
168 |
169 | ## [1.0.1](https://github.com/liatrio/terraform-change-pr-commenter/compare/v1.0.0...v1.0.1) (2022-02-05)
170 |
171 |
172 | ### Bug Fixes
173 |
174 | * adding branding to the action ([e40fd0c](https://github.com/liatrio/terraform-change-pr-commenter/commit/e40fd0c772ab36937de7e86ae71e3e6f013b5a70))
175 |
176 | # 1.0.0 (2022-01-29)
177 |
178 |
179 | ### Features
180 |
181 | * initial release v1.0.0 ([90eaf6e](https://github.com/liatrio/terraform-change-pr-commenter/commit/90eaf6ef9875330479e2368eaf669099c740b006))
182 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const core = require("@actions/core");
2 | const github = require("@actions/github");
3 | const fs = require("fs");
4 |
5 | const expandDetailsComment = core.getBooleanInput("expand-comment");
6 | const includePlanSummary = core.getBooleanInput("include-plan-job-summary");
7 | const myToken = core.getInput("github-token");
8 | const octokit = github.getOctokit(myToken);
9 | const context = github.context;
10 | const inputFilenames = core.getMultilineInput("json-file");
11 | const commentHeader = core.getMultilineInput("comment-header");
12 | const commentFooter = core.getMultilineInput("comment-footer");
13 | const quietMode = core.getBooleanInput("quiet");
14 | const includeLinkToWorkflow = core.getBooleanInput("include-workflow-link");
15 | const includeLinkToJob = core.getBooleanInput("include-job-link");
16 | const hidePreviousComments = core.getBooleanInput("hide-previous-comments");
17 | const logChangedResources = core.getBooleanInput("log-changed-resources");
18 | const includeTagOnlyResources = core.getBooleanInput(
19 | "include-tag-only-resources",
20 | );
21 | const includeUnchangedResources = core.getBooleanInput(
22 | "include-unchanged-resources",
23 | );
24 |
25 | const MAX_COMMENT_LENGTH = 65536;
26 |
27 | // Get current job name from GitHub environment variable
28 | const currentJobName = process.env.GITHUB_JOB || "";
29 | const currentRunnerName = process.env.RUNNER_NAME || "";
30 |
31 | // Log the job name for debugging
32 | console.log("Current job name:", currentJobName);
33 |
34 | const workflowLink = includeLinkToWorkflow
35 | ? `
36 | [Workflow: ${context.workflow}](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})
37 | `
38 | : "";
39 |
40 | // Initialize job link as empty string
41 | let jobLink = "";
42 |
43 | // Function to get job ID from GitHub API
44 | async function getJobId() {
45 | if (includeLinkToJob) {
46 | try {
47 | // Get all jobs for the current workflow run
48 | const response = await octokit.rest.actions.listJobsForWorkflowRun({
49 | owner: context.repo.owner,
50 | repo: context.repo.repo,
51 | run_id: context.runId,
52 | });
53 |
54 | // Find the current job by name
55 | const job = response.data.jobs.find(
56 | (job) =>
57 | job.runner_name === currentRunnerName &&
58 | (job.name.endsWith(currentJobName) ||
59 | job.name.startsWith(currentJobName)),
60 | );
61 |
62 | if (job) {
63 | console.log(`Found job ID: ${job.id} for job name: ${job.name}`);
64 | // Create job link with the numeric job ID
65 | return `
66 | [Job: ${job.name}](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}/job/${job.id})
67 | `;
68 | } else {
69 | console.log(`Could not find job with name: ${currentJobName}`);
70 | console.log(`Jobs: \n${JSON.stringify(response.data.jobs, null, 2)}`);
71 | return "";
72 | }
73 | } catch (error) {
74 | console.error(`Error fetching job ID: ${error.message}`);
75 | return "";
76 | }
77 | }
78 | return "";
79 | }
80 |
81 | var hasNoChanges = false;
82 |
83 | // GraphQL queries and mutations used for hiding previous comments
84 | const minimizeCommentQuery = /* GraphQL */ `
85 | mutation minimizeComment($id: ID!) {
86 | minimizeComment(input: { classifier: OUTDATED, subjectId: $id }) {
87 | clientMutationId
88 | }
89 | }
90 | `;
91 |
92 | const commentsQuery = /* GraphQL */ `
93 | query comments($owner: String!, $name: String!, $number: Int!) {
94 | repository(owner: $owner, name: $name) {
95 | pullRequest(number: $number) {
96 | comments(last: 100, orderBy: { field: UPDATED_AT, direction: DESC }) {
97 | nodes {
98 | id
99 | body
100 | isMinimized
101 | }
102 | }
103 | }
104 | }
105 | }
106 | `;
107 |
108 | const output = () => {
109 | let body = "";
110 | // for each file
111 | for (const file of inputFilenames) {
112 | const resource_changes =
113 | JSON.parse(fs.readFileSync(file)).resource_changes || [];
114 | try {
115 | let changed_resources = resource_changes.filter((resource) => {
116 | return resource.change.actions != ["no-op"];
117 | });
118 |
119 | if (logChangedResources) {
120 | console.log("changed_resources", changed_resources);
121 | }
122 | if (Array.isArray(resource_changes) && resource_changes.length > 0) {
123 | const resources_to_create = [],
124 | resources_to_update = [],
125 | resources_to_delete = [],
126 | resources_to_replace = [],
127 | resources_to_tag = [],
128 | resources_unchanged = [];
129 |
130 | // Deep comparison function to check if objects are identical after removing tags
131 | const isTagOnlyChange = (before, after) => {
132 | if (includeTagOnlyResources == false) {
133 | return false;
134 | }
135 | const beforeCopy = JSON.parse(JSON.stringify(before || {}));
136 | const afterCopy = JSON.parse(JSON.stringify(after || {}));
137 |
138 | delete beforeCopy.tags;
139 | delete beforeCopy.tags_all;
140 | delete afterCopy.tags;
141 | delete afterCopy.tags_all;
142 |
143 | return JSON.stringify(beforeCopy) === JSON.stringify(afterCopy);
144 | };
145 |
146 | // for each resource changes
147 | for (const resource of resource_changes) {
148 | const change = resource.change;
149 | const address = resource.address;
150 |
151 | switch (change.actions[0]) {
152 | default:
153 | break;
154 | case "no-op":
155 | resources_unchanged.push(address);
156 | break;
157 | case "create":
158 | resources_to_create.push(address);
159 | break;
160 | case "delete":
161 | if (change.actions.length > 1) {
162 | resources_to_replace.push(address);
163 | } else {
164 | resources_to_delete.push(address);
165 | }
166 | break;
167 | case "update":
168 | if (isTagOnlyChange(change.before, change.after)) {
169 | resources_to_tag.push(address);
170 | } else {
171 | resources_to_update.push(address);
172 | }
173 | break;
174 | }
175 | }
176 | // the body must be indented at the start otherwise
177 | // there will be formatting error when comment is
178 | // showed on GitHub
179 | let planSummary = `Terraform Plan: ${resources_to_create.length} to be created, ${resources_to_delete.length} to be deleted, ${resources_to_update.length} to be updated${includeTagOnlyResources ? `, ${resources_to_tag.length} to be tagged` : ""}, ${resources_to_replace.length} to be replaced, ${resources_unchanged.length} unchanged.`;
180 |
181 | let fullBody = `
182 | ${commentHeader} for \`${file}\`
183 |
185 | ${planSummary}
186 |
187 | ${includeUnchangedResources ? details("unchanged", resources_unchanged, "•") : ""}
188 | ${details("create", resources_to_create, "+")}
189 | ${details("delete", resources_to_delete, "-")}
190 | ${details("update", resources_to_update, "!")}
191 | ${includeTagOnlyResources ? details("tag", resources_to_tag, "!") : ""}
192 | ${details("replace", resources_to_replace, "+")}
193 |
Sorry, the detailed plan exceeded GitHub's comment size limit (${MAX_COMMENT_LENGTH} characters) and has been truncated. Please see the workflow run for the full plan output.
204 | ${commentFooter.map((a) => (a == "" ? "\n" : a)).join("\n")} 205 | ${workflowLink} 206 | ${jobLink} 207 | `; 208 | } else { 209 | body += fullBody; 210 | } 211 | if ( 212 | resources_to_create + 213 | resources_to_delete + 214 | resources_to_update + 215 | resources_to_tag + 216 | resources_to_replace == 217 | [] 218 | ) { 219 | hasNoChanges = true; 220 | } 221 | } else { 222 | hasNoChanges = true; 223 | console.log( 224 | "No changes found in the plan. setting hasNoChanges to true.", 225 | ); 226 | body += ` 227 |There were no changes done to the infrastructure.
228 | `; 229 | core.info( 230 | `"The content of ${file} did not result in a valid array or the array is empty... Skipping."`, 231 | ); 232 | } 233 | } catch (error) { 234 | core.error(`${file} is not a valid JSON file. error: ${error}`); 235 | } 236 | } 237 | return body; 238 | }; 239 | 240 | const details = (action, resources, operator) => { 241 | let str_title = ""; 242 | let str = ""; 243 | 244 | if (resources.length !== 0) { 245 | if (action === "unchanged") { 246 | str_title = "Unchanged resources"; 247 | } else { 248 | str_title = `Resources to ${action}`; 249 | } 250 | str = ` 251 | #### ${str_title}\n 252 | \`\`\`diff\n 253 | `; 254 | for (const el of resources) { 255 | // In the replace block, we show delete (-) and then create (+) 256 | if (action === "replace") { 257 | str += `- ${el}\n`; 258 | } 259 | str += `${operator} ${el}\n`; 260 | } 261 | 262 | str += "```\n"; 263 | } 264 | 265 | return str; 266 | }; 267 | 268 | const queryComments = (variables) => { 269 | return octokit.graphql(commentsQuery, variables); 270 | }; 271 | 272 | const minimizeComment = (variables) => { 273 | return octokit.graphql(minimizeCommentQuery, variables); 274 | }; 275 | 276 | const hideComments = () => { 277 | core.info(`Hiding previous comments.`); 278 | 279 | queryComments({ 280 | owner: context.repo.owner, 281 | name: context.repo.repo, 282 | number: context.issue.number, 283 | }) 284 | .then((response) => { 285 | core.info( 286 | `Successfully retrieved comments for PR #${context.issue.number}.`, 287 | ); 288 | const comments = response.repository.pullRequest.comments.nodes; 289 | 290 | core.info(`Found ${comments.length} comments in the PR.`); 291 | 292 | core.info(`Comment header used for matching is: ${commentHeader}`); 293 | 294 | filteredComments = comments.filter( 295 | (comment) => 296 | comment.body.includes("Terraform Plan:") || 297 | comment.body.includes( 298 | "There were no changes done to the infrastructure.", 299 | ), 300 | ); 301 | 302 | core.info( 303 | `Filtered down to ${filteredComments.length} comments created by this action.`, 304 | ); 305 | 306 | filteredComments = filteredComments.filter((comment) => 307 | comment.body.includes(commentHeader), 308 | ); 309 | 310 | core.info( 311 | `Filtered down to ${filteredComments.length} comments created by this action.`, 312 | ); 313 | 314 | filteredComments = filteredComments.filter((comment) => 315 | comment.body.includes(commentHeader), 316 | ); 317 | 318 | core.info( 319 | `Filtered down to ${filteredComments.length} comments that need to be minimized.`, 320 | ); 321 | 322 | const minimizePromises = filteredComments 323 | .filter((comment) => !comment.isMinimized) 324 | .map((comment) => { 325 | return minimizeComment({ id: comment.id }).catch((error) => 326 | core.error( 327 | `Failed to minimize comment ${comment.id}: ${error.message}`, 328 | ), 329 | ); 330 | }); 331 | 332 | return Promise.all(minimizePromises) 333 | .then(() => core.info("All minimize operations completed.")) 334 | .catch((error) => 335 | core.error(`Error during minimize operations: ${error.message}`), 336 | ); 337 | }) 338 | .catch((error) => 339 | core.error(`Failed to retrieve comments: ${error.message}`), 340 | ); 341 | }; 342 | 343 | // Main execution wrapped in an async function to allow for await 344 | async function run() { 345 | try { 346 | // Get job link if needed 347 | if (includeLinkToJob) { 348 | jobLink = await getJobId(); 349 | console.log("Job link generated:", jobLink); 350 | } 351 | 352 | let rawOutput = output(); 353 | let createComment = true; 354 | 355 | console.log("hidePreviousComments", hidePreviousComments); 356 | console.log( 357 | "hidePreviousComments && context.eventName === pull_request", 358 | hidePreviousComments && (context.eventName === "pull_request" || context.eventName === "pull_request_target"), 359 | ); 360 | if (hidePreviousComments && (context.eventName === "pull_request" || context.eventName === "pull_request_target")) { 361 | hideComments(); 362 | } 363 | 364 | console.log("includePlanSummary", includePlanSummary); 365 | if (includePlanSummary) { 366 | core.info("Adding plan output to job summary"); 367 | core.summary 368 | .addHeading("Terraform Plan Results") 369 | .addRaw(rawOutput) 370 | .write(); 371 | } 372 | 373 | console.log("quietMode", quietMode); 374 | console.log("hasNoChanges", hasNoChanges); 375 | console.log("quietMode && hasNoChanges", quietMode && hasNoChanges); 376 | if (quietMode && hasNoChanges) { 377 | core.info( 378 | "quiet mode is enabled and there are no changes to the infrastructure.", 379 | ); 380 | core.info("Skipping comment creation."); 381 | createComment = false; 382 | } 383 | 384 | if ( 385 | context.eventName === "pull_request" || 386 | context.eventName === "pull_request_target" || 387 | context.eventName === "workflow_call" 388 | ) { 389 | // Verify we have PR context available in the case that it's a workflow_call event- should always pass for pull_request 390 | if (context.issue && context.issue.number) { 391 | core.info( 392 | `Found PR # ${context.issue.number} from ${context.eventName} event - proceeding to comment.`, 393 | ); 394 | } else { 395 | core.info( 396 | `${context.eventName} event detected but no PR context available.`, 397 | ); 398 | core.info("Skipping comment creation."); 399 | createComment = false; 400 | } 401 | } else { 402 | core.info("Action doesn't seem to be running in a PR workflow context."); 403 | core.info("Skipping comment creation."); 404 | createComment = false; 405 | } 406 | 407 | if (createComment) { 408 | core.info("Adding comment to PR"); 409 | core.info(`Comment: ${rawOutput}`); 410 | octokit.rest.issues.createComment({ 411 | issue_number: context.issue.number, 412 | owner: context.repo.owner, 413 | repo: context.repo.repo, 414 | body: rawOutput, 415 | }); 416 | core.info("Comment added successfully."); 417 | } 418 | core.setOutput("comment-body", rawOutput); 419 | } catch (error) { 420 | core.setFailed(error.message); 421 | } 422 | } 423 | 424 | // Execute the main function 425 | run(); 426 | -------------------------------------------------------------------------------- /dist/licenses.txt: -------------------------------------------------------------------------------- 1 | @actions/core 2 | MIT 3 | The MIT License (MIT) 4 | 5 | Copyright 2019 GitHub 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | 13 | @actions/github 14 | MIT 15 | The MIT License (MIT) 16 | 17 | Copyright 2019 GitHub 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | @actions/http-client 26 | MIT 27 | Actions Http Client for Node.js 28 | 29 | Copyright (c) GitHub, Inc. 30 | 31 | All rights reserved. 32 | 33 | MIT License 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 36 | associated documentation files (the "Software"), to deal in the Software without restriction, 37 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 38 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 39 | subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 44 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 45 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 46 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 47 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 48 | 49 | 50 | @octokit/auth-token 51 | MIT 52 | The MIT License 53 | 54 | Copyright (c) 2019 Octokit contributors 55 | 56 | Permission is hereby granted, free of charge, to any person obtaining a copy 57 | of this software and associated documentation files (the "Software"), to deal 58 | in the Software without restriction, including without limitation the rights 59 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 60 | copies of the Software, and to permit persons to whom the Software is 61 | furnished to do so, subject to the following conditions: 62 | 63 | The above copyright notice and this permission notice shall be included in 64 | all copies or substantial portions of the Software. 65 | 66 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 67 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 68 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 69 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 70 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 71 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 72 | THE SOFTWARE. 73 | 74 | 75 | @octokit/core 76 | MIT 77 | The MIT License 78 | 79 | Copyright (c) 2019 Octokit contributors 80 | 81 | Permission is hereby granted, free of charge, to any person obtaining a copy 82 | of this software and associated documentation files (the "Software"), to deal 83 | in the Software without restriction, including without limitation the rights 84 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 85 | copies of the Software, and to permit persons to whom the Software is 86 | furnished to do so, subject to the following conditions: 87 | 88 | The above copyright notice and this permission notice shall be included in 89 | all copies or substantial portions of the Software. 90 | 91 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 92 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 93 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 94 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 95 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 96 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 97 | THE SOFTWARE. 98 | 99 | 100 | @octokit/endpoint 101 | MIT 102 | The MIT License 103 | 104 | Copyright (c) 2018 Octokit contributors 105 | 106 | Permission is hereby granted, free of charge, to any person obtaining a copy 107 | of this software and associated documentation files (the "Software"), to deal 108 | in the Software without restriction, including without limitation the rights 109 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 110 | copies of the Software, and to permit persons to whom the Software is 111 | furnished to do so, subject to the following conditions: 112 | 113 | The above copyright notice and this permission notice shall be included in 114 | all copies or substantial portions of the Software. 115 | 116 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 117 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 118 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 119 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 120 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 121 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 122 | THE SOFTWARE. 123 | 124 | 125 | @octokit/graphql 126 | MIT 127 | The MIT License 128 | 129 | Copyright (c) 2018 Octokit contributors 130 | 131 | Permission is hereby granted, free of charge, to any person obtaining a copy 132 | of this software and associated documentation files (the "Software"), to deal 133 | in the Software without restriction, including without limitation the rights 134 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 135 | copies of the Software, and to permit persons to whom the Software is 136 | furnished to do so, subject to the following conditions: 137 | 138 | The above copyright notice and this permission notice shall be included in 139 | all copies or substantial portions of the Software. 140 | 141 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 142 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 143 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 144 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 145 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 146 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 147 | THE SOFTWARE. 148 | 149 | 150 | @octokit/plugin-paginate-rest 151 | MIT 152 | MIT License Copyright (c) 2019 Octokit contributors 153 | 154 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 155 | 156 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 157 | 158 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 159 | 160 | 161 | @octokit/plugin-rest-endpoint-methods 162 | MIT 163 | MIT License Copyright (c) 2019 Octokit contributors 164 | 165 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 166 | 167 | The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. 168 | 169 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 170 | 171 | 172 | @octokit/request 173 | MIT 174 | The MIT License 175 | 176 | Copyright (c) 2018 Octokit contributors 177 | 178 | Permission is hereby granted, free of charge, to any person obtaining a copy 179 | of this software and associated documentation files (the "Software"), to deal 180 | in the Software without restriction, including without limitation the rights 181 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 182 | copies of the Software, and to permit persons to whom the Software is 183 | furnished to do so, subject to the following conditions: 184 | 185 | The above copyright notice and this permission notice shall be included in 186 | all copies or substantial portions of the Software. 187 | 188 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 189 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 190 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 191 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 192 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 193 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 194 | THE SOFTWARE. 195 | 196 | 197 | @octokit/request-error 198 | MIT 199 | The MIT License 200 | 201 | Copyright (c) 2019 Octokit contributors 202 | 203 | Permission is hereby granted, free of charge, to any person obtaining a copy 204 | of this software and associated documentation files (the "Software"), to deal 205 | in the Software without restriction, including without limitation the rights 206 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 207 | copies of the Software, and to permit persons to whom the Software is 208 | furnished to do so, subject to the following conditions: 209 | 210 | The above copyright notice and this permission notice shall be included in 211 | all copies or substantial portions of the Software. 212 | 213 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 214 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 215 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 216 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 217 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 218 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 219 | THE SOFTWARE. 220 | 221 | 222 | before-after-hook 223 | Apache-2.0 224 | Apache License 225 | Version 2.0, January 2004 226 | http://www.apache.org/licenses/ 227 | 228 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 229 | 230 | 1. Definitions. 231 | 232 | "License" shall mean the terms and conditions for use, reproduction, 233 | and distribution as defined by Sections 1 through 9 of this document. 234 | 235 | "Licensor" shall mean the copyright owner or entity authorized by 236 | the copyright owner that is granting the License. 237 | 238 | "Legal Entity" shall mean the union of the acting entity and all 239 | other entities that control, are controlled by, or are under common 240 | control with that entity. For the purposes of this definition, 241 | "control" means (i) the power, direct or indirect, to cause the 242 | direction or management of such entity, whether by contract or 243 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 244 | outstanding shares, or (iii) beneficial ownership of such entity. 245 | 246 | "You" (or "Your") shall mean an individual or Legal Entity 247 | exercising permissions granted by this License. 248 | 249 | "Source" form shall mean the preferred form for making modifications, 250 | including but not limited to software source code, documentation 251 | source, and configuration files. 252 | 253 | "Object" form shall mean any form resulting from mechanical 254 | transformation or translation of a Source form, including but 255 | not limited to compiled object code, generated documentation, 256 | and conversions to other media types. 257 | 258 | "Work" shall mean the work of authorship, whether in Source or 259 | Object form, made available under the License, as indicated by a 260 | copyright notice that is included in or attached to the work 261 | (an example is provided in the Appendix below). 262 | 263 | "Derivative Works" shall mean any work, whether in Source or Object 264 | form, that is based on (or derived from) the Work and for which the 265 | editorial revisions, annotations, elaborations, or other modifications 266 | represent, as a whole, an original work of authorship. For the purposes 267 | of this License, Derivative Works shall not include works that remain 268 | separable from, or merely link (or bind by name) to the interfaces of, 269 | the Work and Derivative Works thereof. 270 | 271 | "Contribution" shall mean any work of authorship, including 272 | the original version of the Work and any modifications or additions 273 | to that Work or Derivative Works thereof, that is intentionally 274 | submitted to Licensor for inclusion in the Work by the copyright owner 275 | or by an individual or Legal Entity authorized to submit on behalf of 276 | the copyright owner. For the purposes of this definition, "submitted" 277 | means any form of electronic, verbal, or written communication sent 278 | to the Licensor or its representatives, including but not limited to 279 | communication on electronic mailing lists, source code control systems, 280 | and issue tracking systems that are managed by, or on behalf of, the 281 | Licensor for the purpose of discussing and improving the Work, but 282 | excluding communication that is conspicuously marked or otherwise 283 | designated in writing by the copyright owner as "Not a Contribution." 284 | 285 | "Contributor" shall mean Licensor and any individual or Legal Entity 286 | on behalf of whom a Contribution has been received by Licensor and 287 | subsequently incorporated within the Work. 288 | 289 | 2. Grant of Copyright License. Subject to the terms and conditions of 290 | this License, each Contributor hereby grants to You a perpetual, 291 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 292 | copyright license to reproduce, prepare Derivative Works of, 293 | publicly display, publicly perform, sublicense, and distribute the 294 | Work and such Derivative Works in Source or Object form. 295 | 296 | 3. Grant of Patent License. Subject to the terms and conditions of 297 | this License, each Contributor hereby grants to You a perpetual, 298 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 299 | (except as stated in this section) patent license to make, have made, 300 | use, offer to sell, sell, import, and otherwise transfer the Work, 301 | where such license applies only to those patent claims licensable 302 | by such Contributor that are necessarily infringed by their 303 | Contribution(s) alone or by combination of their Contribution(s) 304 | with the Work to which such Contribution(s) was submitted. If You 305 | institute patent litigation against any entity (including a 306 | cross-claim or counterclaim in a lawsuit) alleging that the Work 307 | or a Contribution incorporated within the Work constitutes direct 308 | or contributory patent infringement, then any patent licenses 309 | granted to You under this License for that Work shall terminate 310 | as of the date such litigation is filed. 311 | 312 | 4. Redistribution. You may reproduce and distribute copies of the 313 | Work or Derivative Works thereof in any medium, with or without 314 | modifications, and in Source or Object form, provided that You 315 | meet the following conditions: 316 | 317 | (a) You must give any other recipients of the Work or 318 | Derivative Works a copy of this License; and 319 | 320 | (b) You must cause any modified files to carry prominent notices 321 | stating that You changed the files; and 322 | 323 | (c) You must retain, in the Source form of any Derivative Works 324 | that You distribute, all copyright, patent, trademark, and 325 | attribution notices from the Source form of the Work, 326 | excluding those notices that do not pertain to any part of 327 | the Derivative Works; and 328 | 329 | (d) If the Work includes a "NOTICE" text file as part of its 330 | distribution, then any Derivative Works that You distribute must 331 | include a readable copy of the attribution notices contained 332 | within such NOTICE file, excluding those notices that do not 333 | pertain to any part of the Derivative Works, in at least one 334 | of the following places: within a NOTICE text file distributed 335 | as part of the Derivative Works; within the Source form or 336 | documentation, if provided along with the Derivative Works; or, 337 | within a display generated by the Derivative Works, if and 338 | wherever such third-party notices normally appear. The contents 339 | of the NOTICE file are for informational purposes only and 340 | do not modify the License. You may add Your own attribution 341 | notices within Derivative Works that You distribute, alongside 342 | or as an addendum to the NOTICE text from the Work, provided 343 | that such additional attribution notices cannot be construed 344 | as modifying the License. 345 | 346 | You may add Your own copyright statement to Your modifications and 347 | may provide additional or different license terms and conditions 348 | for use, reproduction, or distribution of Your modifications, or 349 | for any such Derivative Works as a whole, provided Your use, 350 | reproduction, and distribution of the Work otherwise complies with 351 | the conditions stated in this License. 352 | 353 | 5. Submission of Contributions. Unless You explicitly state otherwise, 354 | any Contribution intentionally submitted for inclusion in the Work 355 | by You to the Licensor shall be under the terms and conditions of 356 | this License, without any additional terms or conditions. 357 | Notwithstanding the above, nothing herein shall supersede or modify 358 | the terms of any separate license agreement you may have executed 359 | with Licensor regarding such Contributions. 360 | 361 | 6. Trademarks. This License does not grant permission to use the trade 362 | names, trademarks, service marks, or product names of the Licensor, 363 | except as required for reasonable and customary use in describing the 364 | origin of the Work and reproducing the content of the NOTICE file. 365 | 366 | 7. Disclaimer of Warranty. Unless required by applicable law or 367 | agreed to in writing, Licensor provides the Work (and each 368 | Contributor provides its Contributions) on an "AS IS" BASIS, 369 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 370 | implied, including, without limitation, any warranties or conditions 371 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 372 | PARTICULAR PURPOSE. You are solely responsible for determining the 373 | appropriateness of using or redistributing the Work and assume any 374 | risks associated with Your exercise of permissions under this License. 375 | 376 | 8. Limitation of Liability. In no event and under no legal theory, 377 | whether in tort (including negligence), contract, or otherwise, 378 | unless required by applicable law (such as deliberate and grossly 379 | negligent acts) or agreed to in writing, shall any Contributor be 380 | liable to You for damages, including any direct, indirect, special, 381 | incidental, or consequential damages of any character arising as a 382 | result of this License or out of the use or inability to use the 383 | Work (including but not limited to damages for loss of goodwill, 384 | work stoppage, computer failure or malfunction, or any and all 385 | other commercial damages or losses), even if such Contributor 386 | has been advised of the possibility of such damages. 387 | 388 | 9. Accepting Warranty or Additional Liability. While redistributing 389 | the Work or Derivative Works thereof, You may choose to offer, 390 | and charge a fee for, acceptance of support, warranty, indemnity, 391 | or other liability obligations and/or rights consistent with this 392 | License. However, in accepting such obligations, You may act only 393 | on Your own behalf and on Your sole responsibility, not on behalf 394 | of any other Contributor, and only if You agree to indemnify, 395 | defend, and hold each Contributor harmless for any liability 396 | incurred by, or claims asserted against, such Contributor by reason 397 | of your accepting any such warranty or additional liability. 398 | 399 | END OF TERMS AND CONDITIONS 400 | 401 | APPENDIX: How to apply the Apache License to your work. 402 | 403 | To apply the Apache License to your work, attach the following 404 | boilerplate notice, with the fields enclosed by brackets "{}" 405 | replaced with your own identifying information. (Don't include 406 | the brackets!) The text should be enclosed in the appropriate 407 | comment syntax for the file format. We also recommend that a 408 | file or class name and description of purpose be included on the 409 | same "printed page" as the copyright notice for easier 410 | identification within third-party archives. 411 | 412 | Copyright 2018 Gregor Martynus and other contributors. 413 | 414 | Licensed under the Apache License, Version 2.0 (the "License"); 415 | you may not use this file except in compliance with the License. 416 | You may obtain a copy of the License at 417 | 418 | http://www.apache.org/licenses/LICENSE-2.0 419 | 420 | Unless required by applicable law or agreed to in writing, software 421 | distributed under the License is distributed on an "AS IS" BASIS, 422 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 423 | See the License for the specific language governing permissions and 424 | limitations under the License. 425 | 426 | 427 | deprecation 428 | ISC 429 | The ISC License 430 | 431 | Copyright (c) Gregor Martynus and contributors 432 | 433 | Permission to use, copy, modify, and/or distribute this software for any 434 | purpose with or without fee is hereby granted, provided that the above 435 | copyright notice and this permission notice appear in all copies. 436 | 437 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 438 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 439 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 440 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 441 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 442 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 443 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 444 | 445 | 446 | encoding 447 | MIT 448 | Copyright (c) 2012-2014 Andris Reinman 449 | 450 | Permission is hereby granted, free of charge, to any person obtaining a copy 451 | of this software and associated documentation files (the "Software"), to deal 452 | in the Software without restriction, including without limitation the rights 453 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 454 | copies of the Software, and to permit persons to whom the Software is 455 | furnished to do so, subject to the following conditions: 456 | 457 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 458 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 459 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 460 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 461 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 462 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 463 | SOFTWARE. 464 | 465 | 466 | iconv-lite 467 | MIT 468 | Copyright (c) 2011 Alexander Shtuchkin 469 | 470 | Permission is hereby granted, free of charge, to any person obtaining 471 | a copy of this software and associated documentation files (the 472 | "Software"), to deal in the Software without restriction, including 473 | without limitation the rights to use, copy, modify, merge, publish, 474 | distribute, sublicense, and/or sell copies of the Software, and to 475 | permit persons to whom the Software is furnished to do so, subject to 476 | the following conditions: 477 | 478 | The above copyright notice and this permission notice shall be 479 | included in all copies or substantial portions of the Software. 480 | 481 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 482 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 483 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 484 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 485 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 486 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 487 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 488 | 489 | 490 | 491 | is-plain-object 492 | MIT 493 | The MIT License (MIT) 494 | 495 | Copyright (c) 2014-2017, Jon Schlinkert. 496 | 497 | Permission is hereby granted, free of charge, to any person obtaining a copy 498 | of this software and associated documentation files (the "Software"), to deal 499 | in the Software without restriction, including without limitation the rights 500 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 501 | copies of the Software, and to permit persons to whom the Software is 502 | furnished to do so, subject to the following conditions: 503 | 504 | The above copyright notice and this permission notice shall be included in 505 | all copies or substantial portions of the Software. 506 | 507 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 508 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 509 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 510 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 511 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 512 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 513 | THE SOFTWARE. 514 | 515 | 516 | node-fetch 517 | MIT 518 | The MIT License (MIT) 519 | 520 | Copyright (c) 2016 David Frank 521 | 522 | Permission is hereby granted, free of charge, to any person obtaining a copy 523 | of this software and associated documentation files (the "Software"), to deal 524 | in the Software without restriction, including without limitation the rights 525 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 526 | copies of the Software, and to permit persons to whom the Software is 527 | furnished to do so, subject to the following conditions: 528 | 529 | The above copyright notice and this permission notice shall be included in all 530 | copies or substantial portions of the Software. 531 | 532 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 533 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 534 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 535 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 536 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 537 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 538 | SOFTWARE. 539 | 540 | 541 | 542 | once 543 | ISC 544 | The ISC License 545 | 546 | Copyright (c) Isaac Z. Schlueter and Contributors 547 | 548 | Permission to use, copy, modify, and/or distribute this software for any 549 | purpose with or without fee is hereby granted, provided that the above 550 | copyright notice and this permission notice appear in all copies. 551 | 552 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 553 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 554 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 555 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 556 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 557 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 558 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 559 | 560 | 561 | safer-buffer 562 | MIT 563 | MIT License 564 | 565 | Copyright (c) 2018 Nikita Skovoroda