Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/model/output.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
import { afterEach, describe, expect, it } from 'bun:test';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { Output } from './output.ts';

const originalGitHubOutput = process.env['GITHUB_OUTPUT'];

let directory: string | undefined;

afterEach(() => {
if (originalGitHubOutput === undefined) {
delete process.env['GITHUB_OUTPUT'];
} else {
process.env['GITHUB_OUTPUT'] = originalGitHubOutput;
}

if (directory) {
rmSync(directory, { recursive: true, force: true });
directory = undefined;
}
});

describe('Output', () => {
describe('setBuildVersion', () => {
it('does not throw', () => {
expect(() => Output.setBuildVersion('1.0.0')).not.toThrow();
});

// The end of the chain unity-builder reads: `game-ci build` publishes a
// version, and a wrapping action gets it out of $GITHUB_OUTPUT.
it('reaches $GITHUB_OUTPUT', () => {
directory = mkdtempSync(join(tmpdir(), 'game-ci-outputs-'));
const filePath = join(directory, 'outputs');
writeFileSync(filePath, '');
process.env['GITHUB_OUTPUT'] = filePath;

Output.setBuildVersion('1.166.0');
Output.setAndroidVersionCode('1166000');

const written = readFileSync(filePath, 'utf8');
expect(written).toContain('buildVersion<<');
expect(written).toContain('1.166.0');
expect(written).toContain('androidVersionCode<<');
expect(written).toContain('1166000');
});
});
});
84 changes: 84 additions & 0 deletions src/module/actions/core.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { afterEach, describe, expect, it } from 'bun:test';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { EOL, tmpdir } from 'node:os';
import { join } from 'node:path';
import { core } from './index.ts';

const originalGitHubOutput = process.env['GITHUB_OUTPUT'];
const originalConsoleLog = console.log;

let directory: string | undefined;

/** An empty $GITHUB_OUTPUT file, as a runner would hand one to a step. */
function useOutputFile(): string {
directory = mkdtempSync(join(tmpdir(), 'game-ci-outputs-'));
const filePath = join(directory, 'outputs');
writeFileSync(filePath, '');
process.env['GITHUB_OUTPUT'] = filePath;
return filePath;
}

afterEach(() => {
if (originalGitHubOutput === undefined) {
delete process.env['GITHUB_OUTPUT'];
} else {
process.env['GITHUB_OUTPUT'] = originalGitHubOutput;
}

console.log = originalConsoleLog;

if (directory) {
rmSync(directory, { recursive: true, force: true });
directory = undefined;
}
});

describe('core.setOutput', () => {
// The regression unity-builder v6 shipped with: the CLI printed a version
// and never wrote the file, so buildVersion/androidVersionCode arrived
// empty on every `unity-builder` run that used the CLI.
it('writes a real record to $GITHUB_OUTPUT when a runner provides one', () => {
const filePath = useOutputFile();

core.setOutput('buildVersion', '1.166.0');

const written = readFileSync(filePath, 'utf8');
expect(written).toMatch(
/^buildVersion<<ghadelimiter_[\w-]+\r?\n1\.166\.0\r?\nghadelimiter_[\w-]+\r?\n$/,
);
});

// `key=value` would truncate at the first newline, so the delimiter form is
// load-bearing rather than cosmetic.
it('round-trips a value containing newlines', () => {
const filePath = useOutputFile();

core.setOutput('buildVersion', `1.166.0${EOL}second-line`);

expect(readFileSync(filePath, 'utf8')).toContain(`1.166.0${EOL}second-line`);
});

it('appends instead of overwriting, so several outputs survive', () => {
const filePath = useOutputFile();

core.setOutput('buildVersion', '1.166.0');
core.setOutput('androidVersionCode', '1166000');

const written = readFileSync(filePath, 'utf8');
expect(written).toContain('buildVersion<<');
expect(written).toContain('androidVersionCode<<');
expect(written).toContain('1166000');
});

it('falls back to printing when there is no $GITHUB_OUTPUT', () => {
delete process.env['GITHUB_OUTPUT'];
const printed: string[] = [];
console.log = (message?: unknown) => {
printed.push(String(message));
};

core.setOutput('buildVersion', '1.166.0');

expect(printed).toEqual(['(mock) Output "buildVersion" is set to "1.166.0"']);
});
});
18 changes: 18 additions & 0 deletions src/module/actions/core.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* eslint-disable no-console */

import { appendFileSync } from 'node:fs';
import { randomUUID } from 'node:crypto';
import { EOL } from 'node:os';

// Adapted from: https://github.com/actions/toolkit/blob/9b7bcb1567c9b7f134eb3c2d6bbf409a5106a956/packages/core/src/core.ts#L13
/**
* Interface for getInput options
Expand All @@ -22,6 +26,20 @@ export const core = {
},

setOutput: (key: string, value: string) => {
// Same split as the real @actions/core: write the file GitHub hands us,
// and only fall back to stdout when there isn't one. Printing was
// previously the only branch, so this never reached $GITHUB_OUTPUT at all
// - which silently dropped the buildVersion/androidVersionCode that
// `game-ci build` publishes for unity-builder to consume.
const filePath = process.env['GITHUB_OUTPUT'];
if (filePath) {
// The delimiter form, not `key=value`: the value is user-supplied (a
// version string), and `key=value` cannot carry a newline.
const delimiter = `ghadelimiter_${randomUUID()}`;
appendFileSync(filePath, `${key}<<${delimiter}${EOL}${value}${EOL}${delimiter}${EOL}`);
return;
}

console.log(`(mock) Output "${key}" is set to "${value}"`);
},

Expand Down
Loading