Skip to content
28 changes: 0 additions & 28 deletions src/publish-beta/command.ts

This file was deleted.

14 changes: 14 additions & 0 deletions src/publish-beta/compile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// publish-beta/compile.ts

import childProcess from 'node:child_process';
import util from 'node:util';
import debug from 'debug';

const log = debug('publish-beta:compile');

const exec = util.promisify(childProcess.exec);
export default async function (directory: string): Promise<void> {
log('compile starting');
await exec('tsc --outDir dist --sourceMap true --declarationMap true', { cwd: directory });
log('compile completed');
}
68 changes: 68 additions & 0 deletions src/publish-beta/files.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// publish-beta/files.spec.ts

import { strict as assert } from 'node:assert';
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { tmpdir } from 'node:os';

import { v4 as uuid } from 'uuid';
import copyNonTSFiles, { removeNonTSFiles } from './files';

describe('copy', () => {
beforeAll(async () => {
await mkdir(path.join(tmpdir(), 'testcopy'), { recursive: true });
await mkdir(path.join(tmpdir(), 'testsrc', 'src'), { recursive: true });
});

afterAll(async () => {
await rm(path.join(tmpdir(), 'testcopy'), { recursive: true });
await rm(path.join(tmpdir(), 'testsrc'), { recursive: true });
});

it('test recursive filtering', async () => {
const rootDirectory = path.join(tmpdir(), 'testcopy', uuid());
await mkdir(rootDirectory);

const sourceDirectory = path.join(rootDirectory, 'src');
const destinationDirectory = path.join(rootDirectory, 'dist');
await mkdir(destinationDirectory);

const sourceV1 = path.join(sourceDirectory, 'api', 'v1');
const sourceV2 = path.join(sourceDirectory, 'api', 'v2');

await mkdir(sourceV1, { recursive: true });
await mkdir(sourceV2, { recursive: true });

await writeFile(path.join(sourceV1, 'test.ts'), 'test');
await writeFile(path.join(sourceV2, 'test2.ts'), 'test');
await writeFile(path.join(sourceV1, 'actiontestv1.yml'), 'actiontestv1.yml');
await writeFile(path.join(sourceV2, 'actiontestv2.yml'), 'actiontestv2.yml');
await writeFile(path.join(sourceDirectory, 'testfile.json'), 'testfile.json');
await copyNonTSFiles(sourceDirectory, destinationDirectory);

const file1 = await readFile(path.join(destinationDirectory, 'testfile.json'), 'utf8');
assert.equal(file1, 'testfile.json');
const file2 = await readFile(path.join(destinationDirectory, 'api/v1/actiontestv1.yml'), 'utf8');
assert.equal(file2, 'actiontestv1.yml');
const file3 = await readFile(path.join(destinationDirectory, 'api/v2/actiontestv2.yml'), 'utf8');
assert.equal(file3, 'actiontestv2.yml');

await assert.rejects(readFile(path.join(destinationDirectory, 'api/v1/test.ts'), 'utf8'));
await assert.rejects(readFile(path.join(destinationDirectory, 'api/v2/test2.ts'), 'utf8'));
});

it('test removal of all files except .ts (excluding .spec.ts and .test.ts)', async () => {
const sourceDirectory = path.join(tmpdir(), 'testsrc', 'src');
await Promise.all([
writeFile(path.join(sourceDirectory, 'test.ts'), 'test'),
writeFile(path.join(sourceDirectory, 'test.spec.ts'), 'test'),
writeFile(path.join(sourceDirectory, 'swagger.yml'), 'test'),
]);
await removeNonTSFiles(sourceDirectory);

const files = await readdir(sourceDirectory, { withFileTypes: true });
assert.equal(files.length, 2);
assert.ok(files.some((item) => item.name === 'test.ts'));
assert.ok(files.some((item) => item.name === 'swagger.yml'));
});
});
37 changes: 37 additions & 0 deletions src/publish-beta/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// publish-beta/files.ts
import path from 'node:path';
import fs from 'node:fs/promises';

