Skip to content

Commit

Permalink
fix: Implemented theme packaging
Browse files Browse the repository at this point in the history
  • Loading branch information
seebeen committed Aug 2, 2023
1 parent e5e355d commit a7f8c2b
Show file tree
Hide file tree
Showing 20 changed files with 501 additions and 156 deletions.
1 change: 1 addition & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
]
},
"coveragePathIgnorePatterns": [
"coverage/",
"/node_modules/",
"/test/",
"/dist/",
Expand Down
15 changes: 9 additions & 6 deletions lib/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,41 @@ export async function publish(
const releaseDir = path.resolve(config.releasePath);
const assetDir = path.resolve(releaseDir, 'assets');
const versionFile = path.resolve(releaseDir, 'VERSION');
const zipCommand = process.env.ZIP_COMMAND ?? 'zip';

const packageResult = await execa(
'zip',
zipCommand,
['-qr', path.join(releaseDir, `package.zip`), config.slug],
{
reject: false,
cwd: config.releasePath,
timeout: 300 * 1000,
timeout: 30 * 1000,
},
);

if (
('exitCode' in packageResult && packageResult.exitCode !== 0) ||
('code' in packageResult && packageResult.code !== 0)
) {
console.log(packageResult);
throw getError('EZIP', packageResult.stderr);
}

if (config.withAssets) {
const zipResult = await execa(
'zip',
zipCommand,
['-qjr', path.join(releaseDir, `assets.zip`), assetDir],
{
reject: false,
cwd: assetDir,
timeout: 300 * 1000,
timeout: 30 * 1000,
},
);

if (
('exitCode' in zipResult && zipResult.exitCode !== 0) ||
('code' in zipResult && zipResult.code !== 0)
) {
throw getError('EZIP', 'Error creating the zip file');
throw getError('EZIP', zipResult.stderr);
}
}

Expand Down
138 changes: 138 additions & 0 deletions lib/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,119 @@ Your Configuration is: ${type}.`,
};
}

export function EINVALIDWITHASSETS(withAssets: string) {
return {
message: `Invalid withAssets: ${withAssets}`,
details: `The [withAssets option](${linkify(
'README.md#withassets',
)}) must be either \`true\` or \`false\`.
Your Configuration is: ${withAssets}.`,
};
}

export function EINVALIDWITHREADME(withReadme: string) {
return {
message: `Invalid withReadme: ${withReadme}`,
details: `The [withReadme option](${linkify(
'README.md#withreadme',
)}) must be either \`true\` or \`false\`.
Your Configuration is: ${withReadme}.`,
};
}

export function EINVALIDWITHVERSIONFILE(withVersionFile: string) {
return {
message: `Invalid withVersionFile: ${withVersionFile}`,
details: `The [withVersionFile option](${linkify(
'README.md#withversionfile',
)}) must be either \`true\` or \`false\`.
Your Configuration is: ${withVersionFile}.`,
};
}

export function EINVALIDPATH(path: string) {
return {
message: `Invalid path: ${path}`,

details: `The [path option](${linkify(
'README.md#path',
)}) must be a valid path.
Your Configuration is: ${path}.`,
};
}

export function EINVALIDCOPYFILES(copyFiles: string) {
return {
message: `Invalid copyFiles: ${copyFiles}`,

details: `The [copyFiles option](${linkify(
'README.md#copyfiles',
)}) must be either \`true\` or \`false\`.
Your Configuration is: ${copyFiles}.`,
};
}

export function EINVALIDRELEASEPATH(releasePath: string) {
return {
message: `Invalid releasePath: ${releasePath}`,
details: `The [releasePath option](${linkify(
'README.md#releasepath',
)}) must be a valid path.
Your Configuration is: ${releasePath}.`,
};
}

export function EINVALIDWORKDIR(workDir: string) {
return {
message: `Invalid workDir: ${workDir}`,
details: `The [workDir option](${linkify(
'README.md#workdir',
)}) must be a valid path.
Your Configuration is: ${workDir}.`,
};
}

