From d2a0dcfd8fbb61c2f3ad36815f211f868e342610 Mon Sep 17 00:00:00 2001 From: sufiyan733 Date: Sun, 20 Sep 2026 23:47:45 +0530 Subject: [PATCH] fix(cli): ensure trailing newline in GitHub Actions outputs and env vars (#4003) --- .changeset/github-actions-output-newline.md | 5 + .../src/utilities/githubActions.test.ts | 102 ++++++++++++++++++ .../cli-v3/src/utilities/githubActions.ts | 18 ++-- 3 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 .changeset/github-actions-output-newline.md create mode 100644 packages/cli-v3/src/utilities/githubActions.test.ts diff --git a/.changeset/github-actions-output-newline.md b/.changeset/github-actions-output-newline.md new file mode 100644 index 00000000000..d2a34f39687 --- /dev/null +++ b/.changeset/github-actions-output-newline.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Ensure GitHub Actions environment variables and step outputs are terminated with a trailing newline. diff --git a/packages/cli-v3/src/utilities/githubActions.test.ts b/packages/cli-v3/src/utilities/githubActions.test.ts new file mode 100644 index 00000000000..2bd162ca2ed --- /dev/null +++ b/packages/cli-v3/src/utilities/githubActions.test.ts @@ -0,0 +1,102 @@ +import { readFileSync, writeFileSync, unlinkSync, existsSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, test } from "vitest"; +import { setGithubActionsOutputAndEnvVars } from "./githubActions.js"; + +describe("setGithubActionsOutputAndEnvVars", () => { + const originalEnv = process.env; + let envFilePath: string; + let outputFilePath: string; + + beforeEach(() => { + process.env = { ...originalEnv }; + const id = Math.random().toString(36).substring(2, 9); + envFilePath = join(tmpdir(), `gh-env-test-${id}.txt`); + outputFilePath = join(tmpdir(), `gh-output-test-${id}.txt`); + writeFileSync(envFilePath, ""); + writeFileSync(outputFilePath, ""); + process.env.GITHUB_ENV = envFilePath; + process.env.GITHUB_OUTPUT = outputFilePath; + }); + + afterEach(() => { + process.env = originalEnv; + if (existsSync(envFilePath)) { + unlinkSync(envFilePath); + } + if (existsSync(outputFilePath)) { + unlinkSync(outputFilePath); + } + }); + + test("writes entries with trailing newline", () => { + setGithubActionsOutputAndEnvVars({ + envVars: { + VAR_ONE: "value1", + VAR_TWO: "value2", + }, + outputs: { + outOne: "val1", + outTwo: "val2", + }, + }); + + const envContent = readFileSync(envFilePath, "utf-8"); + const outputContent = readFileSync(outputFilePath, "utf-8"); + + expect(envContent).toBe("VAR_ONE=value1\nVAR_TWO=value2\n"); + expect(outputContent).toBe("outOne=val1\noutTwo=val2\n"); + }); + + test("multiple sequential calls terminate each line and do not concatenate keys", () => { + setGithubActionsOutputAndEnvVars({ + envVars: { + TRIGGER_VERSION: "1.0.0", + }, + outputs: { + needsPromotion: "false", + }, + }); + + setGithubActionsOutputAndEnvVars({ + envVars: { + NEXT_VAR: "next", + }, + outputs: { + subsequentOutput: "hello", + }, + }); + + const envContent = readFileSync(envFilePath, "utf-8"); + const outputContent = readFileSync(outputFilePath, "utf-8"); + + expect(envContent).toBe("TRIGGER_VERSION=1.0.0\nNEXT_VAR=next\n"); + expect(outputContent).toBe("needsPromotion=false\nsubsequentOutput=hello\n"); + }); + + test("empty entries do not append trailing newline or modify file", () => { + setGithubActionsOutputAndEnvVars({ + envVars: {}, + outputs: {}, + }); + + expect(readFileSync(envFilePath, "utf-8")).toBe(""); + expect(readFileSync(outputFilePath, "utf-8")).toBe(""); + }); + + test("does nothing if GITHUB_ENV or GITHUB_OUTPUT are not set", () => { + delete process.env.GITHUB_ENV; + delete process.env.GITHUB_OUTPUT; + + expect(() => { + setGithubActionsOutputAndEnvVars({ + envVars: { FOO: "bar" }, + outputs: { BAZ: "qux" }, + }); + }).not.toThrow(); + + expect(readFileSync(envFilePath, "utf-8")).toBe(""); + expect(readFileSync(outputFilePath, "utf-8")).toBe(""); + }); +}); diff --git a/packages/cli-v3/src/utilities/githubActions.ts b/packages/cli-v3/src/utilities/githubActions.ts index a4a80e75a1e..ae775e0701a 100644 --- a/packages/cli-v3/src/utilities/githubActions.ts +++ b/packages/cli-v3/src/utilities/githubActions.ts @@ -9,19 +9,21 @@ export function setGithubActionsOutputAndEnvVars({ }) { // Set environment variables if (process.env.GITHUB_ENV) { - const contents = Object.entries(envVars) - .map(([key, value]) => `${key}=${value}`) - .join("\n"); + const entries = Object.entries(envVars); + if (entries.length > 0) { + const contents = `${entries.map(([key, value]) => `${key}=${value}`).join("\n")}\n`; - appendFileSync(process.env.GITHUB_ENV, contents); + appendFileSync(process.env.GITHUB_ENV, contents); + } } // Set outputs if (process.env.GITHUB_OUTPUT) { - const contents = Object.entries(outputs) - .map(([key, value]) => `${key}=${value}`) - .join("\n"); + const entries = Object.entries(outputs); + if (entries.length > 0) { + const contents = `${entries.map(([key, value]) => `${key}=${value}`).join("\n")}\n`; - appendFileSync(process.env.GITHUB_OUTPUT, contents); + appendFileSync(process.env.GITHUB_OUTPUT, contents); + } } }