export default async function copyNonTSFiles(sourceDirectory: string, destinationDirectory: string): Promise<void> {
const files = await fs.readdir(sourceDirectory, { withFileTypes: true });
await Promise.all(
files.map(async (item) => {
const sourceItem = path.join(sourceDirectory, item.name);
const destinationItem = path.join(destinationDirectory, item.name);
if (item.isDirectory()) {
await fs.mkdir(destinationItem, { recursive: true });
await copyNonTSFiles(sourceItem, path.join(destinationDirectory, item.name));
return;
}
if (!item.name.endsWith('.ts')) {
await fs.copyFile(sourceItem, destinationItem);
}
})
);
}

export async function removeNonTSFiles(sourceDirectory: string): Promise<void> {
const files = await fs.readdir(sourceDirectory, { withFileTypes: true });
await Promise.all(
files.map(async (item) => {
const sourceItem = path.join(sourceDirectory, item.name);
if (item.isDirectory()) {
await removeNonTSFiles(sourceItem);
return;
}
if (item.name.endsWith('.ts') && (item.name.endsWith('.spec.ts') || item.name.endsWith('.test.ts'))) {
await fs.rm(sourceItem);
}
})
);
}
2 changes: 1 addition & 1 deletion src/publish-beta/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface GithubConfigurationResponse {
number: number;
repo: string;
}
const log = debug('action:github');
const log = debug('publish-beta:github');