export function EINVALIDVERSIONFILES(versionFiles: string) {
return {
message: `Invalid versionFiles: ${versionFiles}`,

details: `The [versionFiles option](${linkify(
'README.md#versionfiles',
)}) must be an array of strings.
Your Configuration is: ${versionFiles}.`,
};
}

export function EINVALIDINCLUDE(include: string) {
return {
message: `Invalid include: ${include}`,
details: `The [include option](${linkify(
'README.md#include',
)}) must be an array of globs.
Your Configuration is: ${include}.`,
};
}

export function EINVALIDEXCLUDE(exclude: string) {
return {
message: `Invalid exclude: ${exclude}`,
details: `The [exclude option](${linkify(
'README.md#exclude',
)}) must be an array of globs.
Your Configuration is: ${exclude}.`,
};
}

export function EPLUGINFILENOTFOUND(slug: string) {
return {
message: `Plugin file not found: ${slug}.php`,
Expand Down Expand Up @@ -93,3 +206,28 @@ export function EZIP(zipError: string) {
details: `The zip failed with the following error: ${zipError}.`,
};
}

export function ETHEMEFILENOTFOUND(file: string) {
return {
message: `Your theme must contain these files: ${file}`,
details: `Check if the files exist, and if not create them: ${file}.`,
};
}

export function ETHEMEFILEINVALID(file: string) {
return {
message: `Your theme file is invalid: ${file}`,
details: `The file is invalid: ${file}.`,
};
}

export function ETHEMEFILEVERSION(version: string) {
return {
message: `Theme file version is invalid: ${version}`,
details: `The [version string](${linkify(
'README.md#versioning',
)}) must be correct for the replacements to work.
Your Configuration is: ${version}.`,
};
}
5 changes: 4 additions & 1 deletion lib/utils/get-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import SemanticReleaseError from '@semantic-release/error';
import * as Errors from './errors.js';

export default function getError(
code: string,
code: keyof typeof Errors,
value: any,
): SemanticReleaseError {
if (Array.isArray(value)) {
value = value.join(', ');
}
const { message, details } = Errors[code as keyof typeof Errors](value);
return new SemanticReleaseError(message, code, details);
}
16 changes: 15 additions & 1 deletion lib/utils/verify-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ import SemanticReleaseError from '@semantic-release/error';
import { ValidationError } from 'class-validator';
import getError from './get-error.js';

declare type ErrorKeys =
| 'TYPE'
| 'WITHASSETS'
| 'WITHREADME'
| 'WITHVERSIONFILE'
| 'PATH'
| 'COPYFILES'
| 'RELEASEPATH'
| 'WORKDIR'
| 'SLUG'
| 'VERSIONFILES'
| 'INCLUDE'
| 'EXCLUDE';

export default async function verifyConfig(
pluginConfig: PluginConfig,
): Promise<SemanticReleaseError[]> {
Expand All @@ -13,7 +27,7 @@ export default async function verifyConfig(
} catch (err) {
if (Array.isArray(err)) {
return err.map((e: ValidationError) =>
getError(`EINVALID${e.property.toUpperCase()}`, e.value),
getError(`EINVALID${e.property.toUpperCase() as ErrorKeys}`, e.value),
);
}

Expand Down
42 changes: 18 additions & 24 deletions lib/utils/verify-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,33 @@ export async function verifyPlugin(config: PluginConfig): Promise<void> {
? path.resolve(config.path, config.slug)
: path.resolve('./');
const errors: SemanticReleaseError[] = [];
let pluginFileExists = true;

if (!(await fs.pathExists(path.resolve(pluginPath, `${config.slug}.php`)))) {
pluginFileExists = false;
errors.push(getError('EPLUGINFILENOTFOUND', `${config.slug}`));
throw new AggregateError([
getError('EPLUGINFILENOTFOUND', `${config.slug}`),
]);
}

if (pluginFileExists) {
try {
const pluginFile = await fs.readFile(
path.resolve(pluginPath, `${config.slug}.php`),
'utf-8',
);
try {
const pluginFile = await fs.readFile(
path.resolve(pluginPath, `${config.slug}.php`),
'utf-8',
);

if (!pluginFile.includes('Plugin Name:')) {
errors.push(getError('EPLUGINFILEINVALID', `${config.slug}`));
}
if (!pluginFile.includes('Plugin Name:')) {
errors.push(getError('EPLUGINFILEINVALID', `${config.slug}`));
}

const version = pluginFile.match(/Version:\s*(\d+\.\d+\.\d+)/);
const version = pluginFile.match(/Version:\s*(\d+\.\d+\.\d+)/);

if (version !== null && version[1] !== '0.0.0') {
errors.push(getError('EPLUGINFILEVERSION', version[1]));
}
} catch (err) {
if (err instanceof Error) {
errors.push(
new SemanticReleaseError(err.message, 'EINVALIDPLUGIN', ''),
);
}
if (version !== null && version[1] !== '0.0.0') {
errors.push(getError('EPLUGINFILEVERSION', version[1]));
}
} catch (err) {
if (err instanceof Error) {
errors.push(new SemanticReleaseError(err.message, 'EINVALIDPLUGIN', ''));
}
}

if (errors.length > 0) {
throw new AggregateError(errors);
}
}
45 changes: 45 additions & 0 deletions lib/utils/verify-theme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as path from 'node:path';
import fs from 'fs-extra';
import AggregateError from 'aggregate-error';
import SemanticReleaseError from '@semantic-release/error';
import getError from './get-error.js';
import { PluginConfig } from '../classes/plugin-config.class.js';

export async function verifyTheme(config: PluginConfig): Promise<void> {
const themePath = config.path
? path.resolve(config.path, config.slug)
: path.resolve('./');
const errors: SemanticReleaseError[] = [];

if (
!(await fs.pathExists(path.resolve(themePath, 'style.css'))) ||
!(await fs.pathExists(path.resolve(themePath, 'functions.php')))
) {
throw new AggregateError([
getError('ETHEMEFILENOTFOUND', 'style.css, functions.php'),
]);
}

try {
const styleSheet = await fs.readFile(
path.resolve(themePath, 'style.css'),
'utf-8',
);

if (!styleSheet.includes('Theme Name:')) {
errors.push(getError('ETHEMEFILEINVALID', 'style.css'));
}

const version = styleSheet.match(/Version:\s*(\d+\.\d+\.\d+)/);

if (version !== null && version[1] !== '0.0.0') {
errors.push(getError('ETHEMEFILEVERSION', version[1]));
}
} catch (err) {
if (err instanceof Error) {
errors.push(new SemanticReleaseError(err.message, 'EINVALIDTHEME', ''));
}

throw new AggregateError(errors);
}
}
2 changes: 2 additions & 0 deletions lib/verify-conditions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import AggregateError from 'aggregate-error';
import { verifyPlugin } from './utils/verify-plugin.js';
import { VerifyConditionsContext } from 'semantic-release';
import getError from './utils/get-error.js';
import { verifyTheme } from './utils/verify-theme.js';

export async function verifyConditions(
pluginConfig: PluginConfig,
Expand All @@ -15,6 +16,7 @@ export async function verifyConditions(
try {
switch (pluginConfig.type) {
case 'theme':
await verifyTheme(pluginConfig);
break;
case 'plugin':
await verifyPlugin(pluginConfig);
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
"build:production": "rimraf dist && tsc --project tsconfig.build.json",
"lint": "eslint lib/*.ts",
"test:setup": "cd ./test/fixtures/dist-test && npm install",
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest -i",
"test:watch": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --watch -i",
"test:cov": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --coverage -i",
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest -i --forceExit",
"test:watch": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --watch -i --forceExit",
"test:cov": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --coverage -i --forceExit",
"semantic-release": "semantic-release"
},
"type": "module",
Expand Down
File renamed without changes.
Loading

0 comments on commit a7f8c2b

Please sign in to comment.