Skip to content

Commit

Permalink
refactor pre-commit code into separate files (and add --*=false optio…
Browse files Browse the repository at this point in the history
  • Loading branch information
zepumph committed Dec 17, 2024
1 parent f5bd432 commit c48159b
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 184 deletions.
60 changes: 60 additions & 0 deletions js/common/pre-commit/getPreCommitTasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2022-2024, University of Colorado Boulder

// @author Michael Kauzmann (PhET Interactive Simulations)

import buildLocal from '../../../../perennial-alias/js/common/buildLocal.js';
import getOption, { isOptionKeyProvided } from '../../../../perennial-alias/js/grunt/tasks/util/getOption.js';

const SUPPORTED_TASKS = [ 'lint', 'report-media', 'type-check', 'unit-test', 'phet-io-api' ];

/**
* Parse options and build-local defaults to get the list of tasks to be run for pre-commit hooks.
* @author Michael Kauzmann (PhET Interactive Simulations)
* @author Sam Reid (PhET Interactive Simulations)
*
*/
const getPreCommitTasks = ( outputToConsole: boolean ): string[] => {


// By default, run all tasks
const OPT_OUT_ALL = '*'; // Key to opt out of all tasks
let tasksToRun = isOptionKeyProvided( OPT_OUT_ALL ) && !getOption( OPT_OUT_ALL ) ? [] : [ ...SUPPORTED_TASKS ];

// check local preferences for overrides for which tasks to turn 'off'
const hookPreCommit = buildLocal.hookPreCommit;
if ( hookPreCommit && hookPreCommit[ OPT_OUT_ALL ] === false ) {
outputToConsole && console.log( 'all tasks opted out from build-local.json' );
tasksToRun.length = 0;
}

SUPPORTED_TASKS.forEach( ( task: string ) => {

// process the buildLocal preferences first
if ( hookPreCommit && hookPreCommit[ task ] === false ) {
outputToConsole && console.log( 'task opted out from build-local.json:', task );
tasksToRun = tasksToRun.filter( t => t !== task );
}

// process the CLI overrides
if ( isOptionKeyProvided( task ) ) {
if ( getOption( task ) ) {
if ( !tasksToRun.includes( task ) ) {
outputToConsole && console.log( 'task added from CLI:', task );
tasksToRun.push( task );
}
}
else {
outputToConsole && console.log( 'task removed from CLI:', task );
tasksToRun = tasksToRun.filter( t => t !== task );
}
}
} );

if ( getOption( 'allTasks' ) ) {
outputToConsole && console.log( 'forcing all tasks to run' );
tasksToRun = [ ...SUPPORTED_TASKS ];
}
return tasksToRun;
};

export default getPreCommitTasks;
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
// Copyright 2020-2024, University of Colorado Boulder

/**
* See grunt/tasks/pre-commit.ts. This implements each task for that process so they can run in parallel.
* See grunt/tasks/pre-commit.ts. This implements each task for that process so they can run in parallel. This is run
* as a script, and not as a module.
*
* @author Sam Reid (PhET Interactive Simulations)
* @author Michael Kauzmann (PhET Interactive Simulations)
*/

import fs from 'fs';
import CacheLayer from '../../../chipper/js/common/CacheLayer.js';
import reportMedia from '../../../chipper/js/grunt/reportMedia.js';
import execute from '../../../perennial-alias/js/common/execute.js';
import getRepoList from '../../../perennial-alias/js/common/getRepoList.js';
import npmCommand from '../../../perennial-alias/js/common/npmCommand.js';
import withServer from '../../../perennial-alias/js/common/withServer.js';
import lint from '../../../perennial-alias/js/eslint/lint.js';
import typeCheck from '../../../perennial-alias/js/grunt/typeCheck.js';
import puppeteer from '../../../perennial-alias/js/npm-dependencies/puppeteer.js';
import puppeteerQUnit from '../../../perennial-alias/js/test/puppeteerQUnit.js';
import transpile from '../common/transpile.js';
import getPhetLibs from '../grunt/getPhetLibs.js';
import generatePhetioMacroAPI from '../phet-io/generatePhetioMacroAPI.js';
import phetioCompareAPISets from '../phet-io/phetioCompareAPISets.js';
import execute from '../../../../perennial-alias/js/common/execute.js';
import getRepoList from '../../../../perennial-alias/js/common/getRepoList.js';
import npmCommand from '../../../../perennial-alias/js/common/npmCommand.js';
import withServer from '../../../../perennial-alias/js/common/withServer.js';
import lint from '../../../../perennial-alias/js/eslint/lint.js';
import typeCheck from '../../../../perennial-alias/js/grunt/typeCheck.js';
import puppeteer from '../../../../perennial-alias/js/npm-dependencies/puppeteer.js';
import puppeteerQUnit from '../../../../perennial-alias/js/test/puppeteerQUnit.js';
import getPhetLibs from '../../grunt/getPhetLibs.js';
import reportMedia from '../../grunt/reportMedia.js';
import generatePhetioMacroAPI from '../../phet-io/generatePhetioMacroAPI.js';
import phetioCompareAPISets from '../../phet-io/phetioCompareAPISets.js';
import CacheLayer from '../CacheLayer.js';
import transpile from '../transpile.js';

type Repo = string;

Expand Down
92 changes: 92 additions & 0 deletions js/common/pre-commit/preCommitMain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2020-2024, University of Colorado Boulder


import assert from 'assert';
import execute from '../../../../perennial-alias/js/common/execute.js';
import phetTimingLog from '../../../../perennial-alias/js/common/phetTimingLog.js';
import tsxCommand from '../../../../perennial-alias/js/common/tsxCommand.js';
import getOption from '../../../../perennial-alias/js/grunt/tasks/util/getOption.js';
import getPreCommitTasks from './getPreCommitTasks.js';

/**
* Main logic of pre-commit responsible for launching pre-commit tasks in parallel for a given repo
*
* @author Sam Reid (PhET Interactive Simulations)
* @author Michael Kauzmann (PhET Interactive Simulations)
*/

export async function preCommitMain( repo: string, outputToConsole: boolean ): Promise<void> {
const absolute = getOption( 'absolute' ); // Output paths that WebStorm External Tools can parse and hyperlink

const tasksToRun = getPreCommitTasks( outputToConsole );

outputToConsole && console.log( 'tasks to run:', tasksToRun );

const precommitSuccess = await phetTimingLog.startAsync( `pre-commit repo="${repo}"`, async () => {

outputToConsole && console.log( 'repo:', repo );

const taskResults = await Promise.allSettled(
tasksToRun.map( task => {
return phetTimingLog.startAsync(
task,
async () => {
const results = await execute(
tsxCommand,
[
'../chipper/js/common/pre-commit/pre-commit-task.ts',
`--command=${task}`,
`--repo=${repo}`,
outputToConsole ? '--console' : '',
absolute ? '--absolute' : ''
],
'../chipper',
{
errors: 'resolve'
}
);
assert( typeof results !== 'string' );
results.stdout && results.stdout.trim().length > 0 && console.log( results.stdout );
results.stderr && results.stderr.trim().length > 0 && console.log( results.stderr );

if ( results.code === 0 ) {
return { task: task, success: true };
}
else {
let message = 'Task failed: ' + task;
if ( results.stdout && results.stdout.trim().length > 0 ) {
message = message + ': ' + results.stdout;
}
if ( results.stderr && results.stderr.trim().length > 0 ) {
message = message + ': ' + results.stderr;
}
return { task: task, success: false, message: message };
}
},
{
depth: 1
}
);
} )
);

taskResults.forEach( result => {
if ( result.status === 'fulfilled' ) {
if ( result.value.success ) {
console.log( `Task ${result.value.task} succeeded` );
}
else {
console.error( result.value.message );
}
}
else {
console.error( `Task ${result.reason.task} encountered an error: ${result.reason.message}` );
}
} );

return taskResults.every( result => result.status === 'fulfilled' && result.value.success );
} );

// generatePhetioMacroAPI is preventing exit for unknown reasons, so manually exit here
phetTimingLog.close( () => process.exit( precommitSuccess ? 0 : 1 ) );
}
54 changes: 54 additions & 0 deletions js/common/pre-commit/preCommitOnRepos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2022-2024, University of Colorado Boulder

import path from 'path';
import { Repo } from '../../../../perennial-alias/js/browser-and-node/PerennialTypes.js';
import dirname from '../../../../perennial-alias/js/common/dirname.js';
import execute from '../../../../perennial-alias/js/common/execute.js';
import tsxCommand from '../../../../perennial-alias/js/common/tsxCommand.js';

/**
* Spawns the same pre-commit process on each repo in the list
* @author Michael Kauzmann (PhET Interactive Simulations)
* @author Sam Reid (PhET Interactive Simulations)
*/
export default async function preCommitOnRepos( repos: Repo[], outputToConsole: boolean ): Promise<boolean> {
const startTime = Date.now();

let success = true;

// This is done sequentially so we don't spawn a bunch of uncached type-check at once, but in the future we may want to optimize
// to run one sequentially then the rest in parallel
for ( let i = 0; i < repos.length; i++ ) {

process.stdout.write( repos[ i ] + ': ' );

// get all argv, but drop out --all and --changed
const args = process.argv.slice( 2 ).filter( arg => ![ '--all', '--changed' ].includes( arg ) );
outputToConsole && console.log( 'spawning pre-commit.ts with args:', args );

// @ts-expect-error
const __dirname = dirname( import.meta.url );

// get the cwd to the repo, ../../../../repo
const cwd = path.resolve( __dirname, '../../../../', repos[ i ] );

const result = await execute( tsxCommand, [ '../chipper/js/grunt/tasks/pre-commit.ts', ...args ], cwd, {

// resolve errors so Promise.all doesn't fail on first repo that cannot pull/rebase
errors: 'resolve'
} );
outputToConsole && console.log( 'result:', result );
if ( result.code === 0 ) {
console.log( 'Success' );
}
else {
console.log();
result.stdout.trim().length > 0 && console.log( result.stdout.trim() );
result.stderr.trim().length > 0 && console.log( result.stderr.trim() );
success = false;
}
}

console.log( 'Done in ' + ( Date.now() - startTime ) + 'ms' );
return success;
}
Loading

0 comments on commit c48159b

Please sign in to comment.