Skip to content

Instantly share code, notes, and snippets.

@totten
Last active February 4, 2022 05:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save totten/acff49e6dd77947349a1df26d5002003 to your computer and use it in GitHub Desktop.
Save totten/acff49e6dd77947349a1df26d5002003 to your computer and use it in GitHub Desktop.
<?php
## The script will walk through a series of filters, display a preview for each,
## and ask if you want to apply them.
##
## USAGE: php relnote.php release-notes/X.Y.Z.md
##
## REQUIRES: php, colordiff
## SEE: https://gist.github.com/totten/acff49e6dd77947349a1df26d5002003
########################################################################
## Config
$diffCommand = 'colordiff -u %s %s | less -R';
########################################################################
## Filters
$filters = [
// Ex: "core#10" ==> "dev/core#10"
'Full lab ref' => function($l) { return preg_replace(';([^/])(core|report|wordpress|drupal)#(\d+);', '\1dev/\2#\3', $l); },
// Ex: "[NFC] Foo" or "NFC - Foo" ==> "(NFC) Foo"
'(NFC)' => function($l) { return preg_replace(';[\[\(]NFC[\]\)]( -)?;i', '(NFC)', $l); },
// Ex: "[REF] Foo" or "REF - Foo" ==> "(REF) Foo"
'(REF)' => function($l) { return preg_replace(';[\[\(]REF[\]\)]( -)?;i', '(REF)', $l); },
// Ex: "**NFC Foo**" ==> "**(NFC) Foo*"
'(NFC|REF) leading parens' => function($l) { return preg_replace(';(- \*\*)(NFC|REF)( -)? ;i', '\1(\2) ', $l); },
// Ex: "**dev/core#123 Foo**" ==> "**(dev/core#123) Foo**"
'(dev/foo) leading parens' => function($l) { return preg_replace(';(- \*\*)(' . labRef(). ')( -|:)? ;', '\1(\2) ', $l); },
// Ex: **[dev/core#123] Foo**" ==> "**(dev/core#123) Foo**"
'(dev/foo) not [dev/foo]' => function($l) { return preg_replace(';(- \*\*)\[(' . labRef(). ')\]( -|:)? ;', '\1(\2) ', $l); },
// Ex: "**fixes dev/core#123 foo**" ==> "**(dev/core#123) foo**"
'Skip leading "fixes"' => function($l) { return preg_replace(';(- \*\*)fixes (dev/\w+#\d+)( -|:)? ;', '\1(\2) ', $l); },
// Ex: "(NFC) foo bar" ==> "(NFC) Foo bar"
'ucfirst' => function($l){
return preg_replace_callback(';(- \*\*)\((' .tagLabHubRef() .')\) ([\w\-_\/:]+);',
function($m){
if (!looksSymbolic($m[3])) {
$m[3] = ucfirst($m[3]);
}
return sprintf('%s(%s) %s', $m[1], $m[2], $m[3]);
},
$l);
},
// Normalize words: These filters apply standard capitalization+spacing for some phrases that may be written several ways.
'APIv3' => normalizeWord('APIv3', ['api3', 'api v3']),
'APIv4' => normalizeWord('APIv4', ['api4', 'api v4']),
'SearchKit' => normalizeWord('SearchKit', ['Search kit', 'search_kit']),
'Flexmailer' => normalizeWord('Flexmailer', ['flexmail']),
// Normalize words: These filters apply standard capitalization for a long list of names and abbreviations.
'Civi Abbreviations' => normalizeWords(['BAO', 'DAO', 'PCP']),
'Civi Components' => normalizeWords(['Afform', 'AuthX', 'CiviCampaign', 'CiviCase', 'CiviContribute', 'CiviEvent', 'CiviGrant', 'CiviMail', 'CiviMember', 'CiviReport', 'CiviSMS', 'civix', 'cv', 'Greenwich', 'Shoreditch']),
'Industry Abbreviations' => normalizeWords(['ACL', 'CLI', 'CMS', 'ID', 'SMS', 'SQL', 'UI', 'URL', 'URLs']),
'Platform Names' => normalizeWords(['Backdrop', 'Drupal', 'D7', 'D8', 'D9', 'Joomla', 'JS', 'Javascript', 'MySQL', 'PHP', 'WordPress', 'WP']),
'Tool Names' => normalizeWords(['Angular', 'CKEditor', 'composer', 'drush', 'jQuery', 'KCFinder', 'Mosaico', 'PHPUnit', 'Smarty', 'WP-CLI']),
// 'dev/foo move to end' => function($l) {
// $urlRef = 'https?:[/\w\?\.%]+';
// $link = '\[(' . $hubRef . '|' . $labRef . ')\]('. $urlRef . ')';
// $listReg = "($hubRef|$labRef|$link)";
//
// return preg_replace_callback(';(- \*\*)(' . hubRef() . ')\s*(.*);',
// function($m) use ($listReg) {
// [$full, $bullet, $leadingRef, $tail] = $m;
// if (preg_match(';(.*)\(((' . $listReg .').*)$;', $tail, $tailm)) {
// return $bullet . $tailm[1] . '(' . $leadingRef .': ' . $tailm[2];
// }
// else {
// return $bullet . $tail . "($leadingRef)";
// }
// },
// $l);
// },
];
########################################################################
## Utils
function hubRef(): string { return '#\d+'; }
function labRef(): string { return 'dev/\w+#\d+'; }
function tagLabHubRef(): string { return 'REF|NFC|' . hubRef() . '|' . labRef(); }
function normalizeWord(string $word, $aliases = []) {
$wordQuoted = preg_quote($word, ';');
$aliases = implode('|', array_map(
function($alias) { return preg_quote($alias,';'); },
$aliases
));
return function($content) use ($word, $wordQuoted, $aliases) {
// We don't treat '.' or '/' as word-breaks because they often appear in long symbols (file-extensions, URLs, rel-paths).
$lwb = '[\s\*\(\)\[\]]';
$rwb = '[\s\*\(\)\[\],]';
$content = preg_replace(";($lwb)($wordQuoted)($rwb);i", '\1' . $word . '\3', $content);
if ($aliases) {
$content = preg_replace(";$aliases;i", $word, $content);
}
return $content;
};
}
function normalizeWords(array $words) {
return function($content) use ($words) {
foreach ($words as $word) {
$content = normalizeWord($word)($content);
}
return $content;
};
}
/**
* Does $string look a symbol (eg 'fooBar' or 'foo_bar')?
*
* @param $string
* @return bool
*/
function looksSymbolic(string $string): bool {
if (preg_match(';[^a-zA-Z0-9];', $string)) {
return TRUE;
}
if (preg_match(';^[a-z]+[A-Z]+;', $string)) {
return TRUE;
}
if (preg_match(';^[A-Z0-9]+$];', $string)) {
return TRUE;
}
return FALSE;
}
########################################################################
## Main
$currentFile = $argv[1];
$currentContent = file_get_contents($currentFile);
$tempFile = tempnam(sys_get_temp_dir(), 'relnote-preview-');
foreach ($filters as $filterName => $filter) {
$tempContent = $filter($currentContent);
if ($tempContent === $currentContent) {
echo "NO CHANGE: $filterName\n";
continue;
}
printf("\n\n\nPREVIEW: Apply filter: $filterName\n\n");
file_put_contents($tempFile, $tempContent);
passthru(sprintf('colordiff -u %s %s | less -R', $currentFile, $tempFile));
do {
echo "ENABLE ($filterName)? (Y/n) ";
$s = trim(fgets(STDIN));
} while (!in_array($s, ['y', 'n', '']));
if (in_array($s, ['y', ''])) {
file_put_contents($currentFile, $tempContent);
$currentContent = $tempContent;
echo "APPLIED: $filterName\n";
}
}
unlink($tempFile);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment