Use stow for managing dotfiles
This commit is contained in:
parent
7b29ee2104
commit
7fed1cf922
83 changed files with 61 additions and 40 deletions
BIN
bin/bin/drupalorg
Executable file
BIN
bin/bin/drupalorg
Executable file
Binary file not shown.
20
bin/bin/git-abort
Executable file
20
bin/bin/git-abort
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Abort a rebase, merge, `am`, a cherry-pick or a revert, depending on the situation.
|
||||
|
||||
if [[ -e .git/CHERRY_PICK_HEAD ]] ; then
|
||||
exec git cherry-pick --abort "$@"
|
||||
elif [[ -e .git/REVERT_HEAD ]] ; then
|
||||
exec git revert --abort "$@"
|
||||
elif [[ -e .git/rebase-apply/applying ]] ; then
|
||||
exec git am --abort "$@"
|
||||
elif [[ -e .git/rebase-apply ]] ; then
|
||||
exec git rebase --abort "$@"
|
||||
elif [[ -e .git/rebase-merge ]] ; then
|
||||
exec git rebase --abort "$@"
|
||||
elif [[ -e .git/MERGE_MODE ]] ; then
|
||||
exec git merge --abort "$@"
|
||||
else
|
||||
echo git-abort: unknown state
|
||||
exit -1
|
||||
fi
|
271
bin/bin/git-close-pull-request
Executable file
271
bin/bin/git-close-pull-request
Executable file
|
@ -0,0 +1,271 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Usage: git close-pull-request -t <target>
|
||||
*
|
||||
* Run this from a branch which has an upstream remote branch, and an associated
|
||||
* pull request.
|
||||
*
|
||||
* The script will merge the branch into master, push master (which will
|
||||
* automatically close the pull request), and delete both the local and remote
|
||||
* branches.
|
||||
*
|
||||
* Based on a script by @christoomey. Translated into PHP.
|
||||
*/
|
||||
|
||||
class ClosesPullRequests
|
||||
{
|
||||
private $targetBranch;
|
||||
private $localBranch;
|
||||
private $remoteBranch;
|
||||
|
||||
private const RUN_TYPE_COMMAND = 'command';
|
||||
private const RUN_TYPE_QUERY = 'query';
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->localBranch = $this->run(
|
||||
'git rev-parse --abbrev-ref HEAD',
|
||||
self::RUN_TYPE_QUERY
|
||||
);
|
||||
$this->targetBranch = $this->getTargetBranchFromArgs();
|
||||
|
||||
$this->remoteBranch = $this->run(
|
||||
'git rev-parse --abbrev-ref --symbolic-full-name @{u}',
|
||||
self::RUN_TYPE_QUERY
|
||||
);
|
||||
$this->remoteBranch = str_replace('origin/', '', $this->remoteBranch);
|
||||
}
|
||||
|
||||
public function __invoke(): void
|
||||
{
|
||||
$this->confirmCiStatusIsPassing();
|
||||
// TODO: Check that the current branch has a tracking branch.
|
||||
$this->ensureWorkingDirectoryAndIndexAreClean();
|
||||
$this->fetchOrigin();
|
||||
$this->ensureFeatureBranchInSync();
|
||||
$this->ensureTargetBranchInSync();
|
||||
$this->checkoutTargetBranch();
|
||||
$this->mergeLocalBranch();
|
||||
$this->pushTargetBranch();
|
||||
$this->deleteRemoteBranch();
|
||||
$this->deleteLocalBranch();
|
||||
}
|
||||
|
||||
private function ensureWorkingDirectoryAndIndexAreClean(): void
|
||||
{
|
||||
echo 'Ensuring that index and working directory are clean...' . PHP_EOL;
|
||||
|
||||
$isIndexClean = $this->run('git diff --cached --exit-code', self::RUN_TYPE_COMMAND);
|
||||
$isWorkingDirClean = $this->run('git diff --exit-code', self::RUN_TYPE_COMMAND);
|
||||
|
||||
if (!$isIndexClean || !$isWorkingDirClean) {
|
||||
$this->dieWithMessage('Index or working dir not clean. Aborting.');
|
||||
}
|
||||
}
|
||||
|
||||
private function getTargetBranchFromArgs(): string
|
||||
{
|
||||
if (!$targetBranchName = $this->getArg('t:', ['target:'])) {
|
||||
$this->dieWithMessage('Invalid target branch specified. Aborting.');
|
||||
}
|
||||
|
||||
return $targetBranchName;
|
||||
}
|
||||
|
||||
private function confirmCiStatusIsPassing(): void
|
||||
{
|
||||
if ($this->isForce()) {
|
||||
echo 'Forced. Skipping ci-status check...' . PHP_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
echo 'Confirming ci-status on PR is green...' . PHP_EOL;
|
||||
|
||||
$passedCi = $this->run('gh pr checks', self::RUN_TYPE_COMMAND);
|
||||
|
||||
// TODO: Check if there are no CI checks. Does this return `true` as well?
|
||||
if (!$passedCi) {
|
||||
$this->dieWithMessage('CI pending or failed.');
|
||||
}
|
||||
}
|
||||
|
||||
private function fetchOrigin(): void
|
||||
{
|
||||
print 'Fetching origin to confirm local and remote in sync...'
|
||||
. PHP_EOL;
|
||||
|
||||
$this->run('git fetch origin', self::RUN_TYPE_COMMAND);
|
||||
}
|
||||
|
||||
private function ensureTargetBranchInSync(): void
|
||||
{
|
||||
$this->ensureBranchInSyncWithUpstream(
|
||||
$this->targetBranch,
|
||||
$this->targetBranch
|
||||
);
|
||||
}
|
||||
|
||||
private function ensureFeatureBranchInSync(): void
|
||||
{
|
||||
$this->ensureBranchInSyncWithUpstream(
|
||||
$this->localBranch,
|
||||
$this->remoteBranch
|
||||
);
|
||||
}
|
||||
|
||||
private function ensureBranchInSyncWithUpstream(
|
||||
string $localBranch,
|
||||
string $remoteBranch
|
||||
): void {
|
||||
echo sprintf(
|
||||
'Ensuring that %s is in sync with its upstream...',
|
||||
$localBranch
|
||||
) . PHP_EOL;
|
||||
|
||||
$localCommitTip = $this->tipCommitOfBranch($localBranch);
|
||||
$remoteCommitTip = $this->tipCommitOfBranch(sprintf(
|
||||
'origin/%s',
|
||||
$remoteBranch
|
||||
));
|
||||
|
||||
if ($localCommitTip != $remoteCommitTip) {
|
||||
$this->dieWithMessage(sprintf(
|
||||
'Branch %s was out of date, needs rebasing. Aborting.',
|
||||
$localBranch
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
private function tipCommitOfBranch(string $branchName): string
|
||||
{
|
||||
return $this->run(
|
||||
sprintf('git rev-parse %s', $branchName),
|
||||
self::RUN_TYPE_QUERY
|
||||
);
|
||||
}
|
||||
|
||||
private function checkoutTargetBranch(): void
|
||||
{
|
||||
echo sprintf('Checking out %s...' . PHP_EOL, $this->targetBranch);
|
||||
|
||||
$this->run(
|
||||
sprintf('git checkout %s', $this->targetBranch),
|
||||
self::RUN_TYPE_COMMAND
|
||||
);
|
||||
}
|
||||
|
||||
private function mergeLocalBranch(): void
|
||||
{
|
||||
echo sprintf(
|
||||
'Merging %s into %s...' . PHP_EOL,
|
||||
$this->localBranch,
|
||||
$this->targetBranch
|
||||
);
|
||||
|
||||
$mergeCommand = sprintf('git merge --ff-only %s', $this->localBranch);
|
||||
if (!$this->run($mergeCommand, self::RUN_TYPE_COMMAND)) {
|
||||
// Switch back to the previous branch.
|
||||
$this->run('git checkout -', self::RUN_TYPE_COMMAND);
|
||||
|
||||
$this->dieWithMessage(sprintf(
|
||||
'Branch %s is not fast-forwardable.',
|
||||
$this->localBranch
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public function pushTargetBranch(): void
|
||||
{
|
||||
print(sprintf('Pushing updated %s branch...', $this->targetBranch));
|
||||
|
||||
$this->run(
|
||||
sprintf('git push origin %s', $this->targetBranch),
|
||||
self::RUN_TYPE_COMMAND
|
||||
);
|
||||
}
|
||||
|
||||
public function deleteRemoteBranch(): void
|
||||
{
|
||||
echo 'Deleting remote branch...' . PHP_EOL;
|
||||
|
||||
$this->run(
|
||||
sprintf('git push origin :%s', $this->remoteBranch),
|
||||
self::RUN_TYPE_COMMAND
|
||||
);
|
||||
}
|
||||
|
||||
public function deleteLocalBranch(): void
|
||||
{
|
||||
echo 'Deleting local branch...' . PHP_EOL;
|
||||
|
||||
$this->run(
|
||||
sprintf('git branch -d %s', $this->localBranch),
|
||||
self::RUN_TYPE_COMMAND
|
||||
);
|
||||
}
|
||||
|
||||
private function getArg(string $shortOpts, array $longOpts = []): ?string
|
||||
{
|
||||
if (!$values = getopt($shortOpts, $longOpts)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return current($values);
|
||||
}
|
||||
|
||||
private function hasArg(string $shortOpts, array $longOpts = []): bool
|
||||
{
|
||||
return !empty(getopt($shortOpts, $longOpts));
|
||||
}
|
||||
|
||||
private function isForce(): bool
|
||||
{
|
||||
return $this->hasArg('f::', ['force::']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the command.
|
||||
*
|
||||
* @return bool|string
|
||||
* If the type is 'command', the method will return if there were any
|
||||
* errors when running the command based on its return code.
|
||||
*
|
||||
* If the type is 'query', then the output of the command will be returned
|
||||
* as a string.
|
||||
*/
|
||||
private function run(string $command, string $type)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::RUN_TYPE_COMMAND:
|
||||
// Perform the command, hiding the original output and return
|
||||
// whether or not there were errors.
|
||||
@exec("$command", $output, $return);
|
||||
|
||||
return $return == 0;
|
||||
|
||||
case self::RUN_TYPE_QUERY:
|
||||
// Perform the command and return the output.
|
||||
return exec($command, $output);
|
||||
}
|
||||
}
|
||||
|
||||
private function dieWithMessage(string $message): void
|
||||
{
|
||||
echo sprintf("\e[31m%s\e[0m", $message);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
private function exitWithWarning(string $message): void
|
||||
{
|
||||
echo sprintf("\e[33m%s\e[0m", $message);
|
||||
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
(new ClosesPullRequests())->__invoke();
|
7
bin/bin/git-cm
Executable file
7
bin/bin/git-cm
Executable file
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [[ $# > 0 ]]; then
|
||||
git commit -m "$@"
|
||||
else
|
||||
git commit -v
|
||||
fi
|
16
bin/bin/git-continue
Executable file
16
bin/bin/git-continue
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Continue a rebase or cherry-pick in the event of conflicts.
|
||||
|
||||
if [[ -e .git/CHERRY_PICK_HEAD ]] ; then
|
||||
exec git cherry-pick --continue "$@"
|
||||
elif [[ -e .git/rebase-apply/applying ]] ; then
|
||||
exec git rebase --continue "$@"
|
||||
elif [[ -e .git/rebase-apply ]] ; then
|
||||
exec git rebase --continue "$@"
|
||||
elif [[ -e .git/rebase-merge ]] ; then
|
||||
exec git rebase --continue "$@"
|
||||
else
|
||||
echo git-abort: unknown state
|
||||
exit -1
|
||||
fi
|
15
bin/bin/git-create-new-tracking-branch
Executable file
15
bin/bin/git-create-new-tracking-branch
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "Error: Not enough arguments."
|
||||
exit l
|
||||
fi
|
||||
|
||||
# Create a new branch including any additional arguments.
|
||||
git checkout -b "$@"
|
||||
|
||||
# Push the branch to origin, bypassing any Git hooks.
|
||||
new_branch_name=$1
|
||||
git push --no-verify -u origin "${new_branch_name}:opd-${new_branch_name}"
|
42
bin/bin/git-delete-merged-branches
Executable file
42
bin/bin/git-delete-merged-branches
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
function extractBranchNamesFromInfo(string $branchInfo): array
|
||||
{
|
||||
$branchNames = array_map(function (string $branchInfo): string {
|
||||
preg_match('/\s*((\w|-|\/)+)\s*/', $branchInfo, $matches);
|
||||
|
||||
return $matches[1] ?? '';
|
||||
}, explode(PHP_EOL, $branchInfo));
|
||||
|
||||
return array_filter($branchNames);
|
||||
}
|
||||
|
||||
function filterIgnoredBranches(array $branchNames): array
|
||||
{
|
||||
return array_filter($branchNames, function (string $branchName): bool {
|
||||
return !in_array($branchName, ['develop', 'master', 'staging', 'production']);
|
||||
});
|
||||
}
|
||||
|
||||
$branchInfo = shell_exec('git branch -vv | grep ": gone]"');
|
||||
|
||||
# Return early if there are no branches to delete.
|
||||
if ($branchInfo === NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
$branchNames = extractBranchNamesFromInfo($branchInfo);
|
||||
$filteredBranchNames = filterIgnoredBranches($branchNames);
|
||||
|
||||
$currentBranch = exec('git rev-parse --abbrev-ref HEAD');
|
||||
|
||||
foreach ($filteredBranchNames as $branchName) {
|
||||
if ($branchName == $currentBranch) {
|
||||
echo "Cannot delete {$branchName} as it is the current branch.";
|
||||
continue;
|
||||
}
|
||||
|
||||
echo "Deleting {$branchName}...";
|
||||
exec("git branch -D ${branchName}");
|
||||
}
|
19
bin/bin/git-master-to-main-wrapper
Executable file
19
bin/bin/git-master-to-main-wrapper
Executable file
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env zsh
|
||||
|
||||
# Usage: instead of
|
||||
#
|
||||
# git rebase -i master
|
||||
#
|
||||
# run this:
|
||||
#
|
||||
# git master-to-main-wrapper rebase -i %BRANCH%
|
||||
#
|
||||
# It will replace the literal string `%BRANCH%` with "main" (preferred) or
|
||||
# "master" depending on what the current repository uses.
|
||||
|
||||
command=$*
|
||||
branchname=$(main-or-master-branch)
|
||||
replaced_commands=$(echo $command | sed "s/%BRANCH%/$branchname/g")
|
||||
# sh_glob ignores special meaning of parentheses so that fancy logs like this
|
||||
# work: `git master-to-main-wrapper log --format='%w(78)%s%n%+b'`
|
||||
zsh -c "setopt sh_glob; git ${replaced_commands}"
|
26
bin/bin/git-opr
Executable file
26
bin/bin/git-opr
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
ensure_is_published() {
|
||||
[[ ! $is_published ]] && git publish
|
||||
}
|
||||
|
||||
is_published() {
|
||||
echo $(git upstream)
|
||||
}
|
||||
|
||||
open_or_build_pull_request() {
|
||||
type gh &>/dev/null
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: gh command not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Load an existing PR, or create a new one.
|
||||
gh pr view --web || gh pr create --assignee opdavies --web
|
||||
}
|
||||
|
||||
ensure_is_published
|
||||
open_or_build_pull_request
|
15
bin/bin/git-publish
Executable file
15
bin/bin/git-publish
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Usage: git publish
|
||||
*/
|
||||
|
||||
$currentBranch = exec('git rev-parse --abbrev-ref HEAD');
|
||||
|
||||
if (in_array($currentBranch, ['develop', 'main', 'master', 'staging', 'production'])) {
|
||||
print "Currently on ${currentBranch}. Aborting.";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exec("git push -u origin $currentBranch:opd-{$currentBranch}");
|
21
bin/bin/git-up
Executable file
21
bin/bin/git-up
Executable file
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Usage: git up {branch} {remote}
|
||||
|
||||
set -e
|
||||
|
||||
if [[ $# < 1 ]]; then
|
||||
echo "You must specify a branch name to update"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BRANCH=$1
|
||||
REMOTE=${2:-origin}
|
||||
|
||||
git checkout ${BRANCH} && \
|
||||
git fetch ${REMOTE} && \
|
||||
echo && \
|
||||
git sl ${BRANCH}..${REMOTE}/${BRANCH} && \
|
||||
echo && \
|
||||
git pull --quiet && \
|
||||
git checkout -
|
28
bin/bin/git.sh
Executable file
28
bin/bin/git.sh
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash
|
||||
|
||||
# The MIT License (MIT)
|
||||
# Copyright (c) 2013 Alvin Abad
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
echo "Git wrapper script that can specify an ssh-key file
|
||||
Usage:
|
||||
git.sh -i ssh-key-file git-command
|
||||
"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# remove temporary file on exit
|
||||
trap 'rm -f /tmp/.git_ssh.$$' 0
|
||||
|
||||
if [ "$1" = "-i" ]; then
|
||||
SSH_KEY=$2; shift; shift
|
||||
echo "ssh -i $SSH_KEY \$@" > /tmp/.git_ssh.$$
|
||||
chmod +x /tmp/.git_ssh.$$
|
||||
export GIT_SSH=/tmp/.git_ssh.$$
|
||||
fi
|
||||
|
||||
# in case the git command is repeated
|
||||
[ "$1" = "git" ] && shift
|
||||
|
||||
# Run the git command
|
||||
git "$@"
|
10
bin/bin/main-or-master-branch
Executable file
10
bin/bin/main-or-master-branch
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/zsh
|
||||
|
||||
# Check if we should use the `main` or `master` branch for this repo.
|
||||
# Prefer `main` to `master`.
|
||||
|
||||
if git show-ref --quiet origin/main || git rev-parse main &>/dev/null; then
|
||||
echo main
|
||||
else
|
||||
echo master
|
||||
fi
|
8
bin/bin/phpunit-or-pest
Executable file
8
bin/bin/phpunit-or-pest
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -f "vendor/bin/pest" ]]; then
|
||||
echo "pest"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "phpunit"
|
31
bin/bin/tat
Executable file
31
bin/bin/tat
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Attach or create tmux session named the same as current directory.
|
||||
|
||||
path_name="$(basename "$PWD" | tr . -)"
|
||||
session_name=${1-$path_name}
|
||||
|
||||
not_in_tmux() {
|
||||
[ -z "$TMUX" ]
|
||||
}
|
||||
|
||||
session_exists() {
|
||||
tmux has-session -t "=$session_name"
|
||||
}
|
||||
|
||||
create_detached_session() {
|
||||
(TMUX='' tmux new-session -Ad -s "$session_name")
|
||||
}
|
||||
|
||||
create_if_needed_and_attach() {
|
||||
if not_in_tmux; then
|
||||
tmux new-session -As "$session_name"
|
||||
else
|
||||
if ! session_exists; then
|
||||
create_detached_session
|
||||
fi
|
||||
tmux switch-client -t "$session_name"
|
||||
fi
|
||||
}
|
||||
|
||||
create_if_needed_and_attach
|
Loading…
Add table
Add a link
Reference in a new issue