export function getPRNumber(): string {
const prNumberSearch = process.env['GITHUB_REF']?.match(/[0-9]+/gu);
Expand Down
27 changes: 23 additions & 4 deletions src/publish-beta/index.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,36 @@
// publish-beta/index.ts

import process from 'node:process';
import path from 'node:path';
import { debug } from 'debug';

import { publishComment } from './github';
import { packageJSONUpdate } from './package';
import copyNonTSFiles from './files';
import compile from './compile';
import publish from './publish';

const log = debug('publish-beta');
export async function main(): Promise<void | boolean> {
log('Action start');

await compile(process.cwd());
const packageNameAndBetaVersion = await packageJSONUpdate(process.cwd());
await copyNonTSFiles(path.join(process.cwd(), 'src'), path.join(process.cwd(), 'dist'));
await publish(process.cwd());
await publishComment(packageNameAndBetaVersion);
}

import command from './command';
command()
main()
.then(() => {
process.stdin.destroy();
// eslint-disable-next-line unicorn/no-process-exit
process.exit(0);
})
// eslint-disable-next-line unicorn/prefer-top-level-await
.catch(() => {
.catch((error) => {
// eslint-disable-next-line no-console
console.log('Action Error - exit 1');
console.log('Action Error - exit 1 - error:', error);
// eslint-disable-next-line unicorn/no-process-exit
process.exit(1);
});
30 changes: 20 additions & 10 deletions src/publish-beta/package.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import path from 'node:path';
import { tmpdir } from 'node:os';
import process from 'node:process';

import { generatePackageBetaTag, isPackageAnAPI, packageJSONUpdate } from './package';
import { generatePackageBetaTag, packageJSONUpdate } from './package';

describe('package', () => {
beforeAll(async () => {
await mkdir(path.join(tmpdir(), 'packageUpdate'), { recursive: true });
await mkdir(path.join(tmpdir(), 'packageUpdate2'), { recursive: true });
await mkdir(path.join(tmpdir(), 'hasAPI', 'src/api'), { recursive: true });
await mkdir(path.join(tmpdir(), 'noAPI', 'src/'), { recursive: true });
});

afterAll(async () => {
await rm(path.join(tmpdir(), 'packageUpdate'), { recursive: true });
await rm(path.join(tmpdir(), 'packageUpdate2'), { recursive: true });
await rm(path.join(tmpdir(), 'hasAPI'), { recursive: true });
await rm(path.join(tmpdir(), 'noAPI'), { recursive: true });
});
Expand All @@ -27,22 +29,30 @@ describe('package', () => {
assert.ok(packageBetaTag.startsWith('2406-'));
});

it('hasAPI', async () => {
assert.equal(await isPackageAnAPI(path.join(tmpdir(), 'hasAPI')), true);
});
it('noAPI', async () => {
assert.equal(await isPackageAnAPI(path.join(tmpdir(), 'noAPI')), false);
});

it('packageJSONUpdate', async () => {
it('test packageJSON Update and add /src/ to files', async () => {
const filePath = path.join(tmpdir(), 'packageUpdate/package.json');
process.env['GITHUB_REF'] = '/ref/87/branch';
await writeFile(
path.join(tmpdir(), 'packageUpdate/package.json'),
JSON.stringify({ name: 'testpackage', version: '1.2.10' })
JSON.stringify({ name: 'testpackage', version: '1.2.10', files: ['/dist/'] })
);
await mkdir(path.join(tmpdir(), 'packageUpdate/src'), { recursive: true });

await packageJSONUpdate(path.join(tmpdir(), 'packageUpdate'));
const rawUpdatedFile = await readFile(filePath, 'utf8');
assert.ok(JSON.parse(rawUpdatedFile).version.startsWith('1.2.10-beta.87-'));
assert.deepEqual(JSON.parse(rawUpdatedFile).files.sort(), ['/dist/', '/src/'].sort());
});

it('Test with files property missing', async () => {
process.env['GITHUB_REF'] = '/ref/87/branch';
await writeFile(
path.join(tmpdir(), 'packageUpdate2/package.json'),
JSON.stringify({ name: 'testpackage', version: '1.2.10' })
);
await assert.rejects(
packageJSONUpdate(path.join(tmpdir(), 'packageUpdate2')),
'[Error: package.json does not have a files: [] property]'
);
});
});
51 changes: 31 additions & 20 deletions src/publish-beta/package.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
// publish-beta/package.ts

import path from 'node:path';
import { readFile, stat, writeFile } from 'node:fs/promises';
import { readFile, writeFile } from 'node:fs/promises';
import { debug } from 'debug';

import shortId from './short-id';
import { getPRNumber } from './github';
import { removeNonTSFiles } from './files';

const log = debug('action:package');

export async function isPackageAnAPI(rootProjectDirectory: string): Promise<boolean> {
try {
await stat(path.join(rootProjectDirectory, 'src/api'));
log('isAPI:true');
// eslint-disable-next-line no-console
console.log('::set-output name=IS_API::true');
return true;
} catch {
log('isAPI:false');
// eslint-disable-next-line no-console
console.log('::set-output name=IS_API::false');
}
return false;
const log = debug('publish-beta:package');

interface PackageJSON {
name: string;
version: string;
files: string[];
}

export function generatePackageBetaTag(): string {
Expand All @@ -30,14 +22,33 @@ export function generatePackageBetaTag(): string {
return `${prNumber}-${id}`;
}

export async function packageJSONUpdate(rootProjectDirectory: string): Promise<void> {
function checkFilesPropertyExists(packageJSON: string): void {
const packageJSONObject = JSON.parse(packageJSON) as { files?: string[] };
if (!packageJSONObject.files) {
throw new Error('package.json does not have a files: [] property');
}
}

function addSourceToFilesProperty(input: PackageJSON): string[] {
if (!input.files.includes('/src/')) {
return [...input.files, '/src/'];
}
return input.files;
}

export async function packageJSONUpdate(rootProjectDirectory: string): Promise<string> {
const packageJSONPath = path.join(rootProjectDirectory, 'package.json');
const readPackageJson = await readFile(packageJSONPath, 'utf8');
const packageJson = JSON.parse(readPackageJson) as { version: string; name: string };
checkFilesPropertyExists(readPackageJson);
const packageJson = JSON.parse(readPackageJson) as PackageJSON;

await removeNonTSFiles(path.join(rootProjectDirectory, 'src'));

const files = addSourceToFilesProperty(packageJson);
const newVersion = `${packageJson.version}-beta.${generatePackageBetaTag()}`;
packageJson.version = newVersion;
packageJson.files = files;
await writeFile(packageJSONPath, JSON.stringify(packageJson));
log(`Updated package.json - new version is: ${packageJson.name}@${newVersion}`);
// eslint-disable-next-line no-console
console.log(`::set-output name=NEW_VERSION::${packageJson.name}@${newVersion}`);
return `${packageJson.name}@${newVersion}`;
}
14 changes: 14 additions & 0 deletions src/publish-beta/publish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// publish-beta/publish.ts

import childProcess from 'node:child_process';
import util from 'node:util';
import debug from 'debug';

const log = debug('publish-beta:publish');

const exec = util.promisify(childProcess.exec);
export default async function (directory: string): Promise<void> {
log('publish starting');
await exec('npm publish --tag beta', { cwd: directory });
log('publish completed');
}