Last active
October 22, 2015 03:28
-
-
Save dmsnell/23b1189b33106babbb68 to your computer and use it in GitHub Desktop.
Generate linter args from SVN repository
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
#!/usr/local/bin/php | |
<?php | |
/** | |
* Generates a structured list of files related to an | |
* SVN commit or working directory, or generates the | |
* diff between a particular SVN commit and its parent | |
* or between the working directory and the base revision. | |
* | |
* Calling: | |
* ./generate-linter-args.php [-rREVISION_NUMBER] --type ['changeset' or 'diff'] [file list] | |
* | |
* Get current changes: | |
* ./generate-linter-args.php --type changeset | |
* ./generate-linter-args.php --type diff | |
* | |
* Get changes for specific revision: | |
* ./generate-linter-args.php -r12345 --type changeset | |
* ./generate-linter-args.php -r12345 --type diff | |
* | |
* Get changes for specific files in working directory | |
* ./generate-linter-args.php --type changeset "wp-content/lib/class.splines.php" "wp-content/lib/class.reticulator.php" | |
* ./generate-linter-args.php --type diff "wp-content/lib/class.splines.php" "wp-content/lib/class.reticulator.php" | |
*/ | |
/** | |
* Will return an item if it's a member of an array | |
* or `null` if it doesn't exist within that array. | |
* | |
* @param Array $array Containing array | |
* @param mixed $member Key to check for existence in the array | |
* @return bool Whether or not $member is in $array | |
*/ | |
function maybeMember( Array $array, $member ) { | |
return isset( $array[ $member ] ) ? $array[ $member ] : null; | |
} | |
/** | |
* Returns the shell arguments assuming they are what | |
* they should be. Does not perform input validation. | |
* | |
* @return Array [ type of info requested, revision or null, list of filenames or blank ] | |
*/ | |
function getShellArgs() { | |
global $argv; | |
$options = getopt( 'r::', [ 'type:' ] ); | |
// Which parameter to create - required | |
$type = maybeMember( $options, 'type' ); | |
// Revision is either given or "current" | |
$revision = (int) maybeMember( $options, 'r' ); | |
// Get the remaining args after the script name and params | |
// Should be a space-separated list of filenames | |
$filenames = getRealFiles( array_slice( $argv, 1 ) ); | |
return [ $type, $revision, $filenames ]; | |
} | |
/** | |
* Returns only files that exist. | |
* Filters out special names that might also | |
* be svn commands coming through. | |
* | |
* @param Array $names List of potential filenames | |
* @return Array List of filenames for files that exist | |
*/ | |
function getRealFiles( $names ) { | |
$isIrrelevant = function( $name ) { | |
return ! array_key_exists ( $name, | |
[ | |
'commit' => true, | |
'ci' => true | |
] ); | |
}; | |
$files = $names; | |
$files = array_filter( $files, $isIrrelevant ); | |
$files = array_filter( $files, 'file_exists' ); | |
return $files; | |
} | |
/** | |
* Returns only lines that start with the specified character. | |
* | |
* @param string $type The letter describing which svn operation is desired | |
* @return Closure produces a bool output for the passed line if it starts with $type | |
*/ | |
function typeFilter( $type ) { | |
return function( $line ) use ( $type ) { | |
return $type === substr( $line, 0, 1 ); | |
}; | |
} | |
/** | |
* Converts the `svn status` output line into a filename | |
* | |
* @param string $line one line of output from `svn status` | |
* @return string Trimmed version of $line without the status info prefix | |
*/ | |
function statusLineToFilename( $line ) { | |
return trim( substr( $line, 7 ) ); | |
} | |
/** | |
* Returns an array of files of a specified type | |
* | |
* @param Array $lines lines of output from `svn status` | |
* @param string $type Which type of `svn` operation to return | |
* @return Array Filenames for files that exist and match the $type predicate | |
*/ | |
function filesByType( $lines, $type ) { | |
return array_values( array_map( 'statusLineToFilename', array_filter( $lines, typeFilter( $type ) ) ) ); | |
} | |
/** | |
* Splits the output of `svn status` into a structured | |
* object with added, modified, missing, and deleted files | |
* as arrays. | |
* | |
* @param Array $lines output lines from `svn status` | |
* @return stdClass Structured view of changed files | |
*/ | |
function statusLinesToChangeSet( $lines ) { | |
return (object) [ | |
"addedFiles" => array_merge( | |
filesByType( $lines, 'A' ), | |
filesByType( $lines, 'R' ) | |
), | |
"deletedFiles" => filesByType( $lines, 'D' ), | |
"missingFiles" => filesByType( $lines, '!' ), | |
"modifiedFiles" => filesByType( $lines, 'M' ) | |
]; | |
} | |
/** | |
* Computes the changeset between two revisions | |
* | |
* @param int $revision Specific revision to examine | |
* @return Array output from svn command | |
*/ | |
function statusAcrossRevisions( $revision ) { | |
if ( empty( $revision ) ) { | |
$revision = getCurrentRevision(); | |
} | |
exec( sprintf( | |
'svn diff -c%d --summarize', $revision | |
), $output, $exitStatus ); | |
if ( 0 !== $exitStatus ) { | |
exit( $exitStatus ); | |
} | |
return $output; | |
} | |
/** | |
* Computes the changeset assuming we are basing the | |
* comparison off of the local working copy. | |
* | |
* @param Array $filenames possible list of filenames to only examine | |
* @return Array output from svn command | |
*/ | |
function statusForWorkingCopy( $filenames ) { | |
exec( sprintf( 'svn status %s 2>/dev/null', | |
implode( ' ', array_map( 'escapeshellarg', $filenames ) ) | |
), $output, $exitStatus ); | |
if ( 0 !== $exitStatus ) { | |
exit( $exitStatus ); | |
} | |
return $output; | |
} | |
/** | |
* Returns the current revision number for the checked-out | |
* copy of the svn repository. | |
* | |
* @return int Revision of current local working svn repository | |
*/ | |
function getCurrentRevision() { | |
exec( 'svn info | grep "Revision" | awk \'{print $2}\'', $raw_revision ); | |
return (int) array_shift( $raw_revision ); | |
} | |
/** | |
* Returns the output of `svn diff` across revisions | |
* | |
* @param int $revision A specific svn revision to examine | |
* @return Array output from svn command | |
*/ | |
function createRevisionDiff( $revision ) { | |
if ( empty( $revision ) ) { | |
$revision = getCurrentRevision(); | |
} | |
exec( sprintf( | |
'svn diff -c%d 2>/dev/null', $revision | |
), $output, $exitStatus ); | |
if ( 0 !== $exitStatus ) { | |
exit( $exitStatus ); | |
} | |
return $output; | |
} | |
/** | |
* Returns the output of `svn diff` for the working copy | |
* | |
* @param Array $filenames List of filenames to only examine | |
* @return Array output from svn command | |
*/ | |
function createWorkingCopyDiff( $filenames ) { | |
exec( sprintf( 'svn diff %s 2>/dev/null', | |
implode( ' ', array_map( 'escapeshellarg', $filenames ) ) | |
), $output, $exitStatus ); | |
if ( 0 !== $exitStatus ) { | |
exit( $exitStatus ); | |
} | |
return $output; | |
} | |
/** | |
* Returns the desired `svn` diff | |
* | |
* @param int $revision A specific revision to examine | |
* @param Array $filenames Specific list of files to examine only | |
* @return string Newline-terminated list of output lines for `svn diff` | |
*/ | |
function createDiff( $revision, $filenames ) { | |
return implode( PHP_EOL, | |
empty( $revision ) | |
? createWorkingCopyDiff( $filenames ) | |
: createRevisionDiff( $revision ) | |
); | |
} | |
/** | |
* Computes the desired changeset | |
* | |
* @param int $revision A specific revision to examine | |
* @param Array $filenames Specific list of files to examine only | |
* @return string JSON-encoded structured list of changed files for commit | |
*/ | |
function listRelevantFiles( $revision, $filenames ) { | |
return json_encode( statusLinesToChangeSet( | |
empty( $revision ) | |
? statusForWorkingCopy( $filenames ) | |
: statusAcrossRevisions( $revision ) | |
) ); | |
} | |
/** | |
* Generates args suitable for passing into the linters | |
* | |
* @return int Whether or not the program was able to complete successfully | |
*/ | |
function main() { | |
list( $type, $revision, $filenames ) = getShellArgs(); | |
if ( empty( $type ) ) { | |
die( 'Please choose type of information requested: "--type changeset" or "--type diff"' ); | |
} | |
if ( 'changeset' === $type ) { | |
echo listRelevantFiles( $revision, $filenames ); | |
} | |
if ( 'diff' === $type ) { | |
echo createDiff( $revision, $filenames ); | |
} | |
echo "\n"; | |
} | |
main(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment