├── .rubocop.yml
├── gemrc
├── guard.rb
├── yarnrc.yml
├── ripgreprc
├── xrayconfig
├── config
├── bat
│ └── config
├── direnv
│ └── direnv.toml
├── mise
│ └── config.toml
├── xbar
│ └── settings.json.erb
├── git
│ └── ignore
└── pgcli
│ └── config
├── jscsrc
├── sublime
├── Git blame.sublime-settings
├── Git Commit.sublime-settings
├── Ruby.sublime-settings
├── JsPrettier.sublime-settings
├── ruby-initialize.sublime-snippet
├── ruby-metaclass.sublime-snippet
├── co-authored-by.sublime-snippet
├── ColorHighlighter.sublime-settings
├── Default (OSX).sublime-keymap
└── Preferences.sublime-settings
├── bashrc.d
├── shopt.bash
├── prompt.bash
├── man.bash
├── history.bash
├── android.bash
├── java.bash
├── completion.bash
├── env.bash
├── aliases.bash
└── ruby.bash
├── bash_profile
├── bin
├── subl
├── brew-install
├── node-install
├── ruby-install
├── release-notes
├── cci
├── git-ppr
├── every
├── rails_generate_and_open
├── sweep
├── git-pluck
├── defaults-install
├── sup
├── sci
├── bucket
├── sp
├── gemm
├── git-trim
├── outdated-projects
└── git-squash
├── railsrc
├── default-npm-packages
├── psqlrc
├── default-gems
├── .gitignore
├── inputrc
├── bundle
└── config.erb
├── bashrc
├── liquidpromptrc
├── completions
└── pgcli
├── Brewfile.erb
├── terminalizer
└── config.yml
├── gitconfig.erb
├── README.md
├── Rakefile
└── extras
└── Matt.terminal
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | rubocop.yml
--------------------------------------------------------------------------------
/gemrc:
--------------------------------------------------------------------------------
1 | gem: --no-document
2 |
--------------------------------------------------------------------------------
/guard.rb:
--------------------------------------------------------------------------------
1 | clearing :on
2 |
--------------------------------------------------------------------------------
/yarnrc.yml:
--------------------------------------------------------------------------------
1 | enableTelemetry: 0
2 |
--------------------------------------------------------------------------------
/ripgreprc:
--------------------------------------------------------------------------------
1 | -.
2 | --glob=!.git/
3 |
--------------------------------------------------------------------------------
/xrayconfig:
--------------------------------------------------------------------------------
1 | ---
2 | :editor: "subl"
3 |
--------------------------------------------------------------------------------
/config/bat/config:
--------------------------------------------------------------------------------
1 | --theme="Dracula"
2 |
--------------------------------------------------------------------------------
/jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "airbnb"
3 | }
4 |
--------------------------------------------------------------------------------
/sublime/Git blame.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/bashrc.d/shopt.bash:
--------------------------------------------------------------------------------
1 | shopt -s cdspell
2 | shopt -s checkhash
3 |
--------------------------------------------------------------------------------
/config/direnv/direnv.toml:
--------------------------------------------------------------------------------
1 | [global]
2 | load_dotenv=false
3 |
--------------------------------------------------------------------------------
/bash_profile:
--------------------------------------------------------------------------------
1 | if [ -f ~/.bashrc ]; then
2 | source ~/.bashrc
3 | fi
4 |
--------------------------------------------------------------------------------
/bin/subl:
--------------------------------------------------------------------------------
1 | /Applications/Sublime Text.app/Contents/SharedSupport/bin/subl
--------------------------------------------------------------------------------
/railsrc:
--------------------------------------------------------------------------------
1 | -d postgresql
2 | --skip-action-mailbox
3 | --skip-action-text
4 |
--------------------------------------------------------------------------------
/default-npm-packages:
--------------------------------------------------------------------------------
1 | @antfu/ni
2 | eslint
3 | prettier
4 | react-devtools
5 | yarn
6 |
--------------------------------------------------------------------------------
/bin/brew-install:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -xe
4 |
5 | brew update
6 | brew bundle --global --no-upgrade
7 | brew cleanup --prune=30
8 | brew upgrade
9 |
--------------------------------------------------------------------------------
/sublime/Git Commit.sublime-settings:
--------------------------------------------------------------------------------
1 | // These settings override both User and Default settings for the Git Commit syntax
2 | {
3 | "rulers": [50, 72],
4 | }
5 |
--------------------------------------------------------------------------------
/bashrc.d/prompt.bash:
--------------------------------------------------------------------------------
1 | # Use https://github.com/nojhan/liquidprompt
2 | if [ -f $BREW_PREFIX/share/liquidprompt ]; then
3 | . $BREW_PREFIX/share/liquidprompt
4 | fi
5 |
--------------------------------------------------------------------------------
/sublime/Ruby.sublime-settings:
--------------------------------------------------------------------------------
1 | // These settings override both User and Default settings for the Ruby syntax
2 | {
3 | "word_separators": "./\\()\"'-:,.;<>~@#$%^&*|+=[]{}`~",
4 | }
5 |
--------------------------------------------------------------------------------
/psqlrc:
--------------------------------------------------------------------------------
1 | \set PROMPT1 '%n@%/\n%R%x%# '
2 | \set PROMPT2 '%R%x%# '
3 | \set COMP_KEYWORD_CASE upper
4 | \pset border 1
5 | \pset format wrapped
6 | \pset linestyle unicode
7 | \pset null NULL
8 |
--------------------------------------------------------------------------------
/bashrc.d/man.bash:
--------------------------------------------------------------------------------
1 | # Use Apple's man page viewer if we are on a local console
2 | if [ "$TERM_PROGRAM" == "Apple_Terminal" ]; then
3 | function man {
4 | open x-man-page://$1
5 | }
6 | fi
7 |
--------------------------------------------------------------------------------
/sublime/JsPrettier.sublime-settings:
--------------------------------------------------------------------------------
1 | // Settings in here override those in "/JsPrettier/JsPrettier.sublime-settings",
2 |
3 | {
4 | "prettier_cli_path": "~/.local/share/mise/shims/prettier",
5 | }
6 |
--------------------------------------------------------------------------------
/config/mise/config.toml:
--------------------------------------------------------------------------------
1 | [tools]
2 | node = "22.15.0"
3 | ruby = "3.4.3"
4 |
5 | [settings]
6 | idiomatic_version_file_enable_tools = ["ruby", "node"]
7 |
8 | [settings.ruby]
9 | verbose_install = true
10 |
--------------------------------------------------------------------------------
/config/xbar/settings.json.erb:
--------------------------------------------------------------------------------
1 | {
2 | "covid": {
3 | "api_key": <%= prompt("COVID Act Now API key").inspect %>,
4 | "county_fips": <%= prompt("US County FIPS code (for COVID stats)").inspect %>
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/bashrc.d/history.bash:
--------------------------------------------------------------------------------
1 | HISTSIZE=500000
2 | HISTFILESIZE=100000
3 | export HISTIGNORE="&:ls:l:la:ll:exit"
4 |
5 | # Rebind ctrl-r with to show smart history search
6 | export MCFLY_RESULTS=16
7 | eval "$(mcfly init bash)"
8 |
--------------------------------------------------------------------------------
/sublime/ruby-initialize.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
7 | init
8 | source.ruby
9 |
10 |
--------------------------------------------------------------------------------
/sublime/ruby-metaclass.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
7 | meta
8 | source.ruby
9 |
10 |
--------------------------------------------------------------------------------
/default-gems:
--------------------------------------------------------------------------------
1 | bundle_update_interactive
2 | bundler
3 | bundleup
4 | highline
5 | irb
6 | overcommit
7 | rake
8 | retest
9 | rubocop
10 | rubocop-minitest
11 | rubocop-performance
12 | rubocop-rails
13 | solargraph
14 | tomo
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Database of previously entered template values
2 | /.db
3 |
4 | # Brew packages to ignore on this machine
5 | /.brewignore
6 |
7 | # Local Sublime state
8 | /sublime/Package Control*
9 | /sublime/Color Highlighter/
10 |
--------------------------------------------------------------------------------
/sublime/co-authored-by.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
4 | ]]>
5 | co-
6 | text.git.commit
7 |
8 |
--------------------------------------------------------------------------------
/bashrc.d/android.bash:
--------------------------------------------------------------------------------
1 | if [ -d ~/Library/Android/sdk ]; then
2 | export ANDROID_HOME=~/Library/Android/sdk
3 | export PATH=$PATH:$ANDROID_HOME/platform-tools
4 | export PATH=$PATH:$ANDROID_HOME/tools
5 | export PATH=$PATH:$ANDROID_HOME/tools/bin
6 | fi
7 |
--------------------------------------------------------------------------------
/bin/node-install:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | mise install "node@$1"
6 | echo -n "Installed: "
7 | mise exec "node@$1" -- node -v
8 |
9 | read -r -p "Press enter to make $1 the global default Node, or ^C to cancel. "
10 | mise use --global "node@$1"
11 |
--------------------------------------------------------------------------------
/bashrc.d/java.bash:
--------------------------------------------------------------------------------
1 | if [ -d /System/Library/Frameworks/JavaVM.framework/Versions/1.6 ]; then
2 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home
3 | export MAVEN_OPTS="-Xmx512m -XX:+HeapDumpOnOutOfMemoryError"
4 | fi
5 |
6 | alias gw="./gradlew --daemon"
7 |
--------------------------------------------------------------------------------
/inputrc:
--------------------------------------------------------------------------------
1 | "\e[A": history-search-backward
2 | "\e[B": history-search-forward
3 | $if Bash
4 | Space: magic-space
5 | $endif
6 | set completion-ignore-case on
7 | set completion-map-case on
8 | set match-hidden-files off
9 | set show-all-if-ambiguous on
10 | set colored-stats on
11 |
--------------------------------------------------------------------------------
/bin/ruby-install:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | CFLAGS=-O3 mise install "ruby@$1"
6 | mise exec "ruby@$1" -- bundle plugin install bundler-why
7 | echo -n "Installed: "
8 | mise exec "ruby@$1" -- ruby -v
9 |
10 | read -r -p "Press enter to make $1 the global default Ruby, or ^C to cancel. "
11 |
12 | mise use --global "ruby@$1"
13 |
--------------------------------------------------------------------------------
/bundle/config.erb:
--------------------------------------------------------------------------------
1 | ---
2 | BUNDLE_IGNORE_MESSAGES__HTTPARTY: "true"
3 | BUNDLE_GEM__COC: "true"
4 | BUNDLE_GEM__MIT: "true"
5 | BUNDLE_GEM__TEST: "minitest"
6 | BUNDLE_RETRY: "3"
7 | BUNDLE_GITHUB__HTTPS: "true"
8 | BUNDLE_BUILD__MYSQL2: "--with-ldflags=-L<%= brew_prefix %>/opt/zstd/lib --with-opt-dir=<%= brew_prefix %>/opt/openssl"
9 | BUNDLE_GEM__RUBOCOP: "true"
10 | BUNDLE_AUTO_INSTALL: "true"
11 |
--------------------------------------------------------------------------------
/bin/release-notes:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | release = `gh release list`.lines.first[/\s(v[\d.]+)/, 1]
4 | note = `gh release view #{release}`
5 |
6 | regexp = /- Update rubocop.* requirement from .* \((#\d+)\) @\[?dependabot\b.*\n/
7 | prs = note.scan(regexp).flatten.sort
8 | consolidated = "- Update rubocop gems (#{prs.join(", ")}) @dependabot\n"
9 |
10 | edited = note.gsub(regexp) do |match|
11 | match.include?(prs.first) ? consolidated : ""
12 | end
13 |
14 | puts edited
15 |
--------------------------------------------------------------------------------
/bashrc.d/completion.bash:
--------------------------------------------------------------------------------
1 | if [[ $- == *i* ]]
2 | then
3 | if [ -f $BREW_PREFIX/etc/bash_completion ]; then
4 | . $BREW_PREFIX/etc/bash_completion
5 | fi
6 | # Follow these instructions to enable Heroku CLI Autocomplete
7 | # https://devcenter.heroku.com/articles/heroku-cli-autocomplete
8 | CLI_ENGINE_AC_BASH_SETUP_PATH=~/Library/Caches/heroku/completions/bash_setup
9 | if [ -f $CLI_ENGINE_AC_BASH_SETUP_PATH ]; then
10 | . $CLI_ENGINE_AC_BASH_SETUP_PATH
11 | fi
12 | fi
13 |
14 | # Enable "s" alias to auto-complete just like "git switch"
15 | __git_complete s _git_switch
16 |
--------------------------------------------------------------------------------
/sublime/ColorHighlighter.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | "search_colors_in": {
3 | "all_content": {
4 | "enabled": true,
5 | "color_highlighters": {
6 | "color_scheme": {
7 | "enabled": true,
8 | "highlight_style": "filled"
9 | },
10 | "gutter_icons": {
11 | "enabled": false,
12 | },
13 | "phantoms": {
14 | "enabled": false,
15 | }
16 | }
17 | },
18 | "selection": {
19 | "enabled": false,
20 | },
21 | "hover": {
22 | "enabled": false,
23 | },
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/bashrc.d/env.bash:
--------------------------------------------------------------------------------
1 | export CLICOLOR=1
2 | export LSCOLORS=Dxxxxxxxxxxxxxxxxxxxxx
3 | export PAGER=/usr/bin/less
4 | export PSQL_PAGER="/usr/bin/less -S"
5 | export LESS=-RFX
6 | export HOMEBREW_INSTALL_BADGE=🍵
7 | export HOMEBREW_NO_ANALYTICS=1
8 | export PATH=$BREW_PREFIX/opt/grep/libexec/gnubin:$BREW_PREFIX/sbin:$PATH
9 | export RIPGREP_CONFIG_PATH=$HOME/.ripgreprc
10 | export NEXT_TELEMETRY_DISABLED=1
11 | export CUCUMBER_PUBLISH_QUIET=true
12 | export THOR_DIFF="colordiff -u"
13 |
14 | if [ $TERM_PROGRAM == 'Apple_Terminal' ]; then
15 | export EDITOR="subl -w"
16 | export BUNDLER_EDITOR=subl
17 | export GEM_EDITOR=subl
18 | else
19 | export EDITOR=vim
20 | fi
21 |
--------------------------------------------------------------------------------
/bashrc:
--------------------------------------------------------------------------------
1 | if [ -x /opt/homebrew/bin/brew ]; then
2 | BREW_PREFIX=/opt/homebrew
3 | else
4 | BREW_PREFIX=/usr/local
5 | fi
6 |
7 | export PATH=$BREW_PREFIX/bin:$PATH
8 |
9 | if [ -x $BREW_PREFIX/bin/mise ]; then
10 | eval "$(mise activate bash)"
11 | fi
12 |
13 | # Custom bashrc sources are stored in ~/.bashrc.d
14 | if [[ -d $HOME/.bashrc.d ]] ; then
15 | for config in "$HOME"/.bashrc.d/*.bash ; do
16 | . "$config"
17 | done
18 | fi
19 | unset -v config
20 |
21 | # Custom binaries are stored in ~/.bin
22 | if [ -d ~/.bin ]; then
23 | export PATH=~/.bin:$PATH
24 | fi
25 |
26 | if [ -x "$BREW_PREFIX/bin/zoxide" ]; then
27 | eval "$(zoxide init --cmd j bash)"
28 | fi
29 |
30 | unset -v BREW_PREFIX
31 |
--------------------------------------------------------------------------------
/liquidpromptrc:
--------------------------------------------------------------------------------
1 | LP_USER_ALWAYS=0
2 |
3 | LP_ENABLE_AWS_PROFILE=0
4 | LP_ENABLE_BATT=0
5 | LP_ENABLE_BZR=0
6 | LP_ENABLE_FOSSIL=0
7 | LP_ENABLE_FQDN=0
8 | LP_ENABLE_GIT=1
9 | LP_ENABLE_HG=0
10 | LP_ENABLE_JOBS=0
11 | LP_ENABLE_LOAD=0
12 | LP_ENABLE_PERM=0
13 | LP_ENABLE_PROXY=0
14 | LP_ENABLE_RUBY_VENV=0
15 | LP_ENABLE_RUNTIME=0
16 | LP_ENABLE_RUNTIME_BELL=0
17 | LP_ENABLE_SCREEN_TITLE=0
18 | LP_ENABLE_SHORTEN_PATH=1
19 | LP_ENABLE_SSH_COLORS=0
20 | LP_ENABLE_SUDO=0
21 | LP_ENABLE_SVN=0
22 | LP_ENABLE_TEMP=0
23 | LP_ENABLE_TIME=0
24 | LP_ENABLE_TITLE=0
25 | LP_ENABLE_VCS_ROOT=0
26 | LP_MARK_SHORTEN_PATH=…
27 | LP_PATH_KEEP=1
28 | LP_PATH_LENGTH=35
29 | LP_PATH_METHOD=truncate_chars_from_path_left
30 | LP_PATH_VCS_ROOT=1
31 |
--------------------------------------------------------------------------------
/bin/cci:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "cgi"
4 | require "open3"
5 |
6 | def open_circle_ci
7 | origin = capture("git remote get-url origin").chomp
8 | brand = origin.include?("bitbucket.org") ? "bitbucket" : "github"
9 | repo = origin[%r{(:|/)(.*?/.*?)\.git$}, 2]
10 | branch = capture("git branch --show-current").chomp
11 |
12 | url = "https://app.circleci.com/pipelines/#{brand}/#{repo}?branch=#{CGI.escape(branch)}"
13 | system "open", url
14 | end
15 |
16 | def capture(command)
17 | out_err, status = Open3.capture2e(command)
18 | return out_err if status.success?
19 |
20 | $stderr.puts
21 | $stderr.puts(out_err || "Could not execute #{command}")
22 | exit(1)
23 | end
24 |
25 | open_circle_ci
26 |
--------------------------------------------------------------------------------
/bin/git-ppr:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "pathname"
4 | require "shellwords"
5 |
6 | return unless system "git push -u origin head"
7 |
8 | project_root = Pathname.new(`git rev-parse --show-toplevel`.chomp)
9 | pr_template_path = project_root.join(".github/PULL_REQUEST_TEMPLATE.md")
10 | pr_body = pr_template_path.read if pr_template_path.exist?
11 |
12 | git_branch = `git branch --show-current`.chomp
13 | tracker_id = git_branch[/\d{8,}/]
14 |
15 | pr_body.sub!(%r{(pivotaltracker\.com/story/show/)\d+}, '\1' + tracker_id) if pr_body && tracker_id
16 |
17 | gh_command = +"gh pr create --web"
18 | gh_command << " --body #{pr_body.shellescape}" if pr_body
19 |
20 | exit Process.last_status.exitstatus unless system gh_command
21 |
--------------------------------------------------------------------------------
/config/git/ignore:
--------------------------------------------------------------------------------
1 | # Compiled source #
2 | ###################
3 | *.class
4 | *.o
5 | *.pyc
6 | *.so
7 | .sass-cache
8 |
9 | # Logs and databases #
10 | ######################
11 | *.log
12 | *.sqlite
13 | *.sqlite3
14 |
15 | # OS generated files #
16 | ######################
17 | .DS_Store
18 |
19 | # Swap files #
20 | ##############
21 | *.swp
22 |
23 | # Backups #
24 | ###########
25 | *.orig
26 |
27 | # Repositories #
28 | ################
29 | .hg
30 | .svn
31 |
32 | # Projects #
33 | ############
34 | *.tmproj
35 | *.pbxuser
36 | *.mode1v3
37 | __MACOSX
38 | .tm_properties
39 | .bundle/
40 | .pairs
41 | .powder
42 | *.sublime-project
43 | *.sublime-workspace
44 | .idea/
45 | .byebug_history
46 | .vscode/
47 | .envrc
48 | mise.local.toml
49 | .venv/
50 |
--------------------------------------------------------------------------------
/bin/every:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | puts(<<~USAGE) & exit if (%w[-h --help] & ARGV).any?
4 |
5 | Usage: every INTERVAL COMMAND...
6 |
7 | Run the given COMMAND forever (or until it fails), waiting INTERVAL between
8 | each execution. INTERVAL must be a number with unit, e.g.: 30s, 5m, 1h.
9 |
10 | Examples:
11 |
12 | # Print "hello" every five seconds
13 | every 5s echo hello
14 |
15 | # Fetch a web page every minute
16 | every 1m curl https://www.githubstatus.com/
17 |
18 | USAGE
19 |
20 | interval, *command = ARGV
21 |
22 | seconds = case interval
23 | when /\A\d+s\z/ then interval[/\d+/].to_i
24 | when /\A\d+m\z/ then interval[/\d+/].to_i * 60
25 | when /\A\d+h\z/ then interval[/\d+/].to_i * 60 * 60
26 | else raise ArgumentError, "First arg must be an interval (e.g. 30s)"
27 | end
28 |
29 | sleep seconds while system(*command)
30 |
--------------------------------------------------------------------------------
/bin/rails_generate_and_open:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # Passes arguments to `rails generate` and opens the resulting files
4 | # in Sublime. Take from here:
5 | # http://www.davidverhasselt.com/auto-open-rails-generator-files/
6 |
7 | require "pty"
8 |
9 | def extract_created_files(lines)
10 | lines.map do |line|
11 | command, file = colorless(line).split
12 | file if command == "create"
13 | end.compact.reverse
14 | end
15 |
16 | def colorless(str)
17 | str.gsub(/\033\[\d+m/, "")
18 | end
19 |
20 | command = %w[bin/rails generate] + ARGV
21 | lines = []
22 |
23 | # Use PTY to force Thor to output colored text
24 | PTY.spawn(*command) do |r, _, _|
25 | while (line = r.gets)
26 | puts line
27 | lines << line
28 | end
29 | end
30 |
31 | files = extract_created_files(lines)
32 |
33 | if files.any?
34 | puts "\nOpening #{files.length} file#{files.empty? ? "" : "s"}…"
35 | exec("subl", *files)
36 | end
37 |
--------------------------------------------------------------------------------
/bin/sweep:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "date"
4 | THIRTY_DAYS_AGO = Date.today - 30
5 |
6 | def list_files
7 | Dir.entries(".").reject { |file| file.start_with?(".") }
8 | end
9 |
10 | def last_access_date(file)
11 | return File.ctime(file).to_date if File.exist?(file) && (File.file?(file) || File.symlink?(file))
12 | return nil unless File.directory?(file)
13 |
14 | Dir.chdir(file) do
15 | access_dates = list_files.each_with_object([]) do |child, dates|
16 | dates << last_access_date(child) unless child.start_with?(".")
17 | end
18 | access_dates.compact.max
19 | end
20 | end
21 |
22 | ARGV << "." if ARGV.empty?
23 |
24 | ARGV.each do |dir|
25 | Dir.chdir(dir) do
26 | list_files.each do |file|
27 | next if file.start_with?(".")
28 |
29 | date = last_access_date(file)
30 | next if date.nil? || date > THIRTY_DAYS_AGO
31 |
32 | puts "Trashing #{file}"
33 | system "trash", file
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/bashrc.d/aliases.bash:
--------------------------------------------------------------------------------
1 | alias b='bundle exec'
2 | alias bup='bundleup'
3 | alias cat='bat'
4 | # Change to the root level directory the current git repository
5 | alias cdg='cd $(git rev-parse --show-toplevel || pwd)'
6 | alias diff=colordiff
7 | alias get='git'
8 | alias hl='heroku local -p 3000 $(test -f Procfile.dev && echo "-f Procfile.dev")'
9 | alias httpserve='ruby -run -e httpd -- --port=8888'
10 | alias l='eza'
11 | alias la='eza -la --git --icons'
12 | alias ll='eza -l --git --icons'
13 | alias ls='eza'
14 | alias n='npx --no-install'
15 | alias ras='bin/rails server -b 0.0.0.0 -p 3000'
16 | alias s="git sw"
17 | alias secret="ruby -rsecurerandom -e 'puts SecureRandom.hex(64)'"
18 | alias top='top -s 5 -o cpu -stats pid,user,command,cpu,rsize,vsize,threads,state'
19 | alias uuid="ruby -rsecurerandom -e 'puts SecureRandom.uuid'"
20 | alias yi="yarn install --check-files"
21 | alias ys="yarn start"
22 | alias yup="yarn upgrade-interactive"
23 |
24 | function mcdir() {
25 | mkdir -p $1 && cd $1
26 | }
27 |
28 | function gb() {
29 | gh pr view --web $1 > /dev/null 2>&1 || gh repo view --web
30 | }
31 |
--------------------------------------------------------------------------------
/sublime/Default (OSX).sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | { "keys": ["super+\\"], "command": "toggle_side_bar" },
3 | { "keys": ["super+shift+\\"], "command": "reveal_in_side_bar"},
4 | { "keys": ["super+shift+w"], "command": "close_other_tabs" },
5 | { "keys": ["super+backspace"], "command": "run_macro_file", "args": {"file": "res://Packages/Default/Delete Line.sublime-macro"} },
6 | { "keys": ["super+shift+r"], "command": "goto_symbol_in_project" },
7 | { "keys": ["super+b"], "command": "goto_definition" },
8 | { "keys": ["super+shift+t"], "command": "reopen_last_file" },
9 |
10 | // DashDoc package
11 | {
12 | "keys": ["ctrl+h"],
13 | "command": "dash_doc"
14 | },
15 |
16 | // Disable shortcut that conflicts with Dash macOS app
17 | {
18 | "keys": ["super+shift+space"],
19 | "command": "pass"
20 | },
21 |
22 | // Make paste_and_indent the default behavior
23 | { "keys": ["super+shift+v"], "command": "paste" },
24 | { "keys": ["super+v"], "command": "paste_and_indent" },
25 |
26 | // Doc-Block package
27 | { "keys": ["super+shift+j"], "command": "jsdocs_join", "context":
28 | [
29 | { "key": "selector", "operator": "equal", "operand": "comment.block" }
30 | ]
31 | },
32 | { "keys": ["super+shift+j"], "command": "jsdocs_join", "context":
33 | [
34 | { "key": "selector", "operator": "equal", "operand": "comment.line" }
35 | ]
36 | }
37 | ]
38 |
--------------------------------------------------------------------------------
/bin/git-pluck:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "shellwords"
3 |
4 | # Colorization from http://stackoverflow.com/a/11482430
5 | class String
6 | def colorize(color_code)
7 | "\e[#{color_code}m#{self}\e[0m"
8 | end
9 |
10 | def red
11 | colorize(31)
12 | end
13 |
14 | def cyan
15 | colorize(36)
16 | end
17 | end
18 |
19 | def pluck(git_dir, sha)
20 | commands = [
21 | "git fetch --quiet --no-tags #{git_dir.shellescape}",
22 | "git cherry-pick #{sha.shellescape}"
23 | ]
24 |
25 | commands.each do |cmd|
26 | puts cmd.cyan
27 | system(cmd) || fail
28 | end
29 | end
30 |
31 | def usage_and_exit
32 | puts <<~USAGE
33 | git pluck
34 |
35 | Cherry-picks a commit identified by SHA from another Git repository
36 | into the current repository.
37 |
38 | Example:
39 |
40 | git pluck ../rails-starter ab1df43
41 | USAGE
42 |
43 | exit(2)
44 | end
45 |
46 | def fail(reason = nil)
47 | puts(reason.red) unless reason.nil?
48 | exit(1)
49 | end
50 |
51 | def find_repository(path)
52 | git_repo = [File.join(path, ".git"), path].find { |d| Dir.exist?(d) }
53 |
54 | git_repo || fail("#{path} does not exist")
55 | end
56 |
57 | if $PROGRAM_NAME == __FILE__
58 | usage_and_exit if ARGV.length != 2 || (%w[-h --help] & ARGV).any?
59 |
60 | repo = find_repository(ARGV[0])
61 | sha = ARGV[1]
62 |
63 | pluck(repo, sha)
64 | end
65 |
--------------------------------------------------------------------------------
/bin/defaults-install:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | # Dock
6 | defaults write com.apple.dock autohide -bool true
7 | defaults write com.apple.dock launchanim -bool false
8 | defaults write com.apple.dock orientation right
9 | defaults write com.apple.dock show-recents -bool false
10 | defaults write com.apple.dock tilesize -int 64
11 |
12 | # Screenshots
13 | defaults write com.apple.screencapture location ~/Downloads/
14 | defaults write com.apple.screencapture disable-shadow -bool true
15 | defaults write com.apple.screencapture show-thumbnail -bool false
16 |
17 | # Finder
18 | defaults write NSGlobalDomain AppleShowAllExtensions -bool true
19 | defaults write com.apple.finder FXPreferredViewStyle -string clmv
20 | defaults write com.apple.finder _FXSortFoldersFirst -bool true
21 | defaults write com.apple.finder FXRemoveOldTrashItems -bool true
22 | defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false
23 | defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool false
24 | defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool false
25 |
26 | # Enable ability to inspect WebKit views
27 | # https://blog.jim-nielsen.com/2022/inspecting-web-views-in-macos/
28 | defaults write NSGlobalDomain WebKitDeveloperExtras -bool true
29 | defaults write -g WebKitDeveloperExtras -bool YES
30 |
31 | # Menu bar
32 | defaults -currentHost write -globalDomain NSStatusItemSpacing -int 8
33 |
34 | killall Dock
35 | killall Finder
36 | killall SystemUIServer
37 |
--------------------------------------------------------------------------------
/bin/sup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "highline"
3 | HighLine.colorize_strings
4 |
5 | def run(host, cmd, exit_on_failure: true)
6 | ssh_cmd = ["ssh", config, with_user(host), cmd].compact.join(" ")
7 | puts ssh_cmd.yellow
8 | system(ssh_cmd).tap do |success|
9 | exit(1) if exit_on_failure && !success
10 | end
11 | end
12 |
13 | def with_user(host)
14 | return host if host.include?("@")
15 |
16 | "root@#{host}"
17 | end
18 |
19 | def config
20 | "-F .ssh/config" if File.file?(".ssh/config")
21 | end
22 |
23 | def ubuntu16?(host)
24 | !run(host, "type aptitude 2>/dev/null", exit_on_failure: false)
25 | end
26 |
27 | completed = []
28 |
29 | ARGV.each_with_index do |host, i|
30 | if i > 0
31 | puts "Next up: #{host}".red
32 | exit unless HighLine.new.agree("Proceed (y/n)? ".blue)
33 | end
34 |
35 | if ubuntu16?(host)
36 | run host, "sudo apt-get -y autoremove"
37 | run host, "sudo apt-get -y autoclean"
38 | run host, "sudo DEBIAN_FRONTEND=noninteractive apt-get -q -q -y update"
39 | run host,
40 | "sudo DEBIAN_FRONTEND=noninteractive apt-get -q -q " \
41 | '-o DPkg::options::="--force-confdef" ' \
42 | '-o DPkg::options::="--force-confold" dist-upgrade'
43 | else
44 | run host, "DEBIAN_FRONTEND=noninteractive sudo aptitude -q -q -y update"
45 | run host, "DEBIAN_FRONTEND=noninteractive sudo aptitude -q -q safe-upgrade"
46 | end
47 |
48 | run host, "sudo checkrestart -v"
49 |
50 | completed << host
51 | completed.each { |h| puts "✔ #{h}".green }
52 | end
53 |
--------------------------------------------------------------------------------
/bashrc.d/ruby.bash:
--------------------------------------------------------------------------------
1 | export PATH=".git/safe/../../bin:$PATH"
2 | export RUBY_CONFIGURE_OPTS="--with-openssl-dir=$BREW_PREFIX/opt/openssl@3"
3 |
4 | export TESTOPTS="--pride"
5 |
6 | # OS X has its own way of setting LANG, but only at the console.
7 | # By declaring here in .bashrc, daemons like Pow will also pick it up.
8 | export LANG=en_US.UTF-8
9 |
10 | # Use Homebrew's terminal-notifier, which is much faster than Ruby's.
11 | if [ -x $BREW_PREFIX/bin/terminal-notifier ]; then
12 | export TERMINAL_NOTIFIER_BIN=$BREW_PREFIX/bin/terminal-notifier
13 | fi
14 |
15 | # Shortcut for running `rails` or `rake`, based on a simple heuristic
16 | # to determine which command is appropriate.
17 | function r() {
18 | if [ $# -eq 0 ] || [ ! -x bin/rails ]; then
19 | if [ -x bin/rake ]; then
20 | bin/rake "$@"
21 | elif [ -f Gemfile.lock ]; then
22 | bundle exec rake "$@"
23 | else
24 | rake "$@"
25 | fi
26 | else
27 | bin/rails "$@"
28 | fi
29 | }
30 |
31 | # Shortcut to start the test watcher appropriate for the project
32 | function w() {
33 | if [ -x bin/mt ]; then
34 | bin/mt --watch "$@"
35 | else
36 | retest --notify "$@"
37 | fi
38 | }
39 |
40 | # Search all bundler dependencies for a given pattern
41 | # Courtesy of https://everydayrails.com/2018/06/11/bundler-shortcuts.html
42 | function bs() {
43 | rg "$1" $(bundle list --paths)
44 | }
45 |
46 | alias rgen="~/.bin/rails_generate_and_open"
47 | alias routes="bin/rails routes -g '^/(?!rails/active_storage|rails/action_mailbox|rails/conductor|(recede|resume|refresh)_historical_location).*$'"
48 |
49 | eval "$(tomo completion-script)"
50 |
--------------------------------------------------------------------------------
/bin/sci:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "json"
4 | require "open3"
5 |
6 | def open_semaphore_ci
7 | url = if (workflow_url = find_workflow_url_based_on_pr_check)
8 | workflow_url.sub(%r{/summary}, "").sub(/&report_id=[^&+]/, "")
9 | else
10 | subdomain, project = find_project_based_on_git_origin
11 | project_name = project.dig("metadata", "name")
12 | "https://#{subdomain}.semaphoreci.com/projects/#{project_name}"
13 | end
14 |
15 | system "open", url
16 | end
17 |
18 | def find_workflow_url_based_on_pr_check
19 | `gh pr checks 2> /dev/null`[/(https:\S+semaphoreci\.com\S+)/, 1]
20 | end
21 |
22 | def find_project_based_on_git_origin
23 | origin = capture("git remote get-url origin").chomp
24 | contexts_with_current_first.each_with_index do |context, index|
25 | system("sem context #{context}", exception: true) unless index.zero?
26 | projects = capture_json("sem get projects --verbose")
27 | project = projects.find { _1.dig("spec", "repository", "url") == origin }
28 |
29 | return [context.sub(/_semaphoreci_com$/, ""), project] unless project.nil?
30 | end
31 |
32 | raise "Couldn’t find Semaphore project for #{origin}"
33 | end
34 |
35 | def contexts_with_current_first
36 | capture("sem context").lines.sort.reverse.map { _1[/(\S+)$/, 1] }
37 | end
38 |
39 | def capture_json(command)
40 | raw_json = capture(command)[%r(^[\d/]+ [\d:]+ (\[\{.*?)$), 1]
41 | JSON.parse(raw_json)
42 | end
43 |
44 | def capture(command)
45 | out_err, status = Open3.capture2e(command)
46 | return out_err if status.success?
47 |
48 | $stderr.puts
49 | $stderr.puts(out_err || "Could not execute #{command}")
50 | exit(1)
51 | end
52 |
53 | open_semaphore_ci
54 |
--------------------------------------------------------------------------------
/completions/pgcli:
--------------------------------------------------------------------------------
1 | _pg_databases()
2 | {
3 | # -w was introduced in 8.4, https://launchpad.net/bugs/164772
4 | # "Access privileges" in output may contain linefeeds, hence the NF > 1
5 | COMPREPLY=( $( compgen -W "$( psql -AtqwlF $'\t' 2>/dev/null | \
6 | awk 'NF > 1 { print $1 }' )" -- "$cur" ) )
7 | }
8 |
9 | _pg_users()
10 | {
11 | # -w was introduced in 8.4, https://launchpad.net/bugs/164772
12 | COMPREPLY=( $( compgen -W "$( psql -Atqwc 'select usename from pg_user' \
13 | template1 2>/dev/null )" -- "$cur" ) )
14 | [[ ${#COMPREPLY[@]} -eq 0 ]] && COMPREPLY=( $( compgen -u -- "$cur" ) )
15 | }
16 |
17 | __pg_init_completion()
18 | {
19 | COMPREPLY=()
20 | _get_comp_words_by_ref cur prev words cword
21 | }
22 |
23 | _pgcli()
24 | {
25 | local cur prev words cword
26 | if declare -F _init_completions >/dev/null 2>&1; then
27 | _init_completion
28 | else
29 | __pg_init_completion
30 | fi
31 |
32 | case $prev in
33 | -h|--host)
34 | _known_hosts_real "$cur"
35 | return 0
36 | ;;
37 | -U|--user)
38 | _pg_users
39 | return 0
40 | ;;
41 | -d|--dbname)
42 | _pg_databases
43 | return 0
44 | ;;
45 | --help|-v|--version|-p|--port|-R|--row-limit)
46 | # all other arguments are noop with these
47 | return 0
48 | ;;
49 | esac
50 |
51 | case "$cur" in
52 | --*)
53 | # return list of available options
54 | COMPREPLY=( $( compgen -W '--host --port --user --password --no-password
55 | --single-connection --version --dbname --pgclirc --dsn
56 | --row-limit --help' -- "$cur" ) )
57 | [[ $COMPREPLY == *= ]] && compopt -o nospace
58 | return 0
59 | ;;
60 | -)
61 | # only complete long options
62 | compopt -o nospace
63 | COMPREPLY=( -- )
64 | return 0
65 | ;;
66 | *)
67 | # return list of available databases
68 | _pg_databases
69 | esac
70 | } &&
71 | complete -F _pgcli pgcli
72 |
--------------------------------------------------------------------------------
/bin/bucket:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "cgi"
3 | require "open3"
4 |
5 | # Colorization from http://stackoverflow.com/a/11482430
6 | class String
7 | def colorize(color_code)
8 | "\e[#{color_code}m#{self}\e[0m"
9 | end
10 |
11 | def red
12 | colorize(31)
13 | end
14 | end
15 |
16 | def usage_and_exit
17 | puts <<~USAGE
18 | bucket operates on the Bitbucket-hosted git repository that is in your
19 | working directory. It assumes that you have a remote named "origin" that
20 | points to Bitbucket.
21 |
22 | Usage:
23 |
24 | bucket show # Opens the Bitbucket website for this repository
25 | bucket # Shortcut for `bucket show`
26 | bucket compare # Opens the "compare" view for the current branch
27 | bucket pull # Creates a new pull request for the current branch
28 | bucket branches # Shows all un-merged feature branches
29 | USAGE
30 |
31 | exit(2)
32 | end
33 |
34 | def git(*args)
35 | out, err, status = Open3.capture3("git", *args)
36 | fail(err) unless status.success?
37 |
38 | out
39 | end
40 |
41 | def pull_url
42 | source = [repository, current_sha, current_branch].join(":")
43 | "#{show_url}/pull-request/new?source=#{CGI.escape(source)}"
44 | end
45 |
46 | def compare_url
47 | "#{show_url}/branch/#{CGI.escape(current_branch)}#diff"
48 | end
49 |
50 | def branches_url
51 | "#{show_url}/commits/featurebranches"
52 | end
53 |
54 | def show_url
55 | "https://bitbucket.org/#{repository}"
56 | end
57 |
58 | def repository
59 | @repo ||= git("remote", "-v")[%r{bitbucket.org(/|:)(.*)\.git \(push\)}, 2]
60 | @repo || fail("This doesn’t seem to be a Bitbucket repository.")
61 | end
62 |
63 | def current_branch
64 | @current_branch ||= git("branch")[/\s*\*\s*(.*)/, 1]
65 | end
66 |
67 | def current_sha
68 | @current_sha ||= git("rev-parse", current_branch)[/^\S{12}/]
69 | end
70 |
71 | def fail(reason = nil)
72 | puts reason.to_s.red unless reason.nil?
73 | exit(1)
74 | end
75 |
76 | if $PROGRAM_NAME == __FILE__
77 | usage_and_exit if (ARGV & %w[-h --help help]).any?
78 |
79 | command = ARGV.first || "show"
80 | url = send("#{command}_url")
81 |
82 | system("open", url)
83 | end
84 |
--------------------------------------------------------------------------------
/Brewfile.erb:
--------------------------------------------------------------------------------
1 | tap "heroku/brew"
2 |
3 | brew "bash"
4 | brew "bash-completion"
5 | brew "bat"
6 | brew "bison"
7 | brew "circleci"
8 | brew "cloc"
9 | brew "colordiff"
10 | brew "diff-so-fancy"
11 | brew "doggo"
12 | brew "eza"
13 | brew "fd"
14 | brew "gh"
15 | brew "git"
16 | brew "git-extras"
17 | brew "git-lfs"
18 | brew "git-who"
19 | brew "gmp"
20 | brew "heroku/brew/heroku"
21 | brew "httpie"
22 | brew "imagemagick"
23 | brew "jq"
24 | brew "libffi"
25 | brew "libsodium"
26 | brew "libxml2"
27 | brew "libxslt"
28 | brew "libyaml"
29 | brew "liquidprompt"
30 | brew "magic-wormhole"
31 | brew "mas"
32 | brew "mcfly"
33 | brew "mise"
34 | brew "mongosh"
35 | brew "mmv"
36 | brew "openssl"
37 | brew "openssl@3"
38 | brew "pgcli"
39 | brew "php"
40 | brew "postgresql@17", restart_service: true, link: true
41 | brew "readline"
42 | brew "redis", restart_service: true
43 | brew "ripgrep"
44 | brew "ruby-build"
45 | brew "rust"
46 | brew "shellcheck"
47 | brew "trash"
48 | brew "wget"
49 | brew "zoxide"
50 |
51 | cask "alfred"
52 | cask "bruno"
53 | cask "dash"
54 | cask "disk-arbitrator"
55 | cask "docker"
56 | cask "ente-auth"
57 | cask "figma"
58 | cask "flux"
59 | cask "font-inter"
60 | cask "font-meslo-lg-nerd-font"
61 | cask "gitify"
62 | cask "google-chrome"
63 | cask "hot"
64 | cask "iina"
65 | cask "logitech-camera-settings"
66 | cask "mitmproxy"
67 | cask "monitorcontrol"
68 | cask "moom"
69 | cask "ngrok"
70 | cask "notunes"
71 | cask "pika"
72 | cask "pixelsnap"
73 | cask "pop"
74 | cask "slack"
75 | cask "sublime-text"
76 | cask "visual-studio-code"
77 | cask "xbar"
78 | cask "zoom"
79 |
80 | mas "Amphetamine", id: 937984704
81 | mas "Aware", id: 1082170746
82 | mas "Barbee", id: 1548711022
83 | mas "BreakTime", id: 427475982
84 | mas "Fantastical 2", id: 975937182
85 | mas "Gifski", id: 1351639930
86 | mas "Ka-Block!", id: 1335413823
87 | mas "Keynote", id: 409183694
88 | mas "Marked 2", id: 890031187
89 | mas "Numbers", id: 409203825
90 | mas "Pages", id: 409201541
91 | mas "Parcel - Delivery Tracking", id: 639968404
92 | mas "Pixelmator Pro", id: 1289583905
93 | mas "Reeder 5.", id: 1529448980
94 | mas "The Unarchiver", id: 425424353
95 | mas "Things3", id: 904280696
96 | mas "Timery for Toggle", id: 1425368544
97 | mas "Trello", id: 1278508951
98 | mas "Xcode", id: 497799835
99 |
--------------------------------------------------------------------------------
/bin/sp:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | if ARGV.length > 1 || ARGV.grep(/(-h|--help)/).any?
4 | puts <<~USAGE
5 | Sublime Text open as project
6 |
7 | Usage: sp [DIRECTORY]
8 |
9 | Opens DIRECTORY in Sublime Text as a project, creating a new .sublime-project
10 | descriptor if one does not already exist.
11 |
12 | If a .sublime-project file already exists in DIRECTORY, uses that as the
13 | project configuration by passing it to `subl --project`.
14 |
15 | Otherwise creates a new, empty .sublime-project file in DIRECTORY and names it
16 | the same as DIRECTORY. For example, `sp my-project` will create the file
17 | my-project/my-project.sublime-project and open it in Sublime Text.
18 |
19 | If DIRECTORY is omitted, the current and all parent directories will be
20 | searched for an existing .sublime-project. If one can't be found, a new
21 | project will be created in the current directory.
22 |
23 | The `subl` command must be installed and available in the PATH.
24 | USAGE
25 | exit
26 | end
27 |
28 | def find_project_directory
29 | curr = start = Dir.pwd
30 | until Dir["*.sublime-project"].any? || curr.nil?
31 | Dir.chdir("..")
32 | curr = curr == Dir.pwd ? nil : Dir.pwd
33 | end
34 | curr
35 | ensure
36 | Dir.chdir(start)
37 | end
38 |
39 | directory = File.expand_path(ARGV.first || find_project_directory || ".", ".")
40 | name = File.basename(directory)
41 | name = "root" if name =~ %r{/}
42 | project = File.join(directory, "#{name}.sublime-project")
43 |
44 | unless File.exist?(project)
45 | if (glob = Dir[File.join(directory, "*.sublime-project")]).any?
46 | project = glob.first
47 | else
48 | File.write(project, <<~JSON)
49 | {
50 | "folders": [
51 | {
52 | "path": ".",
53 | "folder_exclude_patterns": ["build", "public/assets", "public/packs"],
54 | "file_exclude_patterns": ["yarn.lock", "yarn-error.log"]
55 | }
56 | ],
57 | "settings": {
58 | "js_prettier": {
59 | "auto_format_on_save": true,
60 | "auto_format_on_save_excludes": ["*.md"]
61 | },
62 | "SublimeLinter.linters.scss.@disable": true,
63 | "SublimeLinter.linters.rubocop.use_bundle_exec": false
64 | }
65 | }
66 | JSON
67 | end
68 | end
69 |
70 | puts "Opening #{project}"
71 | exec("subl", "--project", project)
72 |
--------------------------------------------------------------------------------
/bin/gemm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "open3"
4 |
5 | def run(command)
6 | out_err, status = Open3.capture2e(command)
7 | return out_err if status.success?
8 |
9 | $stderr.puts
10 | $stderr.puts(out_err || "Could not execute #{command}")
11 | exit(1)
12 | end
13 |
14 | def choose(question, choices)
15 | puts
16 | choices.each_with_index do |choice, i|
17 | puts "#{i + 1}. #{choice}"
18 | end
19 | print "#{question} [1-#{choices.length}]? "
20 | index = [$stdin.gets.to_i, 1].max
21 | choices[[index, choices.length].min - 1]
22 | end
23 |
24 | def gemfile_groups
25 | File.read("Gemfile").scan(/^group\s*\(?(.*)\)?\s+do\s*$/).map(&:first)
26 | end
27 |
28 | def version_specs(version)
29 | specs = [version]
30 | parts = version.split(".")
31 | parts.length.downto(2).each do |limit|
32 | specs << "~> #{parts[0...limit].join(".")}"
33 | end
34 | specs.reverse
35 | end
36 |
37 | ARGV.each do |gem|
38 | if File.read("Gemfile") =~ /^gem "#{gem}"/
39 | puts "#{gem} is already in the Gemfile!"
40 | exit 1
41 | end
42 |
43 | print "Looking for #{gem}..."
44 | version = Gem.latest_version_for(gem).to_s
45 | raise "Could not find #{gem}" if version.empty?
46 |
47 | puts "\rLooking for #{gem}... Found it! (#{version})"
48 | group = gemfile_groups.any? ? choose("Where do you want it", [""] + gemfile_groups) : ""
49 | spec = choose("Version specification", [""] + version_specs(version))
50 |
51 | insert = %(gem "#{gem}")
52 | insert << %(, "#{spec}") unless spec == ""
53 | insert << "\n"
54 | insert = " #{insert}" unless group == ""
55 |
56 | puts
57 | ok = []
58 | ok << "group #{group} do" unless group == ""
59 | ok << insert.chomp
60 | ok << "end" unless group == ""
61 | puts ok.join("\n")
62 | print "Does this look okay? [Yn]? "
63 | exit unless $stdin.gets =~ /^($|y)/i
64 |
65 | in_group = nil
66 | lines = (File.readlines("Gemfile") + [""]).each_cons(2).with_object([]) do |(curr, succ), out|
67 | out << curr
68 | in_group = "" if !in_group && succ =~ /^#?\s*(gem\s|group)/
69 | in_group = $1 if curr =~ /^group\s*\(?(.*)\)?\s+do\s*$/
70 | match = succ =~ /^\s*gem\s/ && insert && insert.tr("-_", "") < succ.tr("-_", "")
71 | bottom = (out.length > 1 && succ.strip.empty?) || (in_group != "" && succ =~ /^end/)
72 |
73 | next unless in_group == group && insert && (match || bottom)
74 |
75 | out << insert
76 | insert = nil
77 | end
78 | File.write("Gemfile", lines.join)
79 |
80 | puts
81 | print "Updating Gemfile..."
82 |
83 | run("bundle install")
84 |
85 | puts "\rUpdating Gemfile... DONE"
86 | end
87 |
--------------------------------------------------------------------------------
/bin/git-trim:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | RESERVED_BRANCHES = [
4 | /^acceptance$/,
5 | /^develop$/,
6 | /^development$/,
7 | /^gh-pages$/,
8 | /^HEAD$/,
9 | /^hotfix.*$/,
10 | /^main$/,
11 | /^master$/,
12 | /^.*production$/,
13 | /^staging$/,
14 | /^release.*$/,
15 | /^stable.*$/,
16 | /^trunk$/
17 | ].freeze
18 |
19 | require "shellwords"
20 |
21 | def trim
22 | git "fetch --prune --quiet"
23 |
24 | merged_local_branches.each { |b| git "branch -D #{b.shellescape}" }
25 | orphaned_branches.each { |b| git "branch -D #{b.shellescape}" }
26 |
27 | branches = merged_remote_branches
28 | if branches.any? && confirm_delete("Is it okay to delete the following merged #{"remote".yellow} branches?", branches)
29 | branches.each { |b| git "push origin :#{b.shellescape}" }
30 | end
31 | puts
32 | puts "All done! ✨".green
33 | puts "The unmerged and reserved branches listed below have been left as is."
34 | puts
35 | system "git branch -vv"
36 | puts
37 | end
38 |
39 | def merged_local_branches
40 | branches = git("branch --format '%(refname:short)' --merged HEAD", echo: false).lines(chomp: true)
41 | excluding_reserved(branches)
42 | end
43 |
44 | def orphaned_branches
45 | git("for-each-ref --format '%(refname:short) %(upstream:track)' refs/heads", echo: false)
46 | .scan(/^(.+) \[gone\]$/)
47 | .flatten
48 | end
49 |
50 | def merged_remote_branches
51 | branches = git("branch -r --format '%(refname:short)' --merged HEAD", echo: false)
52 | .scan(%r{^origin/(.+)})
53 | .flatten
54 | excluding_reserved(branches)
55 | end
56 |
57 | def excluding_reserved(branches)
58 | @current_branch ||= git("branch --show-current", echo: false).chomp
59 | branches.reject do |branch|
60 | branch == @current_branch || RESERVED_BRANCHES.any? { |pattern| branch.match?(pattern) }
61 | end
62 | end
63 |
64 | def confirm_delete(message, branches)
65 | puts
66 | puts message
67 | puts
68 | branches.each { |branch| puts " origin/#{branch.blue}" }
69 | puts
70 | print 'Really delete (type "yes" to confirm)? '.yellow
71 | gets.chomp == "yes"
72 | end
73 |
74 | def git(command, echo: true)
75 | command = "git #{command}"
76 | puts ">>>> #{command}".grey if echo
77 | result = `#{command}`
78 | return result if Process.last_status.success?
79 |
80 | warn "Failed: #{command}".red
81 | exit 1
82 | end
83 |
84 | class String
85 | def colorize(color_code)
86 | "\e[#{color_code}m#{self}\e[0m"
87 | end
88 |
89 | def blue
90 | colorize(34)
91 | end
92 |
93 | def red
94 | colorize(31)
95 | end
96 |
97 | def green
98 | colorize(32)
99 | end
100 |
101 | def grey
102 | colorize(90)
103 | end
104 |
105 | def yellow
106 | colorize(33)
107 | end
108 | end
109 |
110 | trim if $PROGRAM_NAME == __FILE__
111 |
--------------------------------------------------------------------------------
/terminalizer/config.yml:
--------------------------------------------------------------------------------
1 | # Specify a command to be executed
2 | # like `/bin/bash -l`, `ls`, or any other commands
3 | # the default is bash for Linux
4 | # or powershell.exe for Windows
5 | command: null
6 |
7 | # Specify the current working directory path
8 | # the default is the current working directory path
9 | cwd: null
10 |
11 | # Export additional ENV variables
12 | env:
13 | recording: true
14 |
15 | # Explicitly set the number of columns
16 | # or use `auto` to take the current
17 | # number of columns of your shell
18 | cols: 80
19 |
20 | # Explicitly set the number of rows
21 | # or use `auto` to take the current
22 | # number of rows of your shell
23 | rows: 30
24 |
25 | # Amount of times to repeat GIF
26 | # If value is -1, play once
27 | # If value is 0, loop indefinitely
28 | # If value is a positive number, loop n times
29 | repeat: 0
30 |
31 | # Quality
32 | # 1 - 100
33 | quality: 100
34 |
35 | # Delay between frames in ms
36 | # If the value is `auto` use the actual recording delays
37 | frameDelay: auto
38 |
39 | # Maximum delay between frames in ms
40 | # Ignored if the `frameDelay` isn't set to `auto`
41 | # Set to `auto` to prevent limiting the max idle time
42 | maxIdleTime: 2000
43 |
44 | # The surrounding frame box
45 | # The `type` can be null, window, floating, or solid`
46 | # To hide the title use the value null
47 | # Don't forget to add a backgroundColor style with a null as type
48 | frameBox:
49 | type: solid
50 | title: null
51 | style:
52 | backgroundColor: "#323232"
53 | border: 0px black solid
54 | # boxShadow: none
55 | # margin: 0px
56 |
57 | # Add a watermark image to the rendered gif
58 | # You need to specify an absolute path for
59 | # the image on your machine or a URL, and you can also
60 | # add your own CSS styles
61 | watermark:
62 | imagePath: null
63 | style:
64 | position: absolute
65 | right: 15px
66 | bottom: 15px
67 | width: 100px
68 | opacity: 0.9
69 |
70 | # Cursor style can be one of
71 | # `block`, `underline`, or `bar`
72 | cursorStyle: block
73 |
74 | # Font family
75 | # You can use any font that is installed on your machine
76 | # in CSS-like syntax
77 | fontFamily: "Menlo, Monaco, Lucida Console, Ubuntu Mono, Monospace"
78 |
79 | # The size of the font
80 | fontSize: 18
81 |
82 | # The height of lines
83 | lineHeight: 1
84 |
85 | # The spacing between letters
86 | letterSpacing: 0
87 |
88 | # Theme
89 | theme:
90 | background: "transparent"
91 | foreground: "#fafafa"
92 | cursor: "#c7c7c7"
93 | black: "#2f2f2f"
94 | red: "#d24938"
95 | green: "#8dcb3c"
96 | yellow: "#d5c45a"
97 | blue: "#95c1e1"
98 | magenta: "#d24d96"
99 | cyan: "#3fbbc3"
100 | white: "#e2ded1"
101 | brightBlack: "#595959"
102 | brightRed: "#e65c4b"
103 | brightGreen: "#a3dd49"
104 | brightYellow: "#ebdc70"
105 | brightBlue: "#b4d9f4"
106 | brightMagenta: "#e768ae"
107 | brightCyan: "#58c5cd"
108 | brightWhite: "#f9f7ec"
109 |
--------------------------------------------------------------------------------
/sublime/Preferences.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | "ignored_packages":
3 | [
4 | "Vintage",
5 | ],
6 | "theme": "Adaptive.sublime-theme",
7 | "color_scheme": "Packages/Dracula Color Scheme/Dracula.tmTheme",
8 | "auto_complete_commit_on_tab": true,
9 | "caret_extra_bottom": 0,
10 | "caret_extra_top": 3,
11 | "caret_style": "smooth",
12 | "control_character_style": "names",
13 | "enable_telemetry": false,
14 | "ensure_newline_at_eof_on_save": true,
15 | "file_exclude_patterns":
16 | [
17 | ".gitkeep",
18 | ".keep",
19 | "*.pyc",
20 | "*.pyo",
21 | "*.exe",
22 | "*.dll",
23 | "*.obj",
24 | "*.o",
25 | "*.a",
26 | "*.lib",
27 | "*.so",
28 | "*.dylib",
29 | "*.ncb",
30 | "*.sdf",
31 | "*.suo",
32 | "*.pdb",
33 | "*.idb",
34 | ".DS_Store",
35 | "*.class",
36 | "*.psd",
37 | "*.db",
38 | "*.sublime-workspace",
39 | ".byebug_history"
40 | ],
41 | "folder_exclude_patterns":
42 | [
43 | ".svn",
44 | ".git",
45 | ".hg",
46 | "CVS",
47 | "tmp",
48 | ".bundle",
49 | "log",
50 | ".sass-cache",
51 | "coverage",
52 | "node_modules",
53 | ".Trash",
54 | ".Trash-*",
55 | ".ruby-lsp"
56 | ],
57 | "font_face": "Menlo",
58 | "font_options":
59 | [
60 | "ss07"
61 | ],
62 | "font_size": 14,
63 | "hide_new_tab_button": true,
64 | "highlight_line": true,
65 | "highlight_modified_tabs": true,
66 | "line_padding_top": 4,
67 | "line_padding_bottom": 1,
68 | "rulers":
69 | [
70 | 120
71 | ],
72 | "save_on_focus_lost": true,
73 | "spell_check": true,
74 | "tab_size": 2,
75 | "translate_tabs_to_spaces": true,
76 | "trim_trailing_white_space_on_save": "all",
77 | "added_words":
78 | [
79 | "frontmatter",
80 | "rubocop",
81 | "Bundler",
82 | "binstub",
83 | "bundler",
84 | "binstubs",
85 | "rubygems",
86 | "Gemfile",
87 | "localhost",
88 | "http",
89 | "capybara",
90 | "rspec",
91 | "selectable",
92 | "Minitest",
93 | "Nextgen",
94 | "initializers",
95 | "serializer",
96 | "initializer",
97 | "subclasses",
98 | "codebases",
99 | "accessor",
100 | "Influencer",
101 | "Brictson",
102 | "middleware",
103 | "monorepo",
104 | "inlined",
105 | "Uncomment",
106 | "unhandled",
107 | "interdependencies",
108 | ],
109 | "ignored_words":
110 | [
111 | "cjs",
112 | "css",
113 | "env",
114 | "fileutils",
115 | "frontend",
116 | "init",
117 | "npm",
118 | "open3",
119 | "rb",
120 | "securerandom",
121 | "shellwords",
122 | "stylelint",
123 | "stylelintrc",
124 | "stylesheets",
125 | "tmp",
126 | "tmpdir",
127 | "usr",
128 | ],
129 | "index_files": true,
130 | }
131 |
--------------------------------------------------------------------------------
/gitconfig.erb:
--------------------------------------------------------------------------------
1 | [advice]
2 | skippedCherryPicks = false
3 |
4 | [alias]
5 | amend = commit --amend --reuse-message=HEAD
6 | bl = blame -C
7 | br = branch
8 | branches = for-each-ref --sort=-committerdate --format=\"%(color:blue)%(authordate:relative)\t%(color:red)%(authorname)\t%(color:white)%(color:bold)%(refname:short)\" refs/remotes
9 | chp = cherry-pick
10 | ci = commit
11 | co = checkout
12 | di = diff
13 | dci = duet-commit
14 | drv = duet-revert
15 | dmg = duet-merge
16 | drb = rebase -i --exec 'git duet-commit --amend'
17 | fixup = commit --amend -c HEAD
18 | hist = log --pretty=format:\"%C(yellow bold)%h%Creset %C(red)%ad%Creset | %s%C(green bold)%d%Creset %C(blue)[%an]%Creset\" --graph --date=short
19 | ignored-files = ls-files --others -i --exclude-standard
20 | l = log --oneline --decorate --graph
21 | ll = log --pretty=medium --decorate --graph --stat
22 | ls = log --name-only --oneline
23 | modified-files = ls-files -m
24 | mru = for-each-ref --sort=-committerdate --count=10 refs/heads/ --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(color:red)%(objectname:short)%(color:reset) - %(contents:subject) - %(authorname) (%(color:green)%(committerdate:relative)%(color:reset))'
25 | oops = reset --hard 'HEAD@{1}'
26 | patch = !git --no-pager diff --no-color
27 | pt = !"git pull && git trim"
28 | pushf = push --force-with-lease
29 | pushu = push -u origin head
30 | ru = reset --hard '@{u}'
31 | st = status -s -b
32 | sw = switch
33 | timeline = log --graph --branches --pretty=oneline --decorate
34 | trust = "!sh -c 'mkdir -p .git/safe && hash -r'"
35 | unmerged = branch --sort=committerdate --no-merged
36 | unstage = reset HEAD
37 | untracked-files = ls-files -o --exclude-standard
38 |
39 | [branch]
40 | sort = -committerdate
41 |
42 | [color]
43 | status = auto
44 | diff = auto
45 | ui = true
46 |
47 | [color "branch"]
48 | current = red reverse
49 | local = blue
50 | remote = green
51 |
52 | [color "diff"]
53 | meta = 227
54 | frag = magenta bold
55 | old = red bold
56 | new = green bold
57 | plain = white
58 | commit = 227 bold
59 | whitespace = red reverse
60 |
61 | [color "diff-highlight"]
62 | oldNormal = red bold
63 | oldHighlight = red bold 52
64 | newNormal = green bold
65 | newHighlight = green bold 22
66 |
67 | [color "status"]
68 | added = yellow
69 | changed = green
70 | untracked = cyan
71 |
72 | [commit]
73 | cleanup = scissors
74 | gpgsign = true
75 | verbose = true
76 |
77 | [core]
78 | safecrlf = warn
79 | autocrlf = input
80 | pager = diff-so-fancy | less --tabs=4 -RFX
81 |
82 | [diff]
83 | algorithm = histogram
84 | noprefix = true
85 | renames = true
86 |
87 | [gpg]
88 | format = ssh
89 |
90 | [help]
91 | autocorrect = prompt
92 |
93 | [init]
94 | defaultBranch = main
95 |
96 | [log]
97 | follow = true
98 |
99 | [merge]
100 | tool = opendiff
101 |
102 | [pull]
103 | ff = only
104 |
105 | [push]
106 | default = simple
107 |
108 | [rebase]
109 | autostash = true
110 | instructionFormat = %s [%an]
111 | updateRefs = true
112 |
113 | [rerere]
114 | enabled = true
115 | autoupdate = true
116 |
117 | [tag]
118 | sort = version:refname
119 |
120 | [url "git@github.com:"]
121 | insteadOf = https://github.com/
122 | insteadOf = git://github.com/
123 |
124 | [user]
125 | name = <%= prompt("Your Full Name") %>
126 | email = <%= prompt("Your Email") %>
127 | signingkey = <%= prompt("SSH Public Key") %>
128 |
--------------------------------------------------------------------------------
/bin/outdated-projects:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/inline"
4 | require "date"
5 | require "json"
6 | require "pstore"
7 | require "time"
8 |
9 | gemfile do
10 | source "https://rubygems.org"
11 | gem "chronic"
12 | gem "faraday"
13 | gem "faraday-net_http_persistent"
14 | gem "faraday-retry"
15 | gem "nokogiri"
16 | gem "tty-progressbar"
17 | end
18 |
19 | PSTORE = PStore.new(File.expand_path("~/.cache/outdated-projects"))
20 |
21 | Faraday.default_connection = Faraday.new do |config|
22 | retry_options = {
23 | max: 5,
24 | interval: 0.5,
25 | interval_randomness: 0.5,
26 | backoff_factor: 2,
27 | retry_statuses: [429],
28 | methods: [:get]
29 | }
30 | config.response :json
31 | config.response :raise_error
32 | config.request :retry, retry_options
33 | config.adapter :net_http_persistent
34 | end
35 |
36 | class Project
37 | class << self
38 | def all
39 | gems = Faraday.get("https://rubygems.org/api/v1/owners/mattbrictson/gems.json").body
40 | gems.map { |g| new(g) }
41 | end
42 | end
43 |
44 | attr_reader :name
45 |
46 | def initialize(gem_data)
47 | @name = gem_data["name"]
48 | @gem_data = gem_data
49 | end
50 |
51 | def days_since_release
52 | release_date && [Date.today - release_date, 0].max.to_i
53 | end
54 |
55 | def needs_release?
56 | return true if days_since_release.nil?
57 |
58 | stale = version.start_with?("0") ? 7 : 28
59 | days_since_release >= stale && new_commits?
60 | end
61 |
62 | def release_date
63 | local_date = latest_release_html.css("local-time").first&.public_send(:[], "datetime")
64 | relative_date = latest_release_html.css("relative-time").first&.public_send(:[], "datetime")
65 | date = relative_date || local_date
66 | date && Chronic.parse(date).to_date
67 | end
68 |
69 | def releases_url
70 | "https://github.com/#{github_repo}/releases"
71 | end
72 |
73 | def snooze!
74 | PSTORE.transaction { PSTORE[name] = latest_unreleased_commit_at&.to_s }
75 | end
76 |
77 | private
78 |
79 | attr_reader :gem_data
80 |
81 | def version
82 | gem_data["version"]
83 | end
84 |
85 | def new_commits?
86 | return false if latest_unreleased_commit_at.nil?
87 |
88 | snooze_date.nil? || latest_unreleased_commit_at > snooze_date
89 | end
90 |
91 | def snooze_date
92 | date = PSTORE.transaction { PSTORE[name] }
93 | date && Date.parse(date)
94 | end
95 |
96 | def latest_release_html
97 | @_latest_release_html ||= Nokogiri::HTML(Faraday.get(releases_url).body)
98 | end
99 |
100 | def latest_unreleased_commit_at
101 | return nil if unreleased_commits_html.nil?
102 |
103 | date = unreleased_commits_html.text[/commits on (.*)/i, 1]
104 | Chronic.parse(date).to_date
105 | end
106 |
107 | def unreleased_commits_html
108 | @_unreleased_commits_html ||= begin
109 | url = "https://github.com/#{github_repo}/compare/v#{version}...#{default_branch}"
110 | doc = Nokogiri::HTML(Faraday.get(url).body)
111 | doc.css("#commits_bucket .TimelineItem.pb-2").last
112 | end
113 | end
114 |
115 | def default_branch
116 | @_default_branch ||= begin
117 | branches = Faraday.get("https://github.com/#{github_repo}/branches.json").body
118 | branches.dig("payload", "branches", "default", "name")
119 | end
120 | end
121 |
122 | def github_repo
123 | keys = %w[source_code_uri homepage_uri changelog_uri]
124 | urls = gem_data.values_at(*keys).compact
125 | repos = urls.map { |u| u[%r{github.com/((?:[^/]+)/(?:[^/]+))}, 1] }
126 | repos.compact.first
127 | end
128 | end
129 |
130 | if $PROGRAM_NAME == __FILE__
131 | all_projects = Project.all
132 | needing_release = []
133 |
134 | bar = TTY::ProgressBar.new("Checking projects… [:bar]", total: all_projects.size)
135 | all_projects.each do |proj|
136 | needing_release << proj if proj.needs_release?
137 | bar.advance(1)
138 | end
139 |
140 | if needing_release.empty?
141 | puts "All projects are up to date!"
142 | else
143 | puts "The following projects need releasing:"
144 | needing_release.each do |proj|
145 | puts [proj.name.ljust(28), proj.days_since_release || "n/a", "days old"].join(" ")
146 | system("open", proj.releases_url)
147 | print "Snooze? (y/N) "
148 | proj.snooze! if $stdin.gets.match?(/\Ay/i)
149 | end
150 | end
151 | end
152 |
--------------------------------------------------------------------------------
/bin/git-squash:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "English"
4 | require "shellwords"
5 | require "tempfile"
6 |
7 | def usage_and_exit(status = 0)
8 | out = status == 0 ? $stdout : $stderr
9 |
10 | out.puts <<~USAGE.gsub(/^/, " ")
11 |
12 | Usage: git squash BASE_BRANCH
13 |
14 | Rewrites the history of the current feature branch by squashing it down to a
15 | single commit. Before squashing, BASE_BRANCH will be merged into this branch
16 | in order to bring it up to date. If there are merge conflicts, the squash
17 | will be aborted.
18 |
19 | The resulting squashed commit will have a nice commit message based on the
20 | feature branch name and the messages of the commits being squashed. You will
21 | be given a chance to revise the message before it is committed.
22 |
23 | Example:
24 |
25 | Let's say we are working in a branch `features/improve-webpacker-support`
26 | that was branched off of `main` and has the following commits:
27 |
28 | * 1393233 Document the new yarn:install task
29 | * f165c7a Allow `yarn install` flags to be customized
30 | * ea35e0f Explicitly run `yarn install` per webpacker docs
31 | * 6c46003 Include public/packs in linked_dirs by default
32 |
33 | If we run:
34 |
35 | $ git squash main
36 |
37 | This rewrites the history of our `features/improve-webpacker-support` branch
38 | to now be a single commit:
39 |
40 | * 932084e Feature: improve webpacker support
41 |
42 | And the commit message is:
43 |
44 | Feature: improve webpacker support
45 |
46 | * Include public/packs in linked_dirs by default
47 | * Explicitly run `yarn install` per webpacker docs
48 | * Allow `yarn install` flags to be customized
49 | * Document the new yarn:install task
50 |
51 | If we don't like the results of the squash, we can always "undo":
52 |
53 | $ git reset --hard 1393233
54 |
55 | USAGE
56 |
57 | exit(status)
58 | end
59 |
60 | def squash(base_branch, force: false)
61 | require_feature_branch! unless force
62 | head_sha = capture!("git rev-parse --verify --short HEAD").chomp
63 |
64 | clean_merge!(base_branch)
65 | msg_path = write_commit_message_to_temp_file(base_branch)
66 |
67 | sh! "git reset --soft #{base_branch.shellescape}"
68 | sh! "git commit -F - -e < #{msg_path.shellescape}", "OVERCOMMIT_DISABLE" => "1"
69 |
70 | puts
71 | puts "Done! Now up to date with #{base_branch} and squashed down to 1 commit."
72 | puts "To undo the squash, you can run:"
73 | puts
74 | puts " git reset --hard #{head_sha}"
75 | puts
76 | end
77 |
78 | def require_feature_branch!
79 | return unless %w[main master trunk develop development acceptance].include?(current_branch)
80 |
81 | $stderr.puts <<~ERROR.gsub(/^/, " ")
82 |
83 | ERROR: git squash is meant to be used within feature branches only!
84 |
85 | If you really want to rewrite the history of #{current_branch} by squashing
86 | it 1 commit (this is not recommended!), then run:
87 |
88 | $ git squash --force #{current_branch.shellescape}
89 |
90 | More likely you meant to run this command within your feature branch.
91 | The intended usage is:
92 |
93 | $ git switch FEATURE_BRANCH
94 | $ git squash #{current_branch.shellescape}
95 |
96 | ERROR
97 | exit 1
98 | end
99 |
100 | def clean_merge!(base_branch)
101 | sh! "git merge --no-edit #{base_branch.shellescape}"
102 | return if capture!("git diff --name-only --diff-filter=U").strip.empty?
103 |
104 | sh! "git merge --abort"
105 | $stderr.puts <<~ERROR.gsub(/^/, " ")
106 |
107 | ERROR: There are conflicts with #{base_branch}.
108 | Resolve them with a rebase or merge before running `git squash`.
109 |
110 | ERROR
111 | exit 1
112 | end
113 |
114 | def write_commit_message_to_temp_file(base_branch)
115 | file = Tempfile.new
116 | file.puts squash_summary
117 | file.puts
118 | file.puts commit_messages(base_branch)
119 | file.close
120 | file.path
121 | end
122 |
123 | def squash_summary
124 | current_branch.sub(%r{^bugs?/}i, "Bug fix: ")
125 | .sub(%r{^docs?/}i, "Doc: ")
126 | .sub(%r{^features?/}i, "Feature: ")
127 | .sub(%r{^chores?/}i, "Chore: ")
128 | .gsub(/[-_]/, " ")
129 | end
130 |
131 | def commit_messages(base_branch)
132 | log_options = "--no-merges --reverse --format='* %B%n'"
133 | message = capture! "git log #{base_branch.shellescape}..HEAD #{log_options}"
134 | message.gsub(/\n\n+/, "\n\n")
135 | end
136 |
137 | def current_branch
138 | @_current_branch ||= capture!("git branch --show-current").chomp
139 | end
140 |
141 | def sh!(command, env = {})
142 | puts ">>>> #{command}"
143 | exit 1 unless system(env, command)
144 | end
145 |
146 | def capture!(command)
147 | result = `#{command}`
148 | return result if $CHILD_STATUS.success?
149 |
150 | $stderr.puts "Failed: #{command}"
151 | exit 1
152 | end
153 |
154 | if $PROGRAM_NAME == __FILE__
155 | force = ARGV.delete("--force")
156 | usage_and_exit if (ARGV & %w[-h --help help]).any?
157 | usage_and_exit(2) unless ARGV.length == 1
158 |
159 | squash(ARGV.first, force: force)
160 | end
161 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mattbrictson’s dotfiles
2 |
3 | This project contains the dotfiles and custom shell scripts that I use on my Mac.
4 |
5 | Why keep them at GitHub? It’s a way to share advanced shell tips with other developers, and more practically, a way to back up my configuration. When I get a new Mac or (knock on wood) need to recover from lost data or a corrupt OS, I can simply clone this repository and immediately be back in business.
6 |
7 | Thank you [Ryan Bates](https://github.com/ryanb/dotfiles), [thoughtbot](http://github.com/thoughtbot/dotfiles) and [Tom Ryder](https://github.com/tejr/dotfiles) for the inspiration.
8 |
9 | ## Requirements
10 |
11 | Before using these dotfiles there are a few things you'll need to install manually:
12 |
13 | 1. Your Mac needs git and Apple’s command-line developer tools.
14 | 2. Homebrew.
15 |
16 | My blog post here has a walkthrough: https://mattbrictson.com/rails-osx-setup-guide
17 |
18 | ## Installation
19 |
20 | Choose a place to store the dotfiles, like `~/Code/dotfiles`.
21 |
22 | ```
23 | git clone git://github.com/mattbrictson/dotfiles ~/Code/dotfiles
24 | cd ~/Code/dotfiles
25 | ```
26 |
27 | Run one of the three installation options:
28 |
29 | ```
30 | rake install:dotfiles # Install the dotfiles and scripts
31 | rake install:packages # Install homebrew packages and Mac defaults
32 | rake install # Install all of the above (recommended)
33 | ```
34 |
35 | ### Changing your bash version
36 |
37 | Some features of the dotfiles only work with bash v5+. After installing bash via homebrew (which `rake install:packages` does for you), enable it as your shell as follows:
38 |
39 | 1. Add `/usr/local/bin/bash` to `/etc/shells`
40 | 2. Change your shell to `/usr/local/bin/bash` by running `chsh`
41 |
42 | ### Package Control
43 |
44 | The Sublime Text packages configured in these dotfiles are not installed automatically. You need to manually install [Package Control](https://packagecontrol.io):
45 |
46 | 1. Press SHIFT CMD P to bring up the command palette
47 | 2. Type `install`
48 | 3. Select the option to install Package Control
49 |
50 | Then install the packages you wish.
51 |
52 | ## What’s included?
53 |
54 | ### Sublime Text settings and packages
55 |
56 | My Sublime Text settings are stored in the `sublime` directory of this repo. To ensure that Sublime Text can find this directory, I symlink `~/Library/Application Support/Sublime Text/Packages/User` to it. The installation rake task takes care of setting this up for you. Here's what my dotfiles specify for Sublime:
57 |
58 | - Settings optimized for Rails development
59 | - Better auto-complete behavior
60 | - Custom key bindings
61 |
62 | #### Packages
63 |
64 | - A File Icon
65 | - AdvancedNewFile
66 | - AutoFileName
67 | - CloseOtherWindows
68 | - Color Highlighter
69 | - DashDoc
70 | - DocBlockr
71 | - Dracula Color Scheme
72 | - GitGutter
73 | - JsPrettier
74 | - MarkdownPreview
75 | - Package Control
76 | - Pretty JSON
77 | - Spec Finder
78 | - SublimeLinter
79 | - SublimeLinter-annotations
80 | - SublimeLinter-contrib-erblint
81 | - SublimeLinter-eslint
82 | - SublimeLinter-json
83 | - SublimeLinter-rubocop
84 | - SublimeLinter-ruby
85 | - SublimeLinter-shellcheck
86 | - SublimeLinter-stylelint
87 | - TOML
88 | - YamlPipelines
89 |
90 | ### Handy scripts
91 |
92 | These scripts will be installed to `~/.bin` and added to your `$PATH`:
93 |
94 | - `brew-install` installs and updates my standard suite of homebrew recipes. Run it on a new machine after installing homebrew to get all the recipes needed for Rails development, node, PostgreSQL, etc.
95 | - `bucket` is a simple command-line interface for Bitbucket, most notably providing a way to create pull requests. Run `bucket --help` for details.
96 | - `defaults-install` uses `defaults write` on OS X to change system default behavior to my liking: e.g. don't include drop-shadows on screenshots.
97 | - `git-pluck` adds the `pluck` command to git, which is a trick for cherry-picking a commit from another repository into the current one: `git pluck ../other-repo SHA`.
98 | - `git-trim` adds the `trim` command to git, which deletes local and remote branches that have already been merged and thus are no longer needed.
99 | - `node-install` is a convenient wrapper around `mise install` that does some easy-to-forget housekeeping before and after installation. Usage: `node-install 22.14.0`.
100 | - `ruby-install` is a convenient wrapper around `mise install` that does some easy-to-forget housekeeping before and after installation. Usage: `ruby-install 3.4.2`.
101 | - `sup` uses SSH to update packages on one or more Ubuntu servers (assuming you have root access to them). In other words, `sup SERVER1 SERVER2` will SSH as root into both servers and run the appropriate `aptitude` commands to safely update all packages. It will also report whether any daemons need to be restarted for the updates to take effect.
102 |
103 | ### Shell enhancements (bash)
104 |
105 | - Customizes the shell prompt with current directory and git status: e.g. `~/Work/dotfiles (main *%)$`.
106 | - Replaces `diff` with `colordiff`.
107 | - Prettifies `ls` output and adds `l`, `la`, and `ll` shortcuts.
108 | - Improves default `top` settings.
109 | - Allows bash command history to be navigated with up and down arrow keys.
110 | - Makes bash auto-completion case-insensitive.
111 | - Sets up necessary homebrew, mise, and python virtualenv shell variables.
112 | - Specifies `less` as the default pager and Sublime Text (`subl`) as the default editor.
113 | - Sets better defaults for the `psql` command.
114 |
115 | ### Git configuration
116 |
117 | - Sets up a reasonable global gitignore file to ignore things like `.DS_Store`, `Icon?`, and `*sublime-project`.
118 | - Enables color output and line-ending checks.
119 | - Shortens common commands: `di`, `co`, `ci`, `br`, `l`, `sw`.
120 | - Defines somes useful aliases:
121 | - `git hist`
122 | - `git ignored-files`
123 | - `git untracked-files`
124 | - `git unstage`
125 |
126 | In addition, during installation (see below), you will be prompted for your full name and email address, which are automatically added to the git config file.
127 |
128 | ### Ruby/rails stuff
129 |
130 | - Adds an `r` command that serves as a shortcut for running `bin/rake` or `bin/rails`. It's pretty smart, so `r s` will expand to `bin/rails server`, and `r db` will expand to `bin/rake db:console`. No more mistakes of typing `rails` vs `rake`!
131 | - Disables gem documentation generation so that `gem install` runs much faster.
132 | - Configures the `xray` gem to use Sublime Text.
133 | - Enables command history (use up and down arrows) in `irb`.
134 | - Defines a list of useful gems that are installed by default whenever a new version of ruby is installed via `mise install`.
135 |
--------------------------------------------------------------------------------
/config/pgcli/config:
--------------------------------------------------------------------------------
1 | # vi: ft=dosini
2 | [main]
3 |
4 | # Enables context sensitive auto-completion. If this is disabled the all
5 | # possible completions will be listed.
6 | smart_completion = True
7 |
8 | # Display the completions in several columns. (More completions will be
9 | # visible.)
10 | wider_completion_menu = False
11 |
12 | # Multi-line mode allows breaking up the sql statements into multiple lines. If
13 | # this is set to True, then the end of the statements must have a semi-colon.
14 | # If this is set to False then sql statements can't be split into multiple
15 | # lines. End of line (return) is considered as the end of the statement.
16 | multi_line = False
17 |
18 | # If multi_line_mode is set to "psql", in multi-line mode, [Enter] will execute
19 | # the current input if the input ends in a semicolon.
20 | # If multi_line_mode is set to "safe", in multi-line mode, [Enter] will always
21 | # insert a newline, and [Esc] [Enter] or [Alt]-[Enter] must be used to execute
22 | # a command.
23 | multi_line_mode = psql
24 |
25 | # Destructive warning mode will alert you before executing a sql statement
26 | # that may cause harm to the database such as "drop table", "drop database"
27 | # or "shutdown".
28 | destructive_warning = True
29 |
30 | # Enables expand mode, which is similar to `\x` in psql.
31 | expand = False
32 |
33 | # Enables auto expand mode, which is similar to `\x auto` in psql.
34 | auto_expand = False
35 |
36 | # If set to True, table suggestions will include a table alias
37 | generate_aliases = False
38 |
39 | # log_file location.
40 | # In Unix/Linux: ~/.config/pgcli/log
41 | # In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\log
42 | # %USERPROFILE% is typically C:\Users\{username}
43 | log_file = default
44 |
45 | # keyword casing preference. Possible values "lower", "upper", "auto"
46 | keyword_casing = auto
47 |
48 | # casing_file location.
49 | # In Unix/Linux: ~/.config/pgcli/casing
50 | # In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\casing
51 | # %USERPROFILE% is typically C:\Users\{username}
52 | casing_file = default
53 |
54 | # If generate_casing_file is set to True and there is no file in the above
55 | # location, one will be generated based on usage in SQL/PLPGSQL functions.
56 | generate_casing_file = False
57 |
58 | # Casing of column headers based on the casing_file described above
59 | case_column_headers = True
60 |
61 | # history_file location.
62 | # In Unix/Linux: ~/.config/pgcli/history
63 | # In Windows: %USERPROFILE%\AppData\Local\dbcli\pgcli\history
64 | # %USERPROFILE% is typically C:\Users\{username}
65 | history_file = default
66 |
67 | # Default log level. Possible values: "CRITICAL", "ERROR", "WARNING", "INFO"
68 | # and "DEBUG". "NONE" disables logging.
69 | log_level = INFO
70 |
71 | # Order of columns when expanding * to column list
72 | # Possible values: "table_order" and "alphabetic"
73 | asterisk_column_order = table_order
74 |
75 | # Whether to qualify with table alias/name when suggesting columns
76 | # Possible values: "always", never" and "if_more_than_one_table"
77 | qualify_columns = if_more_than_one_table
78 |
79 | # When no schema is entered, only suggest objects in search_path
80 | search_path_filter = False
81 |
82 | # Default pager.
83 | # By default 'PAGER' environment variable is used
84 | pager = less -SRXF
85 |
86 | # Timing of sql statments and table rendering.
87 | timing = True
88 |
89 | # Table format. Possible values: psql, plain, simple, grid, fancy_grid, pipe,
90 | # ascii, double, github, orgtbl, rst, mediawiki, html, latex, latex_booktabs,
91 | # textile, moinmoin, jira, vertical, tsv, csv.
92 | # Recommended: psql, fancy_grid and grid.
93 | table_format = psql
94 |
95 | # Syntax Style. Possible values: manni, igor, xcode, vim, autumn, vs, rrt,
96 | # native, perldoc, borland, tango, emacs, friendly, monokai, paraiso-dark,
97 | # colorful, murphy, bw, pastie, paraiso-light, trac, default, fruity
98 | syntax_style = default
99 |
100 | # Keybindings:
101 | # When Vi mode is enabled you can use modal editing features offered by Vi in the REPL.
102 | # When Vi mode is disabled emacs keybindings such as Ctrl-A for home and Ctrl-E
103 | # for end are available in the REPL.
104 | vi = False
105 |
106 | # Error handling
107 | # When one of multiple SQL statements causes an error, choose to either
108 | # continue executing the remaining statements, or stopping
109 | # Possible values "STOP" or "RESUME"
110 | on_error = STOP
111 |
112 | # Set threshold for row limit prompt. Use 0 to disable prompt.
113 | row_limit = 1000
114 |
115 | # Skip intro on startup and goodbye on exit
116 | less_chatty = True
117 |
118 | # Postgres prompt
119 | # \t - Current date and time
120 | # \u - Username
121 | # \h - Short hostname of the server (up to first '.')
122 | # \H - Hostname of the server
123 | # \d - Database name
124 | # \p - Database port
125 | # \i - Postgres PID
126 | # \# - "@" sign if logged in as superuser, '>' in other case
127 | # \n - Newline
128 | # \dsn_alias - name of dsn alias if -D option is used (empty otherwise)
129 | prompt = '\u@\h:\d> '
130 |
131 | # Number of lines to reserve for the suggestion menu
132 | min_num_menu_lines = 4
133 |
134 | # Character used to left pad multi-line queries to match the prompt size.
135 | multiline_continuation_char = ''
136 |
137 | # The string used in place of a null value.
138 | null_string = ''
139 |
140 | # manage pager on startup
141 | enable_pager = True
142 |
143 | # Use keyring to automatically save and load password in a secure manner
144 | keyring = True
145 |
146 | # Custom colors for the completion menu, toolbar, etc.
147 | [colors]
148 | completion-menu.completion.current = 'bg:#ffffff #000000'
149 | completion-menu.completion = 'bg:#008888 #ffffff'
150 | completion-menu.meta.completion.current = 'bg:#44aaaa #000000'
151 | completion-menu.meta.completion = 'bg:#448888 #ffffff'
152 | completion-menu.multi-column-meta = 'bg:#aaffff #000000'
153 | scrollbar.arrow = 'bg:#003333'
154 | scrollbar = 'bg:#00aaaa'
155 | selected = '#ffffff bg:#6666aa'
156 | search = '#ffffff bg:#4444aa'
157 | search.current = '#ffffff bg:#44aa44'
158 | bottom-toolbar = 'bg:#222222 #aaaaaa'
159 | bottom-toolbar.off = 'bg:#222222 #888888'
160 | bottom-toolbar.on = 'bg:#222222 #ffffff'
161 | search-toolbar = 'noinherit bold'
162 | search-toolbar.text = 'nobold'
163 | system-toolbar = 'noinherit bold'
164 | arg-toolbar = 'noinherit bold'
165 | arg-toolbar.text = 'nobold'
166 | bottom-toolbar.transaction.valid = 'bg:#222222 #00ff5f bold'
167 | bottom-toolbar.transaction.failed = 'bg:#222222 #ff005f bold'
168 |
169 | # style classes for colored table output
170 | output.header = "#00ff5f bold"
171 | output.odd-row = ""
172 | output.even-row = ""
173 |
174 | # Named queries are queries you can execute by name.
175 | [named queries]
176 |
177 | # DSN to call by -D option
178 | [alias_dsn]
179 | # example_dsn = postgresql://[user[:password]@][netloc][:port][/dbname]
180 |
181 | # Format for number representation
182 | # for decimal "d" - 12345678, ",d" - 12,345,678
183 | # for float "g" - 123456.78, ",g" - 123,456.78
184 | [data_formats]
185 | decimal = ""
186 | float = ""
187 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Based on https://github.com/ryanb/dotfiles/
2 |
3 | require "erb"
4 | require "fileutils"
5 | require "pstore"
6 | require "rake"
7 | require "shellwords"
8 |
9 | IGNORE = %w[
10 | dotfiles.sublime-project
11 | dotfiles.sublime-workspace
12 | completions
13 | extras
14 | LICENSE
15 | Rakefile
16 | README.md
17 | sublime
18 | ].freeze
19 |
20 | def brew_prefix
21 | @brew_prefix ||= File.executable?("/opt/homebrew/bin/brew") ? "/opt/homebrew" : "/usr/local"
22 | end
23 |
24 | task default: "install"
25 |
26 | desc "Install packages and dotfiles"
27 | task install: %w[install:dotfiles install:completions install:packages]
28 |
29 | desc "Warn if git origin is newer"
30 | task :check do
31 | next unless system("git fetch origin")
32 | next if `git diff HEAD origin/main`.strip.empty?
33 |
34 | log(:yellow, "warning Working copy is out of date; consider `git pull`")
35 | end
36 |
37 | namespace :install do
38 | desc "Install homebrew, etc. packages"
39 | task packages: :check do
40 | %w[brew defaults].each do |type|
41 | log(:blue, "executing bin/#{type}-install …")
42 | system("bin/#{type}-install")
43 | end
44 | end
45 |
46 | desc "Install dotfiles into user’s home directory"
47 | task dotfiles: %i[link_sublime link_xbar check] do
48 | always_replace = false
49 |
50 | Dotfile.each do |dotfile|
51 | case dotfile.status
52 | when :identical
53 | log(:green, "identical #{dotfile}")
54 | when :missing
55 | dotfile.link!
56 | when :different
57 | if always_replace
58 | dotfile.replace!
59 | elsif (answer = ask(:red, "overwrite? #{dotfile}"))
60 | always_replace = true if answer == :always
61 | dotfile.replace!
62 | else
63 | log(:gray, "skipping #{dotfile}")
64 | end
65 | end
66 | end
67 |
68 | Rake::Task["install:prune_brewfile"].invoke
69 | end
70 |
71 | task :prune_brewfile do
72 | ignores = File.exist?(".brewignore") ? File.readlines(".brewignore", chomp: true) : []
73 | brewfile_path = File.expand_path("~/.Brewfile")
74 | brewfile = File.readlines(brewfile_path)
75 |
76 | ignores.each do |ignore|
77 | next if ignore.empty?
78 |
79 | brewfile.reject! { |line| line.include?(ignore) }
80 | end
81 |
82 | File.write(brewfile_path, brewfile.join)
83 | end
84 |
85 | desc "Install bash completion scripts"
86 | task :completions do
87 | FileUtils.mkdir_p "#{brew_prefix}/etc/bash_completion.d"
88 | Dir[File.expand_path("completions/*", __dir__)].each do |script|
89 | basename = File.basename(script)
90 | target = "#{brew_prefix}/etc/bash_completion.d/#{basename}"
91 | log(:blue, "linking completions/#{basename}")
92 | FileUtils.rm_rf(target)
93 | FileUtils.ln_s(script, target)
94 | end
95 | end
96 |
97 | desc "Symlink the Sublime Packages/User directory"
98 | task :link_sublime do
99 | sublime = File.expand_path("sublime", __dir__)
100 | user_packages = File.expand_path("~/Library/Application Support/Sublime Text/Packages/User")
101 | unless File.exist?(user_packages)
102 | log(:magenta, "mkdir Library/Application Support/Sublime Text/Packages/User")
103 | FileUtils.mkdir_p(user_packages)
104 | end
105 | if File.directory?(user_packages) && !File.symlink?(user_packages)
106 | log(:blue, "copy Library/Application Support/Sublime Text/Packages/User/*")
107 | FileUtils.cp_r(Dir.glob(user_packages.shellescape + "/*"), sublime)
108 | log(:magenta, "rm Library/Application Support/Sublime Text/Packages/User")
109 | FileUtils.rm_rf(user_packages)
110 | log(:blue, "linking Library/Application Support/Sublime Text/Packages/User")
111 | FileUtils.ln_s(sublime, user_packages)
112 | end
113 | end
114 |
115 | desc "Symlink the xbar plugins directory"
116 | task :link_xbar do
117 | custom_plugins = File.expand_path("../xbar/plugins", __dir__)
118 | break unless File.directory?(custom_plugins)
119 |
120 | xbar_support = File.expand_path("~/Library/Application Support/xbar/plugins")
121 | unless File.symlink?(xbar_support)
122 | log(:magenta, "rm ~/Library/Application Support/xbar/plugins")
123 | FileUtils.rm_rf(xbar_support)
124 | log(:blue, "linking ~/Library/Application Support/xbar/plugins")
125 | FileUtils.ln_s(custom_plugins, xbar_support)
126 | end
127 | end
128 | end
129 |
130 | def log(color, message)
131 | begin
132 | require "highline"
133 | rescue LoadError
134 | end
135 |
136 | first, rest = message.split(" ", 2)
137 | first = first.ljust(10)
138 |
139 | if defined?(HighLine::String)
140 | first = HighLine::String.new(first).public_send(color)
141 | end
142 |
143 | line = [first, rest].join(" ")
144 |
145 | if line.end_with?(" ")
146 | print(line)
147 | else
148 | puts(line)
149 | end
150 | end
151 |
152 | def ask(color, question)
153 | log(color, "#{question} [yNaq]? ")
154 |
155 | case $stdin.gets.chomp
156 | when "a"
157 | :always
158 | when "y"
159 | true
160 | when "q"
161 | exit
162 | else
163 | false
164 | end
165 | end
166 |
167 | class Dotfile
168 | def self.each
169 | `git ls-files -z`.split("\x0").each do |file|
170 | next if file =~ %r{^\.|/\.}
171 | next if IGNORE.include?(file.split("/").first)
172 |
173 | yield(new(file))
174 | end
175 | end
176 |
177 | attr_reader :file
178 |
179 | def initialize(file)
180 | @file = file
181 | end
182 |
183 | def erb?
184 | file =~ /\.erb\z/i
185 | end
186 |
187 | def name
188 | ".#{file.sub(/\.erb\z/i, "")}"
189 | end
190 | alias :to_s :name
191 |
192 | def target
193 | File.expand_path("~/#{name}")
194 | end
195 |
196 | def status
197 | if File.identical?(file, target)
198 | :identical
199 | elsif File.exist?(target) || File.symlink?(target)
200 | :different
201 | else
202 | :missing
203 | end
204 | end
205 |
206 | def link!(delete_first: false)
207 | ensure_target_directory
208 |
209 | if erb?
210 | log(:yellow, "generating #{self}")
211 | contents = ERB.new(File.read(file)).result(binding)
212 |
213 | log(:blue, "writing #{self}")
214 | FileUtils.rm_rf(target) if delete_first
215 | File.open(target, "w") do |out|
216 | out << contents
217 | end
218 | else
219 | log(:blue, "linking #{self}")
220 | FileUtils.rm_rf(target) if delete_first
221 | FileUtils.ln_s(File.absolute_path(file), target)
222 | end
223 | end
224 |
225 | def replace!
226 | link!(delete_first: true)
227 | end
228 |
229 | def ensure_target_directory
230 | directory = File.dirname(target)
231 | return if File.directory?(directory)
232 |
233 | log(:magenta, "mkdir #{File.dirname(name)}")
234 | FileUtils.mkdir_p(directory)
235 | end
236 |
237 | def prompt(label)
238 | default = pstore.transaction { pstore[label] }
239 | print default ? "#{label} (#{default}): " : "#{label}: "
240 | $stdout.flush
241 |
242 | entry = $stdin.gets.chomp
243 | result = entry.empty? && default ? default : entry
244 |
245 | pstore.transaction { pstore[label] = result }
246 | result
247 | end
248 |
249 | def pstore
250 | @_pstore ||= PStore.new(pstore_path)
251 | end
252 |
253 | def pstore_path
254 | File.join(__dir__, ".db")
255 | end
256 | end
257 |
--------------------------------------------------------------------------------
/extras/Matt.terminal:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ANSIBlackColor
6 |
7 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
8 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
9 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAmMC4xMzg3NjA3MDA4IDAuMTM4ODkyODg5
10 | IDAuMTM4NzUzOTM1NwAQAYAC0hQVFhdaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9y
11 | ohYYWE5TT2JqZWN0CBEaJCkyN0lMUVNXXWRqd36nqauwu8TMzwAAAAAAAAEBAAAAAAAA
12 | ABkAAAAAAAAAAAAAAAAAAADY
13 |
14 | ANSIBlueColor
15 |
16 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
17 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
18 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC41MTg3NDQ4MjYzIDAuNzAxNzgwNzk2
19 | MSAwLjg1NDAyMzQ1NjYAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
20 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
21 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
22 |
23 | ANSIBrightBlackColor
24 |
25 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
26 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
27 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC4yNzczMjIwODM3IDAuMjc3NTEyODc4
28 | MiAwLjI3NzMzODk1MTgAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
29 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
30 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
31 |
32 | ANSIBrightBlueColor
33 |
34 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
35 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
36 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC42NTE5NzIyMzQyIDAuODE0ODM5MzAz
37 | NSAwLjk0Mzk0ODYyNjUAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
38 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
39 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
40 |
41 | ANSIBrightCyanColor
42 |
43 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
44 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
45 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAmMC4yOTM0NzYyNTM3IDAuNzI2NDk3NjUw
46 | MSAwLjc2MTUyNjUyNQAQAYAC0hQVFhdaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9y
47 | ohYYWE5TT2JqZWN0CBEaJCkyN0lMUVNXXWRqd36nqauwu8TMzwAAAAAAAAEBAAAAAAAA
48 | ABkAAAAAAAAAAAAAAAAAAADY
49 |
50 | ANSIBrightGreenColor
51 |
52 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
53 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
54 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAmMC41ODQyMzAwNjUzIDAuODU2NjI5MzEy
55 | IDAuMjI3Mzk5MjAwMgAQAYAC0hQVFhdaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9y
56 | ohYYWE5TT2JqZWN0CBEaJCkyN0lMUVNXXWRqd36nqauwu8TMzwAAAAAAAAEBAAAAAAAA
57 | ABkAAAAAAAAAAAAAAAAAAADY
58 |
59 | ANSIBrightMagentaColor
60 |
61 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
62 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
63 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC44NzQxODI4Nzk5IDAuMzAxNzk1MDY1
64 | NCAwLjYyMjI4Nzk4ODcAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
65 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
66 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
67 |
68 | ANSIBrightRedColor
69 |
70 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
71 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
72 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC44Njk1ODIxMTY2IDAuMjcxMzI0ODEz
73 | NCAwLjIzMjA3MDM4NjQAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
74 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
75 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
76 |
77 | ANSIBrightWhiteColor
78 |
79 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
80 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
81 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC45NzAzNzE0ODQ4IDAuOTYxOTgxMTE3
82 | NyAwLjkwNTkzMDkzNjMAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
83 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
84 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
85 |
86 | ANSIBrightYellowColor
87 |
88 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
89 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
90 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC45MDA1MDY2MTU2IDAuODQxNDI4NTE4
91 | MyAwLjM2ODQ1MzUwMjcAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
92 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
93 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
94 |
95 | ANSICyanColor
96 |
97 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
98 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
99 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC4yMDk4ODEwOTcxIDAuNjgxNTE1NTc0
100 | NSAwLjcxNDEwOTU5OTYAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
101 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
102 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
103 |
104 | ANSIGreenColor
105 |
106 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
107 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
108 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAmMC40ODkxMDI0MjMyIDAuNzcwMTQ5MjMx
109 | IDAuMTgyMjQ4NDg4MQAQAYAC0hQVFhdaJGNsYXNzbmFtZVgkY2xhc3Nlc1dOU0NvbG9y
110 | ohYYWE5TT2JqZWN0CBEaJCkyN0lMUVNXXWRqd36nqauwu8TMzwAAAAAAAAEBAAAAAAAA
111 | ABkAAAAAAAAAAAAAAAAAAADY
112 |
113 | ANSIMagentaColor
114 |
115 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
116 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
117 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC43NzQ4NjM1NDExIDAuMTk5ODM5NjUx
118 | NiAwLjUxNTUxNTIwODIAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
119 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
120 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
121 |
122 | ANSIRedColor
123 |
124 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
125 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
126 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC43NzMxMzQzNTA4IDAuMjAwOTkzMzQ0
127 | MiAwLjE2ODU5NTIzOTUAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
128 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
129 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
130 |
131 | ANSIWhiteColor
132 |
133 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
134 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
135 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAlMC44NTY3MTYzOTQ0IDAuODQxODIzMTAx
136 | IDAuNzc2ODMwMjU2ABABgALSFBUWF1okY2xhc3NuYW1lWCRjbGFzc2VzV05TQ29sb3Ki
137 | FhhYTlNPYmplY3QIERokKTI3SUxRU1ddZGp3fqaoqq+6w8vOAAAAAAAAAQEAAAAAAAAA
138 | GQAAAAAAAAAAAAAAAAAAANc=
139 |
140 | ANSIYellowColor
141 |
142 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
143 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
144 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAnMC43OTcwMTQ5NTE3IDAuNzMwMjM4NjE2
145 | NSAwLjI4Mzc4MDYwNDYAEAGAAtIUFRYXWiRjbGFzc25hbWVYJGNsYXNzZXNXTlNDb2xv
146 | cqIWGFhOU09iamVjdAgRGiQpMjdJTFFTV11kand+qKqssbzFzdAAAAAAAAABAQAAAAAA
147 | AAAZAAAAAAAAAAAAAAAAAAAA2Q==
148 |
149 | BackgroundBlur
150 | 0.0
151 | BackgroundBlurInactive
152 | 0.20270330255681818
153 | BackgroundColor
154 |
155 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
156 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
157 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAsMC4xMDAzOTQ4NzA5IDAuMTAwMzk0ODcw
158 | OSAwLjEwMDM5NDg3MDkgMC44NQAQAYAC0hQVFhdaJGNsYXNzbmFtZVgkY2xhc3Nlc1dO
159 | U0NvbG9yohYYWE5TT2JqZWN0CBEaJCkyN0lMUVNXXWRqd36tr7G2wcrS1QAAAAAAAAEB
160 | AAAAAAAAABkAAAAAAAAAAAAAAAAAAADe
161 |
162 | BackgroundSettingsForInactiveWindows
163 |
164 | Bell
165 |
166 | BlinkText
167 |
168 | CursorType
169 | 0
170 | DisableANSIColor
171 |
172 | EscapeNonASCIICharacters
173 |
174 | Font
175 |
176 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
177 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGkCwwVFlUkbnVsbNQNDg8QERIT
178 | FFZOU1NpemVYTlNmRmxhZ3NWTlNOYW1lViRjbGFzcyNALAAAAAAAABAQgAKAA18QIE1l
179 | c2xvTEdTTmVyZEZvbnRDb21wbGV0ZS1SZWd1bGFy0hcYGRpaJGNsYXNzbmFtZVgkY2xh
180 | c3Nlc1ZOU0ZvbnSiGRtYTlNPYmplY3QIERokKTI3SUxRU1heZ253foWOkJKUt7zH0Nfa
181 | AAAAAAAAAQEAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAOM=
182 |
183 | FontAntialias
184 |
185 | FontWidthSpacing
186 | 1.004032258064516
187 | Linewrap
188 |
189 | ProfileCurrentVersion
190 | 2.0699999999999998
191 | SelectionColor
192 |
193 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
194 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
195 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzTxAsMC45MTc3ODE2NTEgMC41NzM5MDIwNzA1
196 | IDAuMDMxOTUxODU5NTkgMC43NQAQAYAC0hQVFhdaJGNsYXNzbmFtZVgkY2xhc3Nlc1dO
197 | U0NvbG9yohYYWE5TT2JqZWN0CBEaJCkyN0lMUVNXXWRqd36tr7G2wcrS1QAAAAAAAAEB
198 | AAAAAAAAABkAAAAAAAAAAAAAAAAAAADe
199 |
200 | ShowDimensionsInTitle
201 |
202 | TerminalType
203 | xterm-256color
204 | TextBoldColor
205 |
206 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
207 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxAREldO
208 | U1doaXRlXE5TQ29sb3JTcGFjZVYkY2xhc3NCMQAQA4AC0hQVFhdaJGNsYXNzbmFtZVgk
209 | Y2xhc3Nlc1dOU0NvbG9yohYYWE5TT2JqZWN0CBEaJCkyN0lMUVNXXWRseYCDhYeMl6Co
210 | qwAAAAAAAAEBAAAAAAAAABkAAAAAAAAAAAAAAAAAAAC0
211 |
212 | TextColor
213 |
214 | YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMS
215 | AAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRyb290gAGjCwwTVSRudWxs0w0ODxARElVO
216 | U1JHQlxOU0NvbG9yU3BhY2VWJGNsYXNzRjEgMSAxABABgALSFBUWF1okY2xhc3NuYW1l
217 | WCRjbGFzc2VzV05TQ29sb3KiFhhYTlNPYmplY3QIERokKTI3SUxRU1ddZGp3foWHiY6Z
218 | oqqtAAAAAAAAAQEAAAAAAAAAGQAAAAAAAAAAAAAAAAAAALY=
219 |
220 | UseBoldFonts
221 |
222 | UseBrightBold
223 |
224 | VisualBell
225 |
226 | VisualBellOnlyWhenMuted
227 |
228 | columnCount
229 | 100
230 | name
231 | Matt
232 | shellExitAction
233 | 1
234 | type
235 | Window Settings
236 | useOptionAsMetaKey
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------