Skip to content

Commit bd5d6aa

Browse files
feat: allow workflow calls as triggers (#89)
* fix: updated check to allow workflow calls as triggers * Potential fix for code scanning alert no. 7: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 6: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fix: added missing perm to reusable workflow --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent 2b9ade3 commit bd5d6aa

File tree

4 files changed

+156
-19
lines changed

4 files changed

+156
-19
lines changed

.github/workflows/action-test.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@ name: Action Tester Workflow
22

33
on:
44
pull_request:
5+
workflow_call:
6+
inputs:
7+
test-mode:
8+
description: 'Test mode for workflow_call testing'
9+
required: false
10+
type: string
11+
default: 'reusable'
12+
13+
permissions:
14+
contents: read
15+
pull-requests: write
516

617
jobs:
718
plan:
@@ -81,3 +92,10 @@ jobs:
8192
include-plan-job-summary: true
8293
include-tag-only-resources: true
8394
include-unchanged-resources: true
95+
96+
# Test workflow_call functionality by calling a reusable workflow
97+
test-reusable-workflow:
98+
if: github.event_name == 'pull_request'
99+
uses: ./.github/workflows/reusable-terraform-test.yml
100+
with:
101+
test-mode: 'workflow_call_test'
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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"

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Implementing this Action is _super_ simple and the comments are consise and easy
1212
- 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.
1313
- 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.
1414
- 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 and reusable workflows (supports `workflow_call` events).
1516
- Possibility to add the output to your workflow summary.
1617
- Possibility to hide previous comments generated by this action.
1718
- Possibility to not create any comments in case there are no infrastructure changes.
@@ -120,6 +121,69 @@ with:
120121
```
121122
**Note:**
122123
- 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 `workflow_call` events, the action requires that the calling workflow was triggered by a pull request to have the necessary PR context. 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+
123187
#### Example Job Summary Output
124188
![Plan output job summary](assets/plan-output-job-summary.png)
125189

index.js

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@ const includeLinkToWorkflow = core.getBooleanInput("include-workflow-link");
1515
const includeLinkToJob = core.getBooleanInput("include-job-link");
1616
const hidePreviousComments = core.getBooleanInput("hide-previous-comments");
1717
const logChangedResources = core.getBooleanInput("log-changed-resources");
18-
const includeTagOnlyResources = core.getBooleanInput("include-tag-only-resources");
19-
const includeUnchangedResources = core.getBooleanInput("include-unchanged-resources");
20-
18+
const includeTagOnlyResources = core.getBooleanInput(
19+
"include-tag-only-resources",
20+
);
21+
const includeUnchangedResources = core.getBooleanInput(
22+
"include-unchanged-resources",
23+
);
2124

2225
// Get current job name from GitHub environment variable
23-
const currentJobName = process.env.GITHUB_JOB || '';
24-
const currentRunnerName = process.env.RUNNER_NAME || '';
26+
const currentJobName = process.env.GITHUB_JOB || "";
27+
const currentRunnerName = process.env.RUNNER_NAME || "";
2528

2629
// Log the job name for debugging
27-
console.log('Current job name:', currentJobName);
30+
console.log("Current job name:", currentJobName);
2831

2932
const workflowLink = includeLinkToWorkflow
3033
? `
@@ -45,13 +48,15 @@ async function getJobId() {
4548
repo: context.repo.repo,
4649
run_id: context.runId,
4750
});
48-
51+
4952
// Find the current job by name
50-
const job = response.data.jobs.find(job =>
51-
job.runner_name === currentRunnerName &&
52-
(job.name.endsWith(currentJobName) || job.name.startsWith(currentJobName))
53+
const job = response.data.jobs.find(
54+
(job) =>
55+
job.runner_name === currentRunnerName &&
56+
(job.name.endsWith(currentJobName) ||
57+
job.name.startsWith(currentJobName)),
5358
);
54-
59+
5560
if (job) {
5661
console.log(`Found job ID: ${job.id} for job name: ${job.name}`);
5762
// Create job link with the numeric job ID
@@ -102,7 +107,8 @@ const output = () => {
102107
let body = "";
103108
// for each file
104109
for (const file of inputFilenames) {
105-
const resource_changes = JSON.parse(fs.readFileSync(file)).resource_changes || [];
110+
const resource_changes =
111+
JSON.parse(fs.readFileSync(file)).resource_changes || [];
106112
try {
107113
let changed_resources = resource_changes.filter((resource) => {
108114
return resource.change.actions != ["no-op"];
@@ -172,7 +178,7 @@ const output = () => {
172178
${commentHeader}
173179
<details ${expandDetailsComment ? "open" : ""}>
174180
<summary>
175-
<b>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.</b>
181+
<b>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.</b>
176182
</summary>
177183
${includeUnchangedResources ? details("unchanged", resources_unchanged, "•") : ""}
178184
${details("create", resources_to_create, "+")}
@@ -317,7 +323,7 @@ async function run() {
317323
jobLink = await getJobId();
318324
console.log("Job link generated:", jobLink);
319325
}
320-
326+
321327
let rawOutput = output();
322328
let createComment = true;
323329

@@ -333,7 +339,10 @@ async function run() {
333339
console.log("includePlanSummary", includePlanSummary);
334340
if (includePlanSummary) {
335341
core.info("Adding plan output to job summary");
336-
core.summary.addHeading("Terraform Plan Results").addRaw(rawOutput).write();
342+
core.summary
343+
.addHeading("Terraform Plan Results")
344+
.addRaw(rawOutput)
345+
.write();
337346
}
338347

339348
console.log("quietMode", quietMode);
@@ -347,10 +356,22 @@ async function run() {
347356
createComment = false;
348357
}
349358

350-
if (context.eventName === "pull_request") {
351-
core.info(
352-
`Found PR # ${context.issue.number} from workflow context - proceeding to comment.`,
353-
);
359+
if (
360+
context.eventName === "pull_request" ||
361+
context.eventName === "workflow_call"
362+
) {
363+
// Verify we have PR context available in the case that it's a workflow_call event- should always pass for pull_request
364+
if (context.issue && context.issue.number) {
365+
core.info(
366+
`Found PR # ${context.issue.number} from ${context.eventName} event - proceeding to comment.`,
367+
);
368+
} else {
369+
core.info(
370+
`${context.eventName} event detected but no PR context available.`,
371+
);
372+
core.info("Skipping comment creation.");
373+
createComment = false;
374+
}
354375
} else {
355376
core.info("Action doesn't seem to be running in a PR workflow context.");
356377
core.info("Skipping comment creation.");

0 commit comments

Comments
 (0)