Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save thattomperson/7a8127ac306bb2e58c24ba16a521b621 to your computer and use it in GitHub Desktop.

Select an option

Save thattomperson/7a8127ac306bb2e58c24ba16a521b621 to your computer and use it in GitHub Desktop.
My Everyday Git hooks `nix-shell -p nodejs_18 --run "curl -L gist.githubusercontent.com/thattomperson/7a8127ac306bb2e58c24ba16a521b621/raw/%255Bpost-commit%255D-update-hooks.js | node"`
#! /usr/bin/env nix-shell
`
#! nix-shell --packages nodejs_18 -i node
`
const https = require('https');
const path = require('path');
const fs = require('fs');
const util = require('util');
const readline = require('readline')
const spawn = require('child_process').spawnSync;
const mkdir = util.promisify(fs.mkdir);
const writeFile = util.promisify(fs.writeFile);
const chmod = util.promisify(fs.chmod);
const readFile = util.promisify(fs.readFile);
const gistId = '7a8127ac306bb2e58c24ba16a521b621';
const updateFile = path.join('.git', 'ttp-hooks-last-update');
const yes = process.argv.includes('-y')
function get(url) {
let content = '';
return new Promise((resolve, reject) => {
https.get(
url,
{
headers: {
'User-Agent': 'node-git-hooks-downloader',
},
},
(res) => {
res.on('data', (data) => {
content += data;
});
res.on('close', () => {
resolve(content);
});
res.on('error', reject);
}
);
});
}
async function getContent({ truncated, content, raw_url }) {
return truncated ? await get(raw_url) : content;
}
async function write(filepath, file) {
const content = await getContent(file);
return writeFile(filepath, content);
}
async function writeAliases(aliases) {
const content = await getContent(aliases);
const lines = content.split('\n');
lines.map(line => {
const [name, body] = line.split(/=(.*)/s).map(p => p.trim());
console.log(`\tAdding ${name} alias`);
return exec('git', 'config', '--replace-all', `alias.${name}`, body);
})
}
function exec(command, ...args) {
spawn(command, args)
}
function groupHooks(files) {
const groups = {
hooks: {},
ungrouped: {},
};
Object.values(files).forEach((file) => {
let matches = /\[(.+?)\]-(.*)/.exec(file.filename);
if (matches) {
groups.hooks[matches[1]] = groups.hooks[matches[1]] || {};
groups.hooks[matches[1]][matches[2]] = file;
} else if (file.filename === 'aliases') {
groups.aliases = file
} else {
groups.ungrouped[file.filename] = file;
}
});
return groups;
}
async function writeAll(groups) {
console.log("Writing hooks")
await Promise.all(
Object.keys(groups.hooks).map(async (hook) => {
await Promise.all(
Object.keys(groups.hooks[hook]).map(async (filename) => {
console.log(`\tWriting ${hook} ${filename}`)
const folder = path.join('.git', 'hooks', 'hooks.d', hook);
const filepath = path.join(folder, filename);
await mkdir(folder, { recursive: true });
await write(filepath, groups.hooks[hook][filename]);
await chmod(filepath, '775');
})
);
const filepath = path.join('.git', 'hooks', hook);
const content = Object.keys(groups.hooks[hook]).reduce(
(contents, filename) => {
const folder = path.join('hooks', 'hooks.d', hook);
const filepath = path.join(folder, filename);
return `${contents}\n $GIT_COMMON_DIR/${filepath}\n`;
},
'#!/usr/bin/env sh\nset -e\n\n GIT_COMMON_DIR=$(git rev-parse --git-common-dir);\n\n'
);
await writeFile(filepath, content);
await chmod(filepath, '775');
})
);
console.log('Writing aliases');
await writeAliases(groups.aliases)
console.log('Writing additional files');
await Promise.all(
Object.keys(groups.ungrouped).map(async (filename) => {
console.log(`\tWriting ${filename}`)
const folder = path.join('.git');
const filepath = path.join(folder, filename);
await mkdir(folder, { recursive: true });
await write(filepath, groups.ungrouped[filename]);
})
);
}
async function checkUpdatedTime(response) {
try {
const lastUpdatedAt = (await readFile(updateFile)).toString().trim();
if (response.updated_at !== lastUpdatedAt) {
response.local_updated_at = lastUpdatedAt;
return response
}
} catch {
response.local_updated_at = '1970-01-01T00:00:00Z';
return response
}
throw 'no-updates';
}
function askForUpdate(response) {
if (yes) {
return Promise.resolve(response)
}
return new Promise((resolve, reject) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const ac = new AbortController();
const signal = ac.signal;
const timeout = setTimeout(() => {
ac.abort()
rl.close()
console.log('Took too long to answer, aborting update')
reject();
}, 10e3)
console.log('There is an update to your githooks available')
rl.question('Would you like to update [Y/n]', { signal }, function (answer) {
console.log({ answer })
clearTimeout(timeout);
if (answer === '' || answer.charAt(0).toLowerCase() === 'y') {
resolve(response)
}
rl.close();
})
});
}
async function updateUpdatedAt(response) {
await writeFile(updateFile, response.updated_at);
return response;
}
// 1 in 100 chance to check for updates
const num = Math.round(Math.random() * 100);
if (num === 1 || yes) {
get(`https://api.github.com/gists/${gistId}`)
.then((content) => JSON.parse(content))
.then(checkUpdatedTime)
.then(askForUpdate)
.then(updateUpdatedAt)
.then((response) => response.files)
.then(groupHooks)
.then(writeAll)
.catch((error) => {
// If we have forced updates and there is nothing to do tell the user
if (yes && error === 'no-updates') {
console.log('No updates required')
}
})
}
#! /usr/bin/env bash
# Check for merge conflicts
# Tested on Linux and Mac
# Simple check for merge conflics
conflicts=`git diff --cached --name-only -G"<<<<<|=====|>>>>>"`
# Something went wrong
if [[ -n "$conflicts" ]]; then
echo
echo "Unresolved merge conflicts in these files:"
for conflict in $conflicts; do
echo $conflict
done;
exit 1;
fi
exit 0
#! /usr/bin/env bash
nix run nixpkgs#php82Packages.phpcs -- --filter=GitStaged --standard=$(git rev-parse --git-common-dir)/phpcs.xml --runtime-set ignore_warnings_on_exit 1 --colors .
php-lint = !f() { nix run nixpkgs#php82Packages.phpcs -- --filter=GitStaged --standard=$(git rev-parse --git-common-dir)/phpcs.xml --runtime-set ignore_warnings_on_exit 1 --colors ${GIT_PREFIX:-./}; }; f
php-lint-fix = !f() { nix run nixpkgs#php82Packages.phpcbf -- --filter=GitStaged --standard=$(git rev-parse --git-common-dir)/phpcs.xml --runtime-set ignore_warnings_on_exit 1 --colors ${GIT_PREFIX:-./}; }; f
update-hooks = !f() { $(git rev-parse --git-common-dir)/hooks/hooks.d/post-commit/update-hooks.js -y; }; f
<?xml version="1.0"?>
<ruleset name="Thirty4">
<exclude-pattern type="relative">^lib/model/migration/*</exclude-pattern>
<exclude-pattern type="relative">^lib/model/om/*</exclude-pattern>
<exclude-pattern type="relative">^lib/model/map/*</exclude-pattern>
<exclude-pattern type="relative">^lib/*/base</exclude-pattern>
<exclude-pattern type="relative">^cache/*</exclude-pattern>
<exclude-pattern type="relative">*.css</exclude-pattern>
<exclude-pattern type="relative">*.js</exclude-pattern>
<rule ref="Generic.Arrays.DisallowLongArraySyntax" />
<rule ref="Generic.CodeAnalysis" />
<rule ref="Squiz.Arrays.ArrayDeclaration.NoComma" />
<rule ref="Squiz.Functions.MultiLineFunctionDeclaration">
<properties>
<property name="indent" value="2" />
</properties>
</rule>
<rule ref="PSR12"/>
<rule ref="Generic.WhiteSpace.ScopeIndent">
<properties>
<property name="indent" value="2" />
</properties>
</rule>
<rule ref="PSR2.Methods.FunctionCallSignature">
<properties>
<property name="indent" value="2" />
</properties>
</rule>
<rule ref="PSR12.ControlStructures.ControlStructureSpacing">
<properties>
<property name="indent" value="2" />
</properties>
</rule>
<rule ref="PSR2.ControlStructures.SwitchDeclaration">
<properties>
<property name="indent" value="2" />
</properties>
</rule>
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
<severity>0</severity>
</rule>
<rule ref="Squiz.Classes.ValidClassName.NotCamelCaps">
<type>warning</type>
</rule>
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
<type>warning</type>
</rule>
<rule ref="Squiz.WhiteSpace.ControlStructureSpacing.SpacingBeforeClose">
<type>warning</type>
</rule>
<rule ref="Generic.CodeAnalysis.EmptyStatement.DetectedCatch">
<type>warning</type>
</rule>
<rule ref="Squiz.Commenting.EmptyCatchComment">
<type>error</type>
</rule>
</ruleset>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment