-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from sparksuite/initial-implementation
Initial implementation
- Loading branch information
Showing
16 changed files
with
801 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
node_modules/ | ||
coverage/ | ||
dist/ | ||
yarn-error.log* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,122 @@ | ||
// TODO: A description | ||
const rugged = 'Hello world'; | ||
#!/usr/bin/env node | ||
|
||
export = rugged; | ||
// Imports | ||
import path from 'path'; | ||
import chalk from 'chalk'; | ||
import glob from 'glob'; | ||
import Listr from 'listr'; | ||
import execa from 'execa'; | ||
import { HandledError, yarnErrorCatcher } from './utils/errors'; | ||
import configure from './utils/configure'; | ||
import verify from './utils/verify'; | ||
import printHeader from './utils/print-header'; | ||
import installDependencies from './steps/install-dependencies'; | ||
import injectRootPackage from './steps/inject-root-package'; | ||
import testProjects from './steps/test-projects'; | ||
|
||
// Initialize finish function | ||
let finishUp = () => {}; | ||
|
||
// Initialize object to store the final result | ||
export interface FinalResult { | ||
errorEncountered: boolean; | ||
failedTests: { | ||
project: string; | ||
output: string; | ||
}[]; | ||
} | ||
|
||
const finalResult: FinalResult = { | ||
errorEncountered: false, | ||
failedTests: [], | ||
}; | ||
|
||
// Wrap everything in a self-executing async function | ||
(async () => { | ||
// Get the configuration | ||
const configuration = await configure(); | ||
|
||
// Verification | ||
const packageFile = verify.packageFile(); | ||
const absolutePath = verify.testProjects(configuration); | ||
|
||
// Determine test project paths | ||
const testProjectPaths = glob.sync(`${absolutePath}/*/`); | ||
|
||
// Define the actual finish function | ||
finishUp = async () => { | ||
// Print section header | ||
printHeader('Resetting projects'); | ||
|
||
// Remove packaged version and add linked version | ||
const tasks = new Listr( | ||
testProjectPaths.map((testProjectPath) => ({ | ||
title: path.basename(testProjectPath), | ||
task: async () => { | ||
await execa('yarn', [`remove`, packageFile.name], { | ||
cwd: testProjectPath, | ||
}).catch((error) => { | ||
if (error.toString().includes(`This module isn't specified in a package.json file`)) { | ||
return; | ||
} | ||
|
||
return yarnErrorCatcher(error); | ||
}); | ||
|
||
await execa('yarn', [`add`, `link:../..`], { | ||
cwd: testProjectPath, | ||
}).catch(yarnErrorCatcher); | ||
}, | ||
})), | ||
{ | ||
concurrent: true, | ||
exitOnError: false, | ||
} | ||
); | ||
|
||
await tasks.run(); | ||
|
||
// Loop over each failed test | ||
for (const failedTest of finalResult.failedTests) { | ||
// Print section header | ||
console.log(`\n${chalk.inverse(chalk.red(chalk.bold(` Output from: ${failedTest.project} `)))}\n`); | ||
|
||
// Print output | ||
console.log(failedTest.output.trim()); | ||
} | ||
|
||
// Final newline | ||
console.log(); | ||
|
||
// Exit accordingly | ||
if (finalResult.errorEncountered || finalResult.failedTests.length) { | ||
process.exit(1); | ||
} | ||
}; | ||
|
||
// Trigger each step | ||
await installDependencies(packageFile, testProjectPaths); | ||
await injectRootPackage(packageFile, testProjectPaths); | ||
await testProjects(testProjectPaths, finalResult); | ||
})() | ||
.catch((error) => { | ||
// Remember that we encountered an error | ||
finalResult.errorEncountered = true; | ||
|
||
// Catch already-handled errors | ||
if (error instanceof HandledError) { | ||
return; | ||
} | ||
|
||
// Handle unexpected errors | ||
console.error(chalk.italic(chalk.red('\nRugged encountered an unexpected error (see below):\n'))); | ||
console.error(chalk.red(error.stack?.trim() || error.toString()?.trim())); | ||
console.error( | ||
chalk.italic( | ||
chalk.red( | ||
`\nYou may want to report this: ${chalk.underline(`https://github.com/sparksuite/rugged/issues/new`)}\n` | ||
) | ||
) | ||
); | ||
}) | ||
.finally(() => finishUp()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Imports | ||
import execa from 'execa'; | ||
import Listr from 'listr'; | ||
import path from 'path'; | ||
import { HandledError, yarnErrorCatcher } from '../utils/errors'; | ||
import printHeader from '../utils/print-header'; | ||
import { PackageFile } from '../utils/verify'; | ||
import tmp from 'tmp'; | ||
|
||
/** Installs dependencies into the root project and test projects */ | ||
export default async function injectRootPackage(packageFile: PackageFile, testProjectPaths: string[]) { | ||
// Print section header | ||
printHeader(`Injecting ${packageFile.name}`); | ||
|
||
// Set up the tasks | ||
const tasks = new Listr([ | ||
{ | ||
title: `Compiling`, | ||
task: () => execa('yarn', ['compile']).catch(yarnErrorCatcher), | ||
}, | ||
{ | ||
title: `Packaging`, | ||
task: (ctx) => { | ||
const tmpDir = tmp.dirSync({ | ||
unsafeCleanup: true, | ||
}); | ||
|
||
ctx.packagePath = path.join(tmpDir.name, `package-${Math.random().toString(36).substring(7)}.tgz`); | ||
|
||
return execa('yarn', [`pack`, `--filename`, ctx.packagePath]).catch(yarnErrorCatcher); | ||
}, | ||
}, | ||
{ | ||
title: 'Removing linked version', | ||
task: () => | ||
new Listr( | ||
testProjectPaths.map((testProjectPath) => ({ | ||
title: path.basename(testProjectPath), | ||
task: async () => { | ||
await execa('yarn', [`remove`, packageFile.name], { | ||
cwd: testProjectPath, | ||
}).catch((error) => { | ||
if (error.toString().includes(`This module isn't specified in a package.json file`)) { | ||
return; | ||
} | ||
|
||
return yarnErrorCatcher(error); | ||
}); | ||
|
||
await execa('yarn', [`unlink`, packageFile.name], { | ||
cwd: testProjectPath, | ||
}).catch(yarnErrorCatcher); | ||
}, | ||
})), | ||
{ | ||
concurrent: true, | ||
exitOnError: false, | ||
} | ||
), | ||
}, | ||
{ | ||
title: 'Injecting packaged version', | ||
task: (ctx) => | ||
new Listr( | ||
testProjectPaths.map((testProjectPath) => ({ | ||
title: path.basename(testProjectPath), | ||
task: () => | ||
execa('yarn', [`add`, `file:${ctx.packagePath}`], { | ||
cwd: testProjectPath, | ||
}).catch(yarnErrorCatcher), | ||
})), | ||
{ | ||
concurrent: true, | ||
exitOnError: false, | ||
} | ||
), | ||
}, | ||
]); | ||
|
||
// Run the tasks, catching any errors | ||
await tasks.run().catch(() => { | ||
console.log(); | ||
throw new HandledError(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Imports | ||
import execa from 'execa'; | ||
import Listr from 'listr'; | ||
import path from 'path'; | ||
import { HandledError, yarnErrorCatcher } from '../utils/errors'; | ||
import printHeader from '../utils/print-header'; | ||
import { PackageFile } from '../utils/verify'; | ||
|
||
/** Installs dependencies into the root project and test projects */ | ||
export default async function installDependencies(packageFile: PackageFile, testProjectPaths: string[]) { | ||
// Print section header | ||
printHeader('Installing dependencies'); | ||
|
||
// Set up the tasks | ||
const tasks = new Listr( | ||
[ | ||
{ | ||
title: packageFile.name, | ||
task: () => execa('yarn', [`install`, `--frozen-lockfile`, `--prefer-offline`]).catch(yarnErrorCatcher), | ||
}, | ||
...testProjectPaths.map((testProjectPath) => ({ | ||
title: `Project: ${path.basename(testProjectPath)}`, | ||
task: () => | ||
execa('yarn', [`install`, `--frozen-lockfile`, `--prefer-offline`], { | ||
cwd: testProjectPath, | ||
}).catch(yarnErrorCatcher), | ||
})), | ||
], | ||
{ | ||
concurrent: true, | ||
exitOnError: false, | ||
} | ||
); | ||
|
||
// Run the tasks, catching any errors | ||
await tasks.run().catch(() => { | ||
console.log(); | ||
throw new HandledError(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Imports | ||
import execa from 'execa'; | ||
import Listr from 'listr'; | ||
import path from 'path'; | ||
import { FinalResult } from '..'; | ||
import { HandledError } from '../utils/errors'; | ||
import printHeader from '../utils/print-header'; | ||
|
||
/** Installs dependencies into the root project and test projects */ | ||
export default async function testProjects(testProjectPaths: string[], finalResult: FinalResult) { | ||
// Print section header | ||
printHeader('Testing projects'); | ||
|
||
// Set up the tasks | ||
const tasks = new Listr( | ||
testProjectPaths.map((testProjectPath) => ({ | ||
title: path.basename(testProjectPath), | ||
task: () => | ||
execa('yarn', [`test`], { | ||
cwd: testProjectPath, | ||
all: true, | ||
}).catch((error) => { | ||
// Add to final result | ||
finalResult.failedTests.push({ | ||
project: path.basename(testProjectPath), | ||
output: error.all ?? 'No output...', | ||
}); | ||
|
||
// Throw error that Listr will pick up | ||
throw new Error('Output will be printed below'); | ||
}), | ||
})), | ||
{ | ||
concurrent: true, | ||
exitOnError: false, | ||
} | ||
); | ||
|
||
// Run the tasks, catching any errors | ||
await tasks.run().catch(() => { | ||
console.log(); | ||
throw new HandledError(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Define what configuration looks like | ||
export interface Configuration { | ||
testProjectsDirectory: string; | ||
} | ||
|
||
/** Construct the configuration object */ | ||
export default async function configure(): Promise<Configuration> { | ||
// Return | ||
return { | ||
testProjectsDirectory: 'test-projects', | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Imports | ||
import execa from 'execa'; | ||
|
||
/** This error indicates it was expected and already handled */ | ||
export class HandledError extends Error { | ||
constructor(message?: string) { | ||
super(message); | ||
this.name = 'HandledError'; | ||
} | ||
} | ||
|
||
/** Yarn error catcher for Listr */ | ||
export const yarnErrorCatcher = (error: execa.ExecaError<string>) => { | ||
throw new Error( | ||
error.stderr | ||
.split('\n') | ||
.map((line) => line.replace(/^error /, '')) | ||
.join('\n') | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Imports | ||
import chalk from 'chalk'; | ||
|
||
/** Print a stylized section header */ | ||
export default function printHeader(header: string) { | ||
console.log(`\n${chalk.inverse(chalk.bold(` ${header} `))}\n`); | ||
} |
Oops, something went wrong.