/* eslint-disable no-console */ import { copy, exists, remove, mkdirp, readdir, mkdtemp } from 'fs-extra'; import { join, resolve, basename } from 'path'; import { tmpdir } from 'os'; import { chdir, cwd } from 'process'; import { execCommand } from '@joplin/utils'; import { glob } from 'glob'; import readRepositoryJson from './utils/readRepositoryJson'; import waitForCliInput from './utils/waitForCliInput'; import getPathToPatchFileFor from './utils/getPathToPatchFileFor'; type BeforeEachInstallCallback = (buildDir: string, pluginName: string)=> Promise; const buildDefaultPlugins = async (outputParentDir: string|null, beforeInstall: BeforeEachInstallCallback) => { const pluginSourcesDir = resolve(join(__dirname, 'plugin-sources')); const pluginRepositoryData = await readRepositoryJson(join(__dirname, 'pluginRepositories.json')); const originalDirectory = cwd(); const logStatus = (...message: string[]) => { const blue = '\x1b[96m'; const reset = '\x1b[0m'; console.log(blue, ...message, reset); }; for (const pluginId in pluginRepositoryData) { const repositoryData = pluginRepositoryData[pluginId]; const buildDir = await mkdtemp(join(tmpdir(), 'default-plugin-build')); try { logStatus('Building plugin', pluginId, 'at', buildDir); const pluginDir = resolve(join(pluginSourcesDir, pluginId)); // Clone the repository if not done yet if (!(await exists(pluginDir)) || (await readdir(pluginDir)).length === 0) { logStatus(`Cloning from repository ${repositoryData.cloneUrl}`); await execCommand(['git', 'clone', '--', repositoryData.cloneUrl, pluginDir]); chdir(pluginDir); } chdir(pluginDir); const currentCommitHash = (await execCommand(['git', 'rev-parse', 'HEAD~'])).trim(); const expectedCommitHash = repositoryData.commit; if (currentCommitHash !== expectedCommitHash) { logStatus(`Switching to commit ${expectedCommitHash}`); await execCommand(['git', 'switch', repositoryData.branch]); try { await execCommand(['git', 'checkout', expectedCommitHash]); } catch (error) { logStatus(`git checkout failed with error ${error}. Fetching...`); await execCommand(['git', 'fetch']); await execCommand(['git', 'checkout', expectedCommitHash]); } } logStatus('Copying repository files...'); await copy(pluginDir, buildDir, { filter: fileName => { return basename(fileName) !== '.git'; }, }); chdir(buildDir); logStatus('Initializing repository.'); await execCommand('git init . -b main'); logStatus('Creating initial commit.'); await execCommand('git add .'); await execCommand(['git', 'config', 'user.name', 'Build script']); await execCommand(['git', 'config', 'user.email', '']); await execCommand(['git', 'commit', '-m', 'Initial commit']); const patchFile = getPathToPatchFileFor(pluginId); if (await exists(patchFile)) { logStatus('Applying patch.'); await execCommand(['git', 'apply', patchFile]); } await beforeInstall(buildDir, pluginId); logStatus('Installing dependencies.'); await execCommand('npm install'); const jplFiles = await glob('publish/*.jpl'); logStatus(`Found built .jpl files: ${JSON.stringify(jplFiles)}`); if (jplFiles.length === 0) { throw new Error(`No published files found in ${buildDir}/publish`); } if (outputParentDir !== null) { logStatus(`Checking output directory in ${outputParentDir}`); const outputDirectory = join(outputParentDir, pluginId); if (await exists(outputDirectory)) { await remove(outputDirectory); } await mkdirp(outputDirectory); const sourceFile = jplFiles[0]; const destFile = join(outputDirectory, 'plugin.jpl'); logStatus(`Copying built file from ${sourceFile} to ${destFile}`); await copy(sourceFile, destFile); } else { console.warn('No output directory specified. Not copying built .jpl files.'); } } catch (error) { console.error(error); console.log('Build directory', buildDir); await waitForCliInput(); throw error; } finally { chdir(originalDirectory); await remove(buildDir); logStatus('Removed build directory'); } } }; export default buildDefaultPlugins;