├── .github ├── dependabot.yml └── workflows │ └── tests.yml ├── .gitignore ├── .rubocop.yml ├── Brewfile ├── LICENSE.txt ├── README.md ├── bash_logout.sh ├── bash_profile.sh ├── bashrc.sh ├── bin ├── git-commit-each ├── git-credential-dotfiles ├── git-gc-global ├── git-pr-each ├── scm-update ├── touchid-enable-pam-sudo └── upsy-desky ├── bundle └── config ├── cursor-keybindings.json ├── cursor-settings.json ├── gemrc ├── gitattributes ├── gitconfig ├── gitconfig.local.macos ├── gitignore ├── grc └── conf.rubybacktrace ├── irbrc ├── logout.sh ├── rspec ├── rubocop-oss.yml ├── rubocop-work.yml ├── screenrc ├── script ├── linux-after-setup ├── setup └── strap-after-setup ├── shprofile.sh ├── shrc.sh ├── ssh ├── authorized_keys ├── config └── id_ed25519.pub ├── vimrc ├── vscode-settings.json ├── zed-settings.json ├── zlogout.sh ├── zprofile.sh └── zshrc.sh /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: daily 8 | time: "07:15" 9 | timezone: "Europe/London" 10 | groups: 11 | github-actions: 12 | patterns: 13 | - "*" 14 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Actions CI 2 | on: 3 | push: 4 | branches: main 5 | pull_request: 6 | jobs: 7 | tests: 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: 12 | - ubuntu-latest 13 | - windows-latest 14 | - macos-latest 15 | steps: 16 | - name: Set up Git repository 17 | uses: actions/checkout@v4 18 | 19 | - name: Validate syntax 20 | run: bash -n *.sh 21 | 22 | - name: Test script/setup 23 | run: bash script/setup 24 | 25 | - name: Test bashrc 26 | run: bash -xc "source ~/.bashrc" 27 | 28 | - name: Test zshrc 29 | run: zsh -xc "source ~/.zshrc" 30 | if: matrix.os == 'macos-latest' 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /ssh/1Password/ 2 | /ssh/id_* 3 | /ssh/id_rsa 4 | /ssh/*_rsa* 5 | /ssh/known_hosts 6 | /gnupg/*.d/ 7 | /gnupg/*.db 8 | /gnupg/*.gpg 9 | /gnupg/*.kbx 10 | /gnupg/*.private.* 11 | /gnupg/*.lock 12 | /gnupg/.* 13 | /gnupg/gpg-agent.conf 14 | /gnupg/sshcontrol 15 | bundle/cache 16 | secrets 17 | rbenv/*/ 18 | Brewfile.lock.json 19 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | rubocop-oss.yml -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | tap "homebrew/aliases" 2 | tap "homebrew/bundle" 3 | tap "homebrew/cask" 4 | tap "homebrew/command-not-found" 5 | tap "homebrew/core" 6 | tap "homebrew/formula-analytics" 7 | tap "homebrew/portable-ruby" 8 | tap "homebrew/services" 9 | tap "homebrew/test-bot" 10 | # Search tool like grep, but optimized for programmers 11 | brew "ack" 12 | # Static analysis and lint tool, for (ba)sh scripts 13 | brew "shellcheck" 14 | # Static checker for GitHub Actions workflow files 15 | brew "actionlint" 16 | # Clone of cat(1) with syntax highlighting and Git integration 17 | brew "bat" 18 | # Cross-platform make 19 | brew "cmake" 20 | # Load/unload environment variables based on $PWD 21 | brew "direnv" 22 | # Command-line tool for DigitalOcean 23 | brew "doctl" 24 | # Disk Usage/Free Utility - a better 'df' alternative 25 | brew "duf" 26 | # More intuitive version of du in rust 27 | brew "dust" 28 | # Modern, maintained replacement for ls 29 | brew "eza" 30 | # Simple, fast and user-friendly alternative to find 31 | brew "fd" 32 | # Manage Procfile-based applications 33 | brew "foreman" 34 | # GitHub command-line tool 35 | brew "gh" 36 | # Distributed revision control system 37 | brew "git" 38 | # Syntax-highlighting pager for git and diff output 39 | brew "git-delta" 40 | # GNU version of the tar archiving utility 41 | brew "gnu-tar" 42 | # Open source programming language to build simple/reliable/efficient software 43 | brew "go" 44 | # Fast linters runner for Go 45 | brew "golangci-lint" 46 | # Colorize logfiles and command output 47 | brew "grc" 48 | # Improved top (interactive process viewer) 49 | brew "htop" 50 | # Command-line benchmarking tool 51 | brew "hyperfine" 52 | # Lightweight and flexible command-line JSON processor 53 | brew "jq" 54 | # YAML Parser 55 | brew "libyaml" 56 | # Platform built on V8 to build network applications 57 | brew "node" 58 | # CLI for Node.js style checker and lint tool for Markdown files 59 | brew "markdownlint-cli" 60 | # Mac App Store command-line interface 61 | brew "mas" 62 | # Install NodeJS versions 63 | brew "node-build" 64 | # Platform built on V8 to build network applications 65 | brew "node@20" 66 | # Platform built on V8 to build network applications 67 | brew "node@22" 68 | # Manage multiple NodeJS versions 69 | brew "nodenv" 70 | # Optimizer for PNG files 71 | brew "pngcrush" 72 | # Object-relational database system 73 | brew "postgresql@16", restart_service: :changed 74 | # Wrapper to colorize and simplify ping's output 75 | brew "prettyping" 76 | # Install various Ruby versions and implementations 77 | brew "ruby-build" 78 | # Ruby version manager 79 | brew "rbenv" 80 | # Search tool like grep and The Silver Searcher 81 | brew "ripgrep" 82 | # Powerful, clean, object-oriented scripting language 83 | brew "ruby" 84 | # Powerful, clean, object-oriented scripting language 85 | brew "ruby@3.1" 86 | # Powerful, clean, object-oriented scripting language 87 | brew "ruby@3.2" 88 | # Powerful, clean, object-oriented scripting language 89 | brew "ruby@3.3" 90 | # Autoformat shell script source code 91 | brew "shfmt" 92 | # Syntax-aware linter for prose 93 | brew "vale" 94 | # Watch files and take action when they change 95 | brew "watchman" 96 | # Internet file retriever 97 | brew "wget" 98 | # Linter for YAML files 99 | brew "yamllint" 100 | # Feature-rich command-line audio/video downloader 101 | brew "yt-dlp" 102 | # Find security issues in GitHub Actions setups 103 | brew "zizmor" 104 | # Fish-like fast/unobtrusive autosuggestions for zsh 105 | brew "zsh-autosuggestions" 106 | # Password manager that keeps all passwords secure behind one password 107 | cask "1password" 108 | # Command-line interface for 1Password 109 | cask "1password-cli" 110 | # Application uninstaller 111 | cask "appcleaner" 112 | # Hard disk backup and cloning utility 113 | cask "carbon-copy-cloner" 114 | # OpenAI's official ChatGPT desktop app 115 | cask "chatgpt" 116 | # Write, edit, and chat about your code with AI 117 | cask "cursor" 118 | # Elgato FACECAM configuration tool 119 | cask "elgato-camera-hub" 120 | # Control your Elgato key lights 121 | cask "elgato-control-center" 122 | cask "font-sf-mono" 123 | cask "font-signika" 124 | cask "font-skranji" 125 | cask "font-space-mono" 126 | # GIT client 127 | cask "fork" 128 | # Web browser 129 | cask "google-chrome" 130 | # Tool to prevent the system from going into sleep mode 131 | cask "keepingyouawake" 132 | # Audiobook manager for Audible users 133 | cask "openaudible" 134 | # Automatically hides or quits apps after periods of inactivity 135 | cask "quitter" 136 | # Utility to resize and position application windows 137 | cask "sizeup" 138 | # Virtual machines UI using QEMU 139 | cask "utm" 140 | # Yet another monitor 141 | cask "yam-display" 142 | # Multiplayer code editor 143 | cask "zed" 144 | # Video communication and virtual meeting platform 145 | cask "zoom" 146 | mas "1Blocker", id: 1365531024 147 | mas "1Password for Safari", id: 1569813296 148 | mas "GarageBand", id: 682658836 149 | mas "In Your Face", id: 1476964367 150 | mas "Keynote", id: 409183694 151 | mas "Numbers", id: 409203825 152 | mas "Pages", id: 409201541 153 | mas "Pixelmator Pro", id: 1289583905 154 | mas "Refined GitHub", id: 1519867270 155 | mas "Slack", id: 803453959 156 | mas "TouchDraw", id: 1580322028 157 | mas "WhatsApp", id: 310633997 158 | vscode "aliariff.vscode-erb-beautify" 159 | vscode "anykeyh.simplecov-vscode" 160 | vscode "ban.spellright" 161 | vscode "bierner.markdown-mermaid" 162 | vscode "bradlc.vscode-tailwindcss" 163 | vscode "davidanson.vscode-markdownlint" 164 | vscode "davidbwaters.macos-modern-theme" 165 | vscode "dbaeumer.vscode-eslint" 166 | vscode "editorconfig.editorconfig" 167 | vscode "foxundermoon.shell-format" 168 | vscode "github.codespaces" 169 | vscode "github.copilot" 170 | vscode "github.copilot-chat" 171 | vscode "github.vscode-github-actions" 172 | vscode "github.vscode-pull-request-github" 173 | vscode "golang.go" 174 | vscode "heybourn.headwind" 175 | vscode "koichisasada.vscode-rdbg" 176 | vscode "manuelpuyol.erb-linter" 177 | vscode "markis.code-coverage" 178 | vscode "ms-azuretools.vscode-docker" 179 | vscode "ms-vscode.makefile-tools" 180 | vscode "msyrus.go-doc" 181 | vscode "redhat.vscode-yaml" 182 | vscode "shopify.ruby-lsp" 183 | vscode "sorbet.sorbet-vscode-extension" 184 | vscode "standard.vscode-standard" 185 | vscode "stylelint.vscode-stylelint" 186 | vscode "timonwong.shellcheck" 187 | vscode "yzane.markdown-pdf" 188 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dot Files 2 | 3 | My dot files shared between machines for ZSH, Bash, macOS, Linux, Cygwin and MSYS. 4 | 5 | ## Installation 6 | 7 | Run [`script/setup`](https://github.com/MikeMcQuaid/dotfiles/blob/main/script/setup) 8 | after checkout to symlink (or copy) everything in this directory to your home directory. 9 | 10 | ## Status 11 | 12 | I'm using these on all my personal machines and GitHub Codespaces. 13 | 14 | ## Contact 15 | 16 | [Mike McQuaid](mailto:mike@mikemcquaid.com) 17 | 18 | ## License 19 | 20 | These dot files are licensed under the [GPLv3 License](https://en.wikipedia.org/wiki/GNU_General_Public_License). 21 | The full license text is available in [LICENSE.txt](https://github.com/MikeMcQuaid/dotfiles/blob/main/LICENSE.txt). 22 | -------------------------------------------------------------------------------- /bash_logout.sh: -------------------------------------------------------------------------------- 1 | source ~/.logout 2 | -------------------------------------------------------------------------------- /bash_profile.sh: -------------------------------------------------------------------------------- 1 | # load shared shell configuration 2 | source ~/.shprofile 3 | 4 | # check if this is a login and/or interactive shell 5 | [ "$0" = "-bash" ] && export LOGIN_BASH=1 6 | echo "$-" | grep -q "i" && export INTERACTIVE_BASH=1 7 | 8 | # run bashrc if this is a login, interactive shell 9 | if [ -n "$LOGIN_BASH" ] && [ -n "$INTERACTIVE_BASH" ]; then 10 | source ~/.bashrc 11 | fi 12 | 13 | # Set HOST for ZSH compatibility 14 | export HOST="$HOSTNAME" 15 | 16 | # check the window size after each command and, if necessary, 17 | # update the values of LINES and COLUMNS. 18 | shopt -s checkwinsize 19 | 20 | # Enable history appending instead of overwriting. 21 | shopt -s histappend 22 | 23 | # Save multiline commands 24 | shopt -s cmdhist 25 | 26 | # Correct minor directory changing spelling mistakes 27 | shopt -s cdspell 28 | 29 | # Remove broken bash completion 30 | /bin/rm -f "${HOMEBREW_PREFIX}/etc/bash_completion.d/ctest" 31 | 32 | # Bash completion 33 | [ -f /etc/profile.d/bash-completion ] && source /etc/profile.d/bash-completion 34 | if type brew &>/dev/null; then 35 | for COMPLETION in "${HOMEBREW_PREFIX}/etc/bash_completion.d"/*; do 36 | [[ -f $COMPLETION ]] && source "$COMPLETION" 37 | done 38 | if [[ -f $(brew --prefix)/etc/profile.d/bash_completion.sh ]]; then 39 | source "$(brew --prefix)/etc/profile.d/bash_completion.sh" 40 | fi 41 | fi 42 | 43 | # Colorful prompt 44 | if [ "$USER" = "root" ]; then 45 | PS1='\[\033[01;35m\]\h\[\033[01;34m\] \W #\[\033[00m\] ' 46 | elif [ -n "${SSH_CONNECTION}" ]; then 47 | PS1='\[\033[01;36m\]\h\[\033[01;34m\] \W #\[\033[00m\] ' 48 | else 49 | PS1='\[\033[01;32m\]\h\[\033[01;34m\] \W #\[\033[00m\] ' 50 | fi 51 | 52 | # only set key bindings on interactive shell 53 | if [ -n "$INTERACTIVE_BASH" ]; then 54 | # fix delete key on macOS 55 | [ "$MACOS" ] && bind '"\e[3~" delete-char' 56 | 57 | # alternate mappings for Ctrl-U/V to search the history 58 | bind '"^u" history-search-backward' 59 | bind '"^v" history-search-forward' 60 | fi 61 | -------------------------------------------------------------------------------- /bashrc.sh: -------------------------------------------------------------------------------- 1 | # check if this is a login shell 2 | [ "$0" = "-bash" ] && export LOGIN_BASH=1 3 | 4 | # run bash_profile if this is not a login shell 5 | [ -z "$LOGIN_BASH" ] && source ~/.bash_profile 6 | 7 | # load shared shell configuration 8 | source ~/.shrc 9 | 10 | # History 11 | export HISTFILE="$HOME/.bash_history" 12 | export HISTCONTROL="ignoredups" 13 | export PROMPT_COMMAND="history -a" 14 | export HISTIGNORE="&:ls:[bf]g:exit" 15 | 16 | # More colours with grc 17 | # shellcheck disable=SC1090 18 | GRC_SH="$HOMEBREW_PREFIX/etc/grc.sh" 19 | [ -f "$GRC_SH" ] && source "$GRC_SH" 20 | 21 | # to avoid non-zero exit code 22 | true 23 | -------------------------------------------------------------------------------- /bin/git-commit-each: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Commit all modified files in commits with one modified file per commit 3 | set -e 4 | 5 | for FILENAME in $(git ls-files --modified --others); do 6 | MESSAGE="$(echo "$FILENAME" | sed -e 's|^Library/Homebrew/||' \ 7 | -e 's|^Formula/||' \ 8 | -e 's|^Casks/||' \ 9 | -e 's|^[0-9a-z]/||' \ 10 | -e 's|^font/\(font-[0-9a-z]/\)\{0,1\}||' \ 11 | -e 's|^lib/||' \ 12 | -e 's/\.rb//' \ 13 | -e 's/$/: /')" 14 | git add "$FILENAME" 15 | git commit "$FILENAME" --message="${MESSAGE}${1}" --message="${2}" 16 | done 17 | -------------------------------------------------------------------------------- /bin/git-credential-dotfiles: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Use the right credential helper on Codespaces/macOS/Linux 3 | set -e 4 | 5 | if [[ "$CODESPACES" == "true" ]]; then 6 | /.codespaces/bin/gitcredential_github.sh "$@" 7 | else 8 | gh auth git-credential "$@" 9 | fi 10 | -------------------------------------------------------------------------------- /bin/git-gc-global: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Garbage collect all Git repositories under the current directory. 3 | set -e 4 | 5 | CURRENT=$PWD 6 | # Set to newline to loop over find output correctly on spaced paths. 7 | IFS=" 8 | " 9 | 10 | echorun() { 11 | echo + "$@" 12 | "$@" 13 | } 14 | 15 | find -L "$CURRENT" -name .git -print0 | while read -d '' -r SCM; do 16 | DIRECTORY="$(dirname "$SCM")" 17 | cd "$DIRECTORY" || continue 18 | echo "== Garbage collecting $(basename "$DIRECTORY")" 19 | if [ -d .git ]; then 20 | echorun git gc --aggressive 21 | fi 22 | echo 23 | done 24 | -------------------------------------------------------------------------------- /bin/git-pr-each: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Submit pull requests for all modified files with one modified file per PR 3 | set -e 4 | 5 | if [ -z "$1" ]; then 6 | echo "Error: missing branch suffix!" >&2 7 | ERROR="1" 8 | fi 9 | BRANCH_SUFFIX="$1" 10 | 11 | if [ -z "$2" ]; then 12 | echo "Error: missing message head suffix!" >&2 13 | ERROR="1" 14 | fi 15 | MESSAGE_HEAD_SUFFIX="$2" 16 | 17 | if [ -z "$3" ]; then 18 | echo "Error: missing message content!" >&2 19 | ERROR="1" 20 | fi 21 | MESSAGE_CONTENT="$3" 22 | 23 | if [ -n "$ERROR" ]; then 24 | exit 1 25 | fi 26 | 27 | for FILENAME in $(git ls-files --modified --others); do 28 | NAME="$(echo "$FILENAME" | sed -e 's|^Library/Homebrew/||' \ 29 | -e 's|^Formula/||' \ 30 | -e 's|^Casks/||' \ 31 | -e 's/\.rb//')" 32 | git checkout -b "$NAME-$BRANCH_SUFFIX" origin/master 33 | git add "$FILENAME" 34 | MESSAGE="$(echo "$NAME" | sed -e 's/$/: /')" 35 | git commit "$FILENAME" --message="${MESSAGE}${MESSAGE_HEAD_SUFFIX}" --message="${MESSAGE_CONTENT}" 36 | git umpr --no-edit 37 | done 38 | -------------------------------------------------------------------------------- /bin/scm-update: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Update all Git and Subversion repositories under the current directory. 3 | set -e 4 | 5 | CURRENT=$PWD 6 | # Set to newline to loop over find output correctly on spaced paths. 7 | IFS=" 8 | " 9 | 10 | echorun() { 11 | echo + "$@" 12 | "$@" 13 | } 14 | 15 | find -L "$CURRENT" \( -name .git -or -name .svn \) -print0 | while read -d '' -r SCM; do 16 | DIRECTORY="$(dirname "$SCM")" 17 | cd "$DIRECTORY" || continue 18 | if [ -d ../.svn ] || echo "$DIRECTORY" | grep -q "vendor/ruby"; then 19 | continue 20 | fi 21 | echo "== Updating $(basename "$DIRECTORY")" 22 | if [ -d .git/svn ]; then 23 | echorun git svn fetch 24 | echorun git svn rebase 25 | elif [ -d .git ]; then 26 | echorun git fetch --all 27 | if [ -n "$(git remote -v)" ]; then 28 | echorun git checkout --quiet master 29 | echorun git merge --no-edit --ff-only origin/master 30 | git branch --merged | grep -v '\*' | xargs -n 1 git branch -d 31 | fi 32 | elif [ -d .svn ]; then 33 | echorun svn update 34 | fi 35 | echo 36 | done 37 | -------------------------------------------------------------------------------- /bin/touchid-enable-pam-sudo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Enables sudo authentication using TouchID. 3 | 4 | pam_sudo_filename = "/etc/pam.d/sudo_local" 5 | pam_sudo_contents = File.read(pam_sudo_filename) 6 | if pam_sudo_contents.include?("pam_tid.so") 7 | unless ARGV.include?("--quiet") 8 | puts "TouchID PAM sudo module already enabled!" 9 | end 10 | exit 11 | end 12 | 13 | first_line = "# sudo_local: local config file which survives system update and is included for sudo" 14 | first_line_regex = /^#{first_line}$/ 15 | unless pam_sudo_contents.match?(first_line_regex) 16 | warn "Error: #{pam_sudo_filename} is not in the expected format!" 17 | abort 18 | end 19 | 20 | if Process.uid != 0 21 | puts "Rerunning #{$0} with sudo..." 22 | exec "sudo", $0 23 | end 24 | 25 | pam_sudo_contents.sub!( 26 | first_line_regex, 27 | "#{first_line}\nauth sufficient pam_tid.so" 28 | ) 29 | 30 | File.write(pam_sudo_filename, pam_sudo_contents) 31 | puts "TouchID PAM sudo module now enabled!" 32 | -------------------------------------------------------------------------------- /bin/upsy-desky: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Moves desk up or down. 3 | set -euo pipefail 4 | 5 | COMMAND="${1:-}" 6 | 7 | usage() { 8 | echo "Usage: ${0} " >&2 9 | exit 1 10 | } 11 | 12 | [[ -z "${COMMAND}" ]] && usage 13 | 14 | POWER_TYPE="$(/usr/bin/pmset -g batt | grep -Eo '(Battery|AC) Power')" 15 | [[ "${POWER_TYPE}" != "AC Power" ]] && exit 0 16 | 17 | LID_UP="$(/usr/sbin/ioreg -r -k AppleClamshellState | grep AppleClamshellState | awk '{print $4}')" 18 | [[ "${LID_UP}" != "No" ]] && exit 0 19 | 20 | DESK_IP_ADDRESS="10.0.0.18" 21 | 22 | if [[ "${COMMAND}" == "up" ]] 23 | then 24 | DESK_PRESS_URL="http://${DESK_IP_ADDRESS}/button/desk_preset_1/press" 25 | elif [[ "${COMMAND}" == "down" ]] 26 | then 27 | DESK_PRESS_URL="http://${DESK_IP_ADDRESS}/button/desk_preset_2/press" 28 | else 29 | usage 30 | fi 31 | 32 | /usr/bin/curl -X POST "${DESK_PRESS_URL}" &>/dev/null 33 | -------------------------------------------------------------------------------- /bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | BUNDLE_AUTO_INSTALL: "true" 4 | BUNDLE_DISABLE_SHARED_GEMS: "true" 5 | BUNDLE_ONLY_UPDATE_TO_NEWER_VERSIONS: "true" 6 | BUNDLE_DEFAULT_INSTALL_USES_PATH: "true" 7 | -------------------------------------------------------------------------------- /cursor-keybindings.json: -------------------------------------------------------------------------------- 1 | // Place your key bindings in this file to override the defaults 2 | [ 3 | { 4 | "key": "shift+cmd+k", 5 | "command": "-aipopup.action.modal.generate", 6 | "when": "editorFocus && !composerBarIsVisible && !composerControlPanelIsVisible" 7 | }, 8 | { 9 | "key": "cmd+k", 10 | "command": "-aipopup.action.modal.generate", 11 | "when": "editorFocus && !composerBarIsVisible && !composerControlPanelIsVisible" 12 | }, 13 | { 14 | "key": "cmd+k", 15 | "command": "-cursorai.action.generateInTerminal", 16 | "when": "terminalFocus && terminalHasBeenCreated || terminalFocus && terminalProcessSupported" 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /cursor-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.fontFamily": "'SF Mono', Monaco, Consolas, monospace", 3 | "editor.tabSize": 2, 4 | "editor.fontWeight": 500, 5 | "window.titleBarStyle": "native", 6 | "window.nativeTabs": true, 7 | "workbench.editor.showIcons": false, 8 | "problems.decorations.enabled": false, 9 | "editor.renderWhitespace": "all", 10 | "editor.multiCursorModifier": "ctrlCmd", 11 | "editor.wordWrap": "bounded", 12 | "editor.wordWrapColumn": 118, 13 | "editor.formatOnPaste": true, 14 | "editor.formatOnSave": true, 15 | "editor.formatOnSaveTimeout": 5000, 16 | "editor.formatOnType": true, 17 | "editor.renderLineHighlight": "none", 18 | "editor.glyphMargin": false, 19 | "editor.selectionHighlight": false, 20 | "editor.find.globalFindClipboard": true, 21 | "editor.renderControlCharacters": true, 22 | "editor.suggest.localityBonus": true, 23 | "editor.suggestSelection": "recentlyUsedByPrefix", 24 | "editor.quickSuggestions": { 25 | "other": true, 26 | "comments": true, 27 | "strings": true 28 | }, 29 | "editor.rulers": [ 30 | 80, 31 | 118 32 | ], 33 | "editor.scrollBeyondLastLine": false, 34 | "editor.showUnused": false, 35 | "editor.tabCompletion": "on", 36 | "workbench.editor.tabCloseButton": "left", 37 | "workbench.iconTheme": "macos-modern-big-sur-icon-theme", 38 | "workbench.startupEditor": "none", 39 | "workbench.editor.enablePreviewFromQuickOpen": false, 40 | "workbench.editor.closeOnFileDelete": false, 41 | "workbench.editor.revealIfOpen": true, 42 | "workbench.editor.tabSizing": "shrink", 43 | "workbench.fontAliasing": "auto", 44 | "window.restoreWindows": "all", 45 | "files.trimFinalNewlines": true, 46 | "files.trimTrailingWhitespace": true, 47 | "files.insertFinalNewline": true, 48 | "explorer.confirmDelete": false, 49 | "explorer.confirmDragAndDrop": false, 50 | "search.globalFindClipboard": false, 51 | "search.smartCase": true, 52 | "debug.inlineValues": "on", 53 | "breadcrumbs.enabled": true, 54 | "git.promptToSaveFilesBeforeCommit": "always", 55 | "spellright.language": [ 56 | "en_GB", 57 | "en_US" 58 | ], 59 | "spellright.groupDictionaries": false, 60 | "spellright.addToSystemDictionary": true, 61 | "search.runInExtensionHost": true, 62 | "spellright.documentTypes": [ 63 | "markdown", 64 | "latex", 65 | "plaintext", 66 | "git-commit" 67 | ], 68 | "git.enableCommitSigning": true, 69 | "git.fetchOnPull": true, 70 | "git.openDiffOnClick": false, 71 | "window.autoDetectColorScheme": true, 72 | "workbench.preferredLightColorTheme": "MacOS Modern Light - Xcode Default", 73 | "workbench.preferredDarkColorTheme": "MacOS Modern Dark - Xcode Default", 74 | "workbench.colorTheme": "MacOS Modern Light - Xcode Default", 75 | "workbench.productIconTheme": "macos-modern", 76 | "git.enableSmartCommit": true, 77 | "diffEditor.renderSideBySide": false, 78 | "terminal.integrated.profiles.linux": { 79 | "zsh": { 80 | "path": "zsh" 81 | } 82 | }, 83 | "terminal.integrated.profiles.osx": { 84 | "zsh": { 85 | "path": "zsh" 86 | } 87 | }, 88 | "security.workspace.trust.untrustedFiles": "open", 89 | "editor.inlineSuggest.enabled": true, 90 | "editor.bracketPairColorization.enabled": true, 91 | "terminal.integrated.scrollback": 8000, 92 | "terminal.integrated.persistentSessionScrollback": 1000, 93 | "files.autoSave": "afterDelay", 94 | "editor.guides.indentation": true, 95 | "editor.minimap.enabled": false, 96 | "debug.allowBreakpointsEverywhere": true, 97 | "debug.showBreakpointsInOverviewRuler": true, 98 | "workbench.editor.enablePreview": false, 99 | "githubPullRequests.pullBranch": "never", 100 | "github.copilot.enable": { 101 | "*": false 102 | }, 103 | "git.openRepositoryInParentFolders": "always", 104 | "[dockerfile]": { 105 | "editor.defaultFormatter": "ms-azuretools.vscode-docker" 106 | }, 107 | "go.toolsManagement.autoUpdate": true, 108 | "terminal.integrated.tabs.enabled": false, 109 | "terminal.integrated.enableMultiLinePasteWarning": "never", 110 | "workbench.editor.tabActionLocation": "left", 111 | "accessibility.signals.terminalBell": { 112 | "sound": "on" 113 | }, 114 | "terminal.integrated.enableVisualBell": true, 115 | "github.copilot.editor.enableAutoCompletions": true, 116 | "rubyLsp.formatter": "rubocop", 117 | "cursor.cmdk.useThemedDiffBackground": true, 118 | "cursor.diffs.useCharacterLevelDiffs": true, 119 | "[properties]": { 120 | "editor.formatOnSave": false, 121 | "editor.formatOnPaste": false, 122 | "editor.formatOnType": false 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /gemrc: -------------------------------------------------------------------------------- 1 | gem: --user-install 2 | -------------------------------------------------------------------------------- /gitattributes: -------------------------------------------------------------------------------- 1 | # Diff .t2d files as if they were XML (they are). 2 | *.t2d diff=xml 3 | -------------------------------------------------------------------------------- /gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | # Name used in commit messages. 3 | name = Mike McQuaid 4 | # Email used in commit messages. 5 | email = mike@mikemcquaid.com 6 | [include] 7 | path = ~/.gitconfig.local 8 | [github] 9 | # GitHub username for command-line tools. 10 | user = MikeMcQuaid 11 | [color] 12 | # Colour terminal command output when possible. 13 | ui = auto 14 | [gist] 15 | # Open GitHub Gist in browser after submission. 16 | browse = true 17 | [push] 18 | # Push to the set upstream branch being tracked by default. 19 | default = simple 20 | # Automatically create remotes if they don't exist. 21 | autoSetupRemote = true 22 | [pull] 23 | # Default to rebasing on pulls 24 | rebase = true 25 | [fetch] 26 | # Always prune when fetching (and pulling). 27 | prune = true 28 | # Always fetch all remotes. 29 | all = true 30 | # Write commit graph to speed up some repositories. 31 | writeCommitGraph = true 32 | [gc] 33 | # Write commit graph to speed up some repositories. 34 | writeCommitGraph = true 35 | [rerere] 36 | # Store and re-use manual conflict resolution changes. 37 | enabled = true 38 | # Automatically re-apply resolutions if seen again 39 | autoupdate = true 40 | [core] 41 | # Exclude everything this file. Used for general exclusions. 42 | excludesfile = ~/.gitignore 43 | # Set attributes on files. Used for general diff improvements. 44 | attributesfile = ~/.gitattributes 45 | # Don't prompt for commit messages for merge commits. 46 | mergeoptions = --no-edit 47 | # Speed up some repositories. 48 | commitGraph = true 49 | # Output unicode characters (e.g. emoji). 50 | quotepath = on 51 | [help] 52 | # Autocorrect mistyped commands. 53 | autocorrect = 1 54 | [alias] 55 | ## 'New' Commands 56 | # Unstage the changes in a given file. 57 | unstage = reset HEAD -- 58 | # View the current changes in the staging area. 59 | staged = diff --cached 60 | # Print the name of the current branch. 61 | current-branch = symbolic-ref --short HEAD 62 | # Print the name of the current upstream tracking branch. 63 | upstream = !git config --get branch.$(git current-branch).remote \ 64 | || echo origin 65 | # Cherry-pick a commit with your signature. 66 | sign = cherry-pick --signoff 67 | # List all current SVN externals for this repository. 68 | svn-externals = !git svn show-externals | grep -x \\"[^#].*\\" 69 | # Create a git:// server of the current repository. 70 | # WARNING: this gives all users read/write access 71 | # without authentication (so only use on trusted networks). 72 | serve = !git daemon --reuseaddr --export-all --base-path=. \ 73 | --verbose ./.git 74 | # Merge a branch and commit a merge commit (even if one 75 | # isn't needed) 76 | noff = merge --no-ff 77 | # Merge a branch with a merge commit and resolve any conflicts 78 | # always using that branch's version rather than the current branch. 79 | theirs = !git noff -Xtheirs 80 | # Fetch all branches and rebase the current branch against 81 | # upstream/HEAD. 82 | rebase-against-head = !git fetch --all \ 83 | && git rebase $(git upstream)/HEAD 84 | # Push the current branch upstream to origin using the same branch 85 | # name for the remote branch. 86 | upstream-current-branch = !git push --set-upstream origin \ 87 | $(git current-branch) 88 | # Create a pull request on GitHub using the `gh` command. 89 | pull-request = !rm -f .git/PULLREQ_EDITMSG && gh pr create --web 90 | # Upstream the current branch to origin and create a pull request 91 | # on GitHub. 92 | upstream-and-pull-request = !git upstream-current-branch \ 93 | && git pull-request 94 | # Get the current diff but show differences between characters 95 | # instead of just the differences between lines. 96 | word-diff = diff --word-diff 97 | # Push the current branch and set it as the default upstream branch. 98 | push-and-set-upstream = push --set-upstream 99 | # Create a new branch by checking out another branch. 100 | checkout-as-new-branch = checkout -b 101 | # Rebase against origin/HEAD and prompt for what operations 102 | # should be performed. 103 | interactively-rebase-against-origin-head = \ 104 | !git rev-parse --verify --quiet origin/HEAD >/dev/null || git remote set-head origin --auto && \ 105 | git rebase --interactive origin/HEAD 106 | # Show the commit log with a prettier, clearer history. 107 | pretty-one-line-log = log --graph --oneline --decorate 108 | # Commit any changes to files, squash them into the last commit 109 | # and update its date. 110 | fix-up-previous-commit = !git commit --all --amend \ 111 | --reuse-message=HEAD --date=\"$(date)\" #" 112 | # Commit a work-in-progress commit (to use with 113 | # fix-up-previous-commit) 114 | work-in-progress = commit -a -m 'WIP' 115 | # Merge a branch with a merge commit and use the more time-consuming 116 | # patience diff algorithm 117 | patience = !git noff -Xpatience 118 | # Hard reset branch to the upstream version. 119 | hard-reset = !git reset --hard $(git upstream)/$(git current-branch) 120 | # Assume the specified file is unchanged to stop changes 121 | # being seen by Git 122 | assume = update-index --assume-unchanged 123 | # No longer assume a specified file remains unchanged 124 | unassume = update-index --no-assume-unchanged 125 | # List all files that are assumed to be unchanged 126 | assumed = !git ls-files -v | grep '^[hsmrck?]' | cut -c 3- 127 | # Delete all non-master/main branches 128 | delete-merged = !git branch --merged | grep -v 'master' | grep -v 'main' | grep -v '*' | xargs -n 1 git branch -D 129 | # Get the merge-base compared to origin/HEAD 130 | merge-base-head = merge-base origin/HEAD HEAD 131 | # Diff against the current branch's merge-base 132 | diff-merge-base = !git diff $(git merge-base-head) 133 | # `brew style` changed files 134 | brew-style-changed = !git diff origin/HEAD --name-only | xargs brew style --fix --display-cop-names 135 | 136 | # Push the current branch upstream to MikeMcQuaid using the same 137 | # branch name for the remote branch. 138 | um = !(git remote -v | grep -q MikeMcQuaid || gh repo fork --remote --remote-name MikeMcQuaid) \ 139 | && gh repo set-default $(git remote get-url origin) \ 140 | && git push --set-upstream MikeMcQuaid $(git current-branch) 141 | # Push the current branch to MikeMcQuaid and open a pull request. 142 | umpr = !git um && gh pr create --web --head MikeMcQuaid:$(git current-branch) 143 | 144 | ## Shortened 'New' Commands 145 | fahr = !git fetch --all && git hard-reset 146 | rem = !git rebase-against-head 147 | wip = !git work-in-progress 148 | pr = !git upstream-and-pull-request 149 | up = !git upstream-current-branch 150 | fa = !git fetch --all 151 | pf = !git push --force-with-lease 152 | dm = !git diff-merge-base 153 | mb = !git merge-base-head 154 | w = !git word-diff 155 | u = !git push-and-set-upstream 156 | b = !git checkout-as-new-branch 157 | i = !git interactively-rebase-against-origin-head 158 | # `true` needed as the return status is wrong otherwise. 159 | l = !git pretty-one-line-log || true 160 | f = !git fix-up-previous-commit 161 | 162 | ## Shortened Existing Commands 163 | p = pull 164 | s = status --short --branch 165 | [instaweb] 166 | # Use the Ruby WEBRick library when creating a `git instaweb` 167 | # HTTP server. 168 | httpd = webrick 169 | [diff] 170 | # Use the better histogram diff algorithm 171 | algorithm = histogram 172 | # Use new diff algorithm to make e.g. function diffs look better. 173 | compactionheuristic = true 174 | # Use a different colour for moved lines than added/modified ones 175 | colorMoved = true 176 | # Use more Git-internals specific prefixes for diff hunks 177 | mnemonicPrefix = true 178 | # Detect if a file has been renamed 179 | renames = true 180 | [grep] 181 | # Use Perl regexes for grep 182 | patternType = perl 183 | [diff "xml"] 184 | textconv = xmllint --format --recover 185 | [mergetool] 186 | # Don't prompt before opening the merge tool. 187 | prompt = false 188 | # Don't keep backups of the merge tool inputs. 189 | keepBackup = false 190 | # Don't keep the merge tool temporary input/output files. 191 | keepTemporaries = false 192 | [apply] 193 | # Cleanup whitespace by default when apply patches. 194 | whitespace = fix 195 | [rebase] 196 | # Run `git stash` if needed before a `git rebase` 197 | autoStash = true 198 | # Auto-add `--autosquash` to `git rebase` 199 | autoSquash = true 200 | [url "git@github.com:"] 201 | # Use SSH for GitHub instead of https:// 202 | # Enable this in networks where https:// has issues. 203 | # insteadOf = https://github.com/ 204 | [credential] 205 | # Use my custom dotfiles credentials helper. 206 | helper = dotfiles 207 | [credential "https://github.com"] 208 | username = MikeMcQuaid 209 | [hub] 210 | # Use HTTPS rather than SSH protocol in Hub 211 | protocol = https 212 | # Settings for Git LFS 213 | [filter "lfs"] 214 | clean = git-lfs clean -- %f 215 | smudge = git-lfs smudge -- %f 216 | required = true 217 | process = git-lfs filter-process 218 | [commit] 219 | # Show the diff as a comment in the commit message template. 220 | verbose = true 221 | [branch] 222 | # Sort branches by committer date. 223 | sort = -committerdate 224 | [tag] 225 | # Sort tags by newest first 226 | sort = -version:refname 227 | [protocol] 228 | # Use Git v2 protocol for better performance 229 | version = 2 230 | [log] 231 | # Print more readable dates in `git log` 232 | date = human 233 | # git-delta settings 234 | [delta] 235 | features = line-numbers decorations 236 | syntax-theme = GitHub 237 | file-decoration-style = '' 238 | hunk-header-decoration-style = '' 239 | [merge] 240 | # Use 3-way diffs. 241 | conflictstyle = diff3 242 | [checkout] 243 | # If in doubt about the branch to checkout, pick origin. 244 | defaultRemote = origin 245 | [init] 246 | # Use same default branch name as GitHub. 247 | defaultBranch = main 248 | [safe] 249 | directory = /opt/homebrew 250 | directory = /opt/homebrew/Library/Taps/homebrew/homebrew-cask 251 | directory = /opt/homebrew/Library/Taps/homebrew/homebrew-command-not-found 252 | directory = /opt/homebrew/Library/Taps/homebrew/homebrew-core 253 | directory = /opt/homebrew/Library/Taps/homebrew/homebrew-portable-ruby 254 | directory = /opt/homebrew/Library/Taps/homebrew/homebrew-test-bot 255 | directory = /opt/homebrew/Library/Taps/workbrew/homebrew-tap 256 | directory = /opt/homebrew/Library/Taps/workbrew/homebrew-private 257 | directory = /home/linuxbrew/.linuxbrew/Homebrew 258 | -------------------------------------------------------------------------------- /gitconfig.local.macos: -------------------------------------------------------------------------------- 1 | [user] 2 | signingkey = ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3dbEVCqBzPGwz7v6vWfn4uM/of0Hpo8Ql9by+6q3nO 3 | [commit] 4 | # Sign commits with GPG 5 | gpgsign = true 6 | [gpg] 7 | # Do GPG signing with SSH keys 8 | format = ssh 9 | [gpg "ssh"] 10 | # Let 1Password act as GPG agent 11 | program = "/Applications/1Password.app/Contents/MacOS/op-ssh-sign" 12 | -------------------------------------------------------------------------------- /gitignore: -------------------------------------------------------------------------------- 1 | # Ignore files generated by Qt. 2 | moc_*.cpp 3 | qrc_*.cpp 4 | ui_*.h 5 | 6 | # Ignore files generated by QMake. 7 | .qmake.* 8 | qmake/*.d 9 | .moc/ 10 | .obj/ 11 | 12 | # Ignore compiler output files. 13 | config.log 14 | config.tests/ 15 | *.o 16 | *.pyc 17 | 18 | # Ignore text editor local configuration.. 19 | *.pro.user 20 | .tm_properties 21 | *.xcodeproj/project.xcworkspace/ 22 | *.xcodeproj/xcuserdata/ 23 | 24 | # Ignore temporary generated files. 25 | *.rej 26 | *.swp 27 | *~ 28 | 29 | # Ignore thumbnails metadata generated by macOS. 30 | .DS_Store 31 | 32 | # Ignore things generated by Bundler. 33 | **/.bundle/bin/ 34 | **/.bundle/cache/ 35 | vendor/ruby/ 36 | vendor/*/vendor/ruby/ 37 | 38 | # Ignore files generated by CMake. 39 | CMakeFiles/ 40 | CMakeCache.txt 41 | cmake_install.cmake 42 | install_manifest.txt 43 | 44 | # Ignore build directories. 45 | build/ 46 | 47 | # Ignore some testing files. 48 | test/js/functional/*.html 49 | -------------------------------------------------------------------------------- /grc/conf.rubybacktrace: -------------------------------------------------------------------------------- 1 | regexp=(from|:in)\s 2 | colours=green 3 | count=more 4 | - 5 | regexp=/[^: ]* 6 | colours=red 7 | count=once 8 | - 9 | regexp=:\d+ 10 | colours=yellow 11 | count=once 12 | - 13 | regexp=`[^']+' 14 | colours=blue 15 | count=once 16 | - 17 | regexp=: .*\)$ 18 | colours=magenta 19 | count=once 20 | -------------------------------------------------------------------------------- /irbrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MikeMcQuaid/dotfiles/6b16566ba76edbbc27a904beb2fca113a1d6b246/irbrc -------------------------------------------------------------------------------- /logout.sh: -------------------------------------------------------------------------------- 1 | pwd >~/.lastpwd 2 | -------------------------------------------------------------------------------- /rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format progress 3 | -------------------------------------------------------------------------------- /rubocop-oss.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | SuggestExtensions: false 3 | 4 | # To allow #/ or #: comments. 5 | Layout/LeadingCommentSpace: 6 | Enabled: false 7 | 8 | # This is flaky. 9 | Layout/RescueEnsureAlignment: 10 | Enabled: false 11 | 12 | # Too much noise. 13 | Layout/LineLength: 14 | Enabled: false 15 | 16 | # Too much noise. 17 | Style/FrozenStringLiteralComment: 18 | Enabled: false 19 | 20 | # Don't see the need. 21 | Style/NumericLiterals: 22 | Enabled: false 23 | 24 | # Don't see the need for these, too much noise. 25 | Style/PerlBackrefs: 26 | Enabled: false 27 | 28 | # I prefer this approach. 29 | Style/StringLiterals: 30 | EnforcedStyle: double_quotes 31 | 32 | # Consistency with above. 33 | Style/StringLiteralsInInterpolation: 34 | EnforcedStyle: double_quotes 35 | 36 | # I prefer this approach. 37 | Style/TrailingCommaInArrayLiteral: 38 | EnforcedStyleForMultiline: consistent_comma 39 | Style/TrailingCommaInHashLiteral: 40 | EnforcedStyleForMultiline: consistent_comma 41 | 42 | # Too much noise 43 | Metrics: 44 | Enabled: false 45 | -------------------------------------------------------------------------------- /rubocop-work.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | SuggestExtensions: true 3 | 4 | # To allow #/ or #: comments. 5 | Layout/LeadingCommentSpace: 6 | Enabled: false 7 | 8 | # This is flaky. 9 | Layout/RescueEnsureAlignment: 10 | Enabled: false 11 | 12 | # Too much noise. 13 | Layout/LineLength: 14 | Enabled: false 15 | 16 | # Too much noise. 17 | Style/FrozenStringLiteralComment: 18 | Enabled: false 19 | 20 | # Don't see the need. 21 | Style/NumericLiterals: 22 | Enabled: false 23 | 24 | # Don't see the need for these, too much noise. 25 | Style/PerlBackrefs: 26 | Enabled: false 27 | 28 | # I prefer this approach. 29 | Style/StringLiterals: 30 | EnforcedStyle: double_quotes 31 | 32 | # Consistency with above. 33 | Style/StringLiteralsInInterpolation: 34 | EnforcedStyle: double_quotes 35 | 36 | # I prefer this approach. 37 | Style/TrailingCommaInArrayLiteral: 38 | EnforcedStyleForMultiline: consistent_comma 39 | Style/TrailingCommaInHashLiteral: 40 | EnforcedStyleForMultiline: consistent_comma 41 | 42 | # Too much noise 43 | Metrics: 44 | Enabled: false 45 | -------------------------------------------------------------------------------- /screenrc: -------------------------------------------------------------------------------- 1 | termcapinfo xterm* ti@:te@ 2 | -------------------------------------------------------------------------------- /script/linux-after-setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run by script/setup on Linux 3 | 4 | cd "$(dirname "$0")/.." || exit 5 | 6 | # Use ZSH as my default Shell 7 | if [[ "$SHELL" != "/usr/bin/zsh" ]]; then 8 | chsh -s /usr/bin/zsh mike 9 | fi 10 | 11 | # Install stuff from apt-get if needed 12 | if command -v "apt-get" >/dev/null; then 13 | echo "Updating apt-get..." 14 | sudo apt-get update 15 | if ! command -v "gh" >/dev/null; then 16 | sudo apt-get install -y gh 17 | fi 18 | if [[ -n "$WSL" ]]; then 19 | if ! command -v "wslview" >/dev/null; then 20 | sudo apt-get install -y wslu 21 | fi 22 | fi 23 | fi 24 | 25 | # Add GitHub credentials if missing 26 | if command -v "gh" >/dev/null && ! gh auth token &>/dev/null; then 27 | gh auth login --git-protocol https --hostname github.com --web 28 | fi 29 | -------------------------------------------------------------------------------- /script/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Install all dotfiles into the home directory 3 | 4 | if [[ -L "$0" ]]; then 5 | SCRIPTSETUP="$(readlink "$0")" 6 | else 7 | SCRIPTSETUP="$0" 8 | fi 9 | 10 | DOTFILESDIRREL=$(dirname "${SCRIPTSETUP}") 11 | cd "${DOTFILESDIRREL}"/.. || exit 12 | DOTFILESDIR=$(pwd -P) 13 | 14 | [[ "$(uname -s)" = "Darwin" ]] && export MACOS=1 && export UNIX=1 15 | [[ "$(uname -s)" = "Linux" ]] && export LINUX=1 && export UNIX=1 16 | uname -s | grep -q "_NT-" && export WINDOWS=1 17 | [[ -n "$LINUX" ]] && grep -qEi "(Microsoft|WSL)" /proc/version && export WSL=1 18 | 19 | if [[ -n "${MACOS}" ]]; then 20 | VSCODE="${HOME}/Library/Application Support/Code/User" 21 | CURSOR="${HOME}/Library/Application Support/Cursor/User" 22 | elif [[ -n "${LINUX}" ]]; then 23 | VSCODE="${HOME}/.config/Code/User" 24 | CURSOR="${HOME}/.config/Cursor/User" 25 | elif [[ -n "${WINDOWS}" ]]; then 26 | VSCODE="${APPDATA}/Code/User" 27 | CURSOR="${APPDATA}/Cursor/User" 28 | fi 29 | ZED="${HOME}/.config/zed/settings.json" 30 | 31 | for DOTFILE in *; do 32 | HOMEFILE="${HOME}/.${DOTFILE}" 33 | [[ -d "${DOTFILE}" ]] && DOTFILE="${DOTFILE}/" 34 | DIRFILE="${DOTFILESDIR}/${DOTFILE}" 35 | 36 | # Don't mess with Codespaces' default SSH setup. 37 | if [[ -n "${CODESPACES}" ]]; then 38 | echo "${DOTFILE}" | grep -E -q '^ssh/' && continue 39 | fi 40 | 41 | # Don't try to install documentation/script files 42 | echo "${DOTFILE}" | grep -E -q '(^script/$|\.txt$|\.md$)' && continue 43 | 44 | # Only install gitconfig.local.macos on macOS 45 | if echo "${DOTFILE}" | grep -q 'gitconfig.local.macos'; then 46 | [[ -z "${MACOS}" ]] && continue 47 | HOMEFILE="${HOME}/.gitconfig.local" 48 | fi 49 | 50 | # Fixup VSCode settings path 51 | echo "${DOTFILE}" | grep -q 'vscode-settings' && 52 | HOMEFILE="${VSCODE}/settings.json" && 53 | mkdir -p "${VSCODE}" 54 | 55 | # Fixup Cursor settings path 56 | echo "${DOTFILE}" | grep -q 'cursor-settings' && 57 | HOMEFILE="${CURSOR}/settings.json" && 58 | mkdir -p "${CURSOR}" 59 | 60 | # Fixup Cursor keybindings path 61 | echo "${DOTFILE}" | grep -q 'vscode-keybindings' && 62 | HOMEFILE="${CURSOR}/keybindings.json" && 63 | mkdir -p "${CURSOR}" 64 | 65 | # Fixup Zed settings path 66 | echo "${DOTFILE}" | grep -q 'zed-settings' && 67 | HOMEFILE="${ZED}/settings.json" && 68 | mkdir -p "${ZED}" 69 | 70 | # Remove .sh extensions 71 | echo "${DOTFILE}" | grep -q '\.sh' && 72 | HOMEFILE="${HOME}/.$(echo "${DOTFILE}" | sed -e 's/\.sh//')" 73 | 74 | # Fixup RuboCop configuration (if possible) 75 | if echo "${DOTFILE}" | grep -q 'rubocop-work.yml'; then 76 | HOMEWORK="${HOME}/Workbrew" 77 | [[ -d "${HOMEWORK}" ]] || continue 78 | HOMEFILE="${HOMEWORK}/.rubocop.yml" 79 | elif echo "${DOTFILE}" | grep -q 'rubocop-oss.yml'; then 80 | HOMEOSS="${HOME}/OSS" 81 | [[ -d "${HOMEOSS}" ]] || continue 82 | HOMEFILE="${HOMEOSS}/.rubocop.yml" 83 | fi 84 | 85 | if [[ -n "${UNIX}" ]]; then 86 | if ! [[ -d "${DOTFILE}" ]]; then 87 | if [[ -L "${HOMEFILE}" ]]; then 88 | ln -sf "${DIRFILE}" "${HOMEFILE}" 89 | else 90 | ln -svf "${DIRFILE}" "${HOMEFILE}" 91 | fi 92 | elif [[ -L "${HOMEFILE}" ]]; then 93 | rm -r "${HOMEFILE}" 2>/dev/null 94 | ln -s "${DIRFILE}" "${HOMEFILE}" 95 | else 96 | rm -rv "${HOMEFILE}" 2>/dev/null 97 | ln -sv "${DIRFILE}" "${HOMEFILE}" 98 | fi 99 | else 100 | cp -rv "${DIRFILE}" "${HOMEFILE}" 101 | fi 102 | done 103 | 104 | HOMEDOTFILES="${HOME}/.dotfiles" 105 | if [[ "${DOTFILESDIR}" != "${HOMEDOTFILES}" ]]; then 106 | rm -f "${HOMEDOTFILES}" 107 | ln -sf "${DOTFILESDIR}" "${HOMEDOTFILES}" 108 | fi 109 | 110 | # Setup my home directory how I like it 111 | mkdir -pv ~/OSS ~/Workbrew 112 | if command -v "brew" >/dev/null && ! [[ -d ~/OSS/Homebrew ]]; then 113 | ln -vsf "$(brew --repository)" ~/OSS/Homebrew 114 | fi 115 | if ! [[ -d ~/OSS/dotfiles ]]; then 116 | ln -vsf "${DOTFILESDIR}" ~/OSS/dotfiles 117 | fi 118 | 119 | if [[ -n "${LINUX}" ]]; then 120 | exec script/linux-after-setup 121 | fi -------------------------------------------------------------------------------- /script/strap-after-setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run by Strap after installing Brewfile dependencies 3 | 4 | cd "$(dirname "$0")/.." || exit 5 | 6 | # Close terminal windows on successful exit code 7 | /usr/libexec/PlistBuddy ~/Library/Preferences/com.apple.Terminal.plist -c \ 8 | "Set 'Window Settings':Basic:shellExitAction 1" 9 | 10 | # Use ZSH as my default Shell 11 | if [ "$SHELL" != "/bin/zsh" ]; then 12 | chsh -s /bin/zsh mike 13 | fi 14 | 15 | # Enable Touch ID for sudo 16 | bin/touchid-enable-pam-sudo --quiet 17 | 18 | # Add GitHub credentials if missing 19 | if command -v "gh" >/dev/null && ! gh auth token &>/dev/null; then 20 | gh auth login --git-protocol https --hostname github.com --web 21 | fi 22 | 23 | # Ensure auto-update is always enabled 24 | if [ "$(defaults read /Library/Preferences/com.apple.commerce.plist AutoUpdate)" != "1" ]; then 25 | sudo defaults write /Library/Preferences/com.apple.commerce.plist AutoUpdate 1 26 | fi 27 | -------------------------------------------------------------------------------- /shprofile.sh: -------------------------------------------------------------------------------- 1 | # 077 would be more secure, but 022 is more useful. 2 | umask 022 3 | 4 | # Save more history 5 | export HISTSIZE="100000" 6 | export SAVEHIST="100000" 7 | 8 | # OS variables 9 | [ "$(uname -s)" = "Darwin" ] && export MACOS=1 && export UNIX=1 10 | [ "$(uname -s)" = "Linux" ] && export LINUX=1 && export UNIX=1 11 | uname -s | grep -q "_NT-" && export WINDOWS=1 12 | [ "$LINUX" ] && grep -qEi "(Microsoft|WSL)" /proc/version && export WSL=1 13 | 14 | # Fix systems missing $USER 15 | [ -z "$USER" ] && export USER="$(whoami)" 16 | 17 | # Count CPUs for Make jobs 18 | if [ $MACOS ]; then 19 | export CPUCOUNT="$(sysctl -n hw.ncpu)" 20 | elif [ $LINUX ]; then 21 | export CPUCOUNT="$(getconf _NPROCESSORS_ONLN)" 22 | else 23 | export CPUCOUNT=1 24 | fi 25 | 26 | if [ "$CPUCOUNT" -gt 1 ]; then 27 | export MAKEFLAGS="-j$CPUCOUNT" 28 | export BUNDLE_JOBS="$CPUCOUNT" 29 | fi 30 | 31 | # Setup Homebrew 32 | PATH="/home/linuxbrew/.linuxbrew/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" 33 | if which brew &>/dev/null; then 34 | eval "$(brew shellenv)" 35 | fi 36 | 37 | # Enable Terminal.app folder icons 38 | [ "$TERM_PROGRAM" = "Apple_Terminal" ] && export TERMINALAPP=1 39 | if [ $TERMINALAPP ]; then 40 | set_terminal_app_pwd() { 41 | # Tell Terminal.app about each directory change. 42 | printf '\e]7;%s\a' "$(echo "file://$HOST$PWD" | sed -e 's/ /%20/g')" 43 | } 44 | fi 45 | [ -s ~/.lastpwd ] && [ "$PWD" = "$HOME" ] && 46 | builtin cd "$(cat ~/.lastpwd)" 2>/dev/null 47 | [ $TERMINALAPP ] && set_terminal_app_pwd 48 | 49 | SHPROFILE_LOADED=1 50 | -------------------------------------------------------------------------------- /shrc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # shellcheck disable=SC2155 3 | 4 | # Colourful manpages 5 | export LESS_TERMCAP_mb=$'\E[01;31m' 6 | export LESS_TERMCAP_md=$'\E[01;31m' 7 | export LESS_TERMCAP_me=$'\E[0m' 8 | export LESS_TERMCAP_se=$'\E[0m' 9 | export LESS_TERMCAP_so=$'\E[01;44;33m' 10 | export LESS_TERMCAP_ue=$'\E[0m' 11 | export LESS_TERMCAP_us=$'\E[01;32m' 12 | 13 | # Set to avoid `env` output from changing console colour 14 | export LESS_TERMEND=$'\E[0m' 15 | 16 | # Print field by number 17 | field() { 18 | ruby -ane "puts \$F[$1]" 19 | } 20 | 21 | # Setup PATH 22 | 23 | # Remove from anywhere in PATH 24 | remove_from_path() { 25 | [[ -d "$1" ]] || return 26 | PATHSUB=":${PATH}:" 27 | PATHSUB=${PATHSUB//:$1:/:} 28 | PATHSUB=${PATHSUB#:} 29 | PATHSUB=${PATHSUB%:} 30 | export PATH="${PATHSUB}" 31 | } 32 | 33 | # Add to the start of PATH if it exists 34 | add_to_path_start() { 35 | [[ -d "$1" ]] || return 36 | remove_from_path "$1" 37 | export PATH="$1:${PATH}" 38 | } 39 | 40 | # Add to the end of PATH if it exists 41 | add_to_path_end() { 42 | [[ -d "$1" ]] || return 43 | remove_from_path "$1" 44 | export PATH="${PATH}:$1" 45 | } 46 | 47 | # Add to PATH even if it doesn't exist 48 | force_add_to_path_start() { 49 | remove_from_path "$1" 50 | export PATH="$1:${PATH}" 51 | } 52 | 53 | quiet_which() { 54 | command -v "$1" >/dev/null 55 | } 56 | 57 | if [[ -n "${MACOS}" ]]; then 58 | add_to_path_start "/opt/homebrew/bin" 59 | elif [[ -n "${LINUX}" ]]; then 60 | add_to_path_start "/home/linuxbrew/.linuxbrew/bin" 61 | fi 62 | 63 | add_to_path_start "/opt/workbrew/bin" 64 | add_to_path_start "/usr/local/bin" 65 | add_to_path_end "${HOME}/.dotfiles/bin" 66 | 67 | # Setup Go development 68 | export GOPATH="${HOME}/.gopath" 69 | add_to_path_end "${GOPATH}/bin" 70 | 71 | # Aliases 72 | alias mkdir="mkdir -vp" 73 | alias df="df -H" 74 | alias rm="rm -iv" 75 | alias mv="mv -iv" 76 | alias cp="cp -irv" 77 | alias du="du -sh" 78 | alias make="nice make" 79 | alias less="less --ignore-case --raw-control-chars" 80 | alias rsync="rsync --partial --progress --human-readable --compress" 81 | alias rg="rg --colors 'match:style:nobold' --colors 'path:style:nobold'" 82 | alias be="bundle exec" 83 | alias sha256="shasum -a 256" 84 | alias perlsed="perl -p -e" 85 | 86 | # Command-specific stuff 87 | if quiet_which brew; then 88 | eval "$(brew shellenv)" 89 | 90 | export HOMEBREW_DEVELOPER=1 91 | export HOMEBREW_BOOTSNAP=1 92 | export HOMEBREW_BUNDLE_INSTALL_CLEANUP=1 93 | export HOMEBREW_BUNDLE_DUMP_DESCRIBE=1 94 | export HOMEBREW_NO_ENV_HINTS=1 95 | export HOMEBREW_CLEANUP_PERIODIC_FULL_DAYS=1 96 | export HOMEBREW_CLEANUP_MAX_AGE_DAYS=30 97 | export HOMEBREW_WORKBREW_LOCKFILE=1 98 | export HOMEBREW_UPGRADE_GREEDY_CASKS="cursor" 99 | 100 | add_to_path_end "${HOMEBREW_PREFIX}/Library/Homebrew/shims/gems" 101 | 102 | # Specifically want this to expand when defined, not when run. 103 | # shellcheck disable=SC2139 104 | alias homebrew="${HOMEBREW_PREFIX}/bin/brew" 105 | alias portableruby="${HOMEBREW_PREFIX}/Library/Homebrew/vendor/portable-ruby/current/bin/ruby" 106 | alias portablebundle="${HOMEBREW_PREFIX}/Library/Homebrew/vendor/portable-ruby/current/bin/bundle" 107 | 108 | alias workbrew='/opt/workbrew/bin/brew' 109 | alias workbrewdo='sudo --set-home --preserve-env --user=workbrew --' 110 | alias workbrewpermissions='sudo chown -R workbrew /opt/homebrew; sudo chmod -R g+w /opt/homebrew; sudo chmod -R g-w /opt/homebrew/var' 111 | alias youtube-dl='yt-dlp' 112 | alias bbe="brew bundle exec --" 113 | alias ebbe='eval "$(brew bundle env)"' 114 | 115 | alias hbc='cd $HOMEBREW_REPOSITORY/Library/Taps/homebrew/homebrew-core' 116 | fi 117 | 118 | if quiet_which delta; then 119 | export GIT_PAGER='delta' 120 | else 121 | # shellcheck disable=SC2016 122 | export GIT_PAGER='less -+$LESS -RX' 123 | fi 124 | 125 | if quiet_which eza; then 126 | alias ls="eza --classify --group --git" 127 | elif [[ -n "${MACOS}" ]]; then 128 | alias ls="ls -F" 129 | else 130 | alias ls="ls -F --color=auto" 131 | fi 132 | 133 | if quiet_which bat; then 134 | export BAT_THEME="ansi" 135 | alias cat="bat" 136 | export HOMEBREW_BAT=1 137 | fi 138 | 139 | if quiet_which prettyping; then 140 | alias ping="prettyping --nolegend" 141 | fi 142 | 143 | if quiet_which dust; then 144 | alias du="dust" 145 | fi 146 | 147 | if quiet_which duf; then 148 | alias df="duf" 149 | fi 150 | 151 | if quiet_which htop; then 152 | alias top="sudo htop" 153 | fi 154 | 155 | # Configure environment 156 | export CLICOLOR=1 157 | 158 | # OS-specific configuration 159 | if [[ -n "${MACOS}" ]]; then 160 | export GREP_OPTIONS="--color=auto" 161 | export VAGRANT_DEFAULT_PROVIDER="vmware_fusion" 162 | export HOMEBREW_ENFORCE_SBOM=1 163 | 164 | alias locate="mdfind -name" 165 | alias finder-hide="setfile -a V" 166 | 167 | # output what's listening on the supplied port 168 | on-port() { 169 | sudo lsof -nP -i4TCP:"$1" 170 | } 171 | 172 | # make no-argument find Just Work. 173 | find() { 174 | local arg 175 | local path_arg 176 | local dot_arg 177 | 178 | for arg; do 179 | [[ ${arg} =~ "^-" ]] && break 180 | path_arg="${arg}" 181 | done 182 | 183 | [[ -z "${path_arg}" ]] && dot_arg="." 184 | 185 | command find ${dot_arg} "$@" 186 | } 187 | 188 | # Only run this if it's not already running 189 | pgrep -fq touchid-enable-pam-sudo || touchid-enable-pam-sudo --quiet 190 | elif [[ -n "${LINUX}" ]]; then 191 | quiet_which keychain && eval "$(keychain -q --eval --agents ssh id_rsa)" 192 | 193 | # Run dircolors if it exists 194 | quiet_which dircolors && eval "$(dircolors -b)" 195 | 196 | add_to_path_end "/data/github/shell/bin" 197 | add_to_path_start "/workspaces/github/bin" 198 | 199 | alias su="/bin/su -" 200 | alias open="xdg-open" 201 | elif [[ -n "${WINDOWS}" ]]; then 202 | open() { 203 | # shellcheck disable=SC2145 204 | cmd /C"$@" 205 | } 206 | fi 207 | 208 | # Run rbenv/nodenv if they exist 209 | if quiet_which rbenv; then 210 | shims="$(rbenv root)/shims" 211 | if ! [[ -d "${shims}" ]]; then 212 | rbenv rehash 213 | fi 214 | add_to_path_start "${shims}" 215 | fi 216 | 217 | if quiet_which nodenv; then 218 | shims="$(nodenv root)/shims" 219 | if ! [[ -d "${shims}" ]]; then 220 | nodenv rehash 221 | fi 222 | add_to_path_start "${shims}" 223 | fi 224 | 225 | # Load GITHUB_TOKEN from gh 226 | if quiet_which gh; then 227 | export GITHUB_TOKEN="$(gh auth token)" 228 | export GH_TOKEN="${GITHUB_TOKEN}" 229 | export HOMEBREW_GITHUB_API_TOKEN="${GITHUB_TOKEN}" 230 | export JEKYLL_GITHUB_TOKEN="${GITHUB_TOKEN}" 231 | fi 232 | 233 | # Set up editor 234 | if quiet_which cursor; then 235 | export EDITOR="cursor" 236 | alias code="cursor" 237 | elif quiet_which code; then 238 | export EDITOR="code" 239 | fi 240 | 241 | if quiet_which code; then 242 | export GIT_EDITOR="${EDITOR} -w" 243 | export SVN_EDITOR="${GIT_EDITOR}" 244 | 245 | # Edit Rails credentials in VSCode 246 | rails-credentials-edit-production() { 247 | EDITOR="${EDITOR} -w" bundle exec rails credentials:edit --environment production 248 | } 249 | rails-credentials-edit-development() { 250 | EDITOR="${EDITOR} -w" bundle exec rails credentials:edit --environment development 251 | } 252 | else 253 | export EDITOR="vim" 254 | fi 255 | 256 | # Save directory changes 257 | cd() { 258 | builtin cd "$@" || return 259 | [[ -n "${TERMINALAPP}" ]] && command -v set_terminal_app_pwd >/dev/null && 260 | set_terminal_app_pwd 261 | pwd >"${HOME}/.lastpwd" 262 | ls 263 | } 264 | 265 | # Use ruby-prof to generate a call stack 266 | ruby-call-stack() { 267 | ruby-prof --printer=call_stack --file=call_stack.html -- "$@" 268 | } 269 | 270 | # Pretty-print JSON files 271 | json() { 272 | [[ -n "$1" ]] || return 273 | cat "$1" | jq . 274 | } 275 | 276 | # Pretty-print Homebrew install receipts 277 | receipt() { 278 | [[ -n "$1" ]] || return 279 | json "${HOMEBREW_PREFIX}/opt/$1/INSTALL_RECEIPT.json" 280 | } 281 | 282 | # Move files to the Trash folder 283 | trash() { 284 | mv "$@" "${HOME}/.Trash/" 285 | } 286 | 287 | # GitHub API shortcut 288 | github-api-curl() { 289 | curl -H "Authorization: token ${GITHUB_TOKEN}" "https://api.github.com/$1" | jq . 290 | } 291 | 292 | # GitHub Packages shortcut 293 | github-packages-curl() { 294 | curl -H "Authorization: Bearer QQ==" -H "Accept: application/vnd.oci.image.index.v1+json" "$@" | jq . 295 | } 296 | 297 | # Spit out Okta keychain password 298 | okta-keychain() { 299 | security find-generic-password -l device_trust '-w' 300 | } 301 | 302 | # Clear entire screen buffer 303 | clearer() { 304 | tput reset 305 | } 306 | 307 | # Sit/stand desk 308 | desk_stand() { 309 | curl -X POST http://10.0.0.45/button/desk_preset_1/press 310 | } 311 | desk_sit() { 312 | curl -X POST http://10.0.0.45/button/desk_preset_2/press 313 | } 314 | -------------------------------------------------------------------------------- /ssh/authorized_keys: -------------------------------------------------------------------------------- 1 | id_ed25519.pub -------------------------------------------------------------------------------- /ssh/config: -------------------------------------------------------------------------------- 1 | Host github.com gitlab.com 2 | User git 3 | 4 | Host * 5 | StrictHostKeyChecking ask 6 | VerifyHostKeyDNS ask 7 | NoHostAuthenticationForLocalhost yes 8 | IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" 9 | ControlMaster auto 10 | ControlPath /tmp/ssh-%r@%h:%p.socket 11 | -------------------------------------------------------------------------------- /ssh/id_ed25519.pub: -------------------------------------------------------------------------------- 1 | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3dbEVCqBzPGwz7v6vWfn4uM/of0Hpo8Ql9by+6q3nO mike@mikemcquaid.com 2 | -------------------------------------------------------------------------------- /vimrc: -------------------------------------------------------------------------------- 1 | " Switch syntax highlighting on 2 | syntax on 3 | 4 | " always show ruler at bottom 5 | set ruler 6 | 7 | " don't make foo~ files 8 | set nobackup 9 | 10 | " searching 11 | set ignorecase 12 | set smartcase 13 | set hlsearch 14 | set incsearch 15 | 16 | " indentation 17 | set autoindent 18 | set smarttab 19 | if has("autocmd") 20 | filetype on 21 | filetype indent on 22 | filetype plugin on 23 | endif 24 | 25 | " whitespace 26 | if has("multi_byte") 27 | set encoding=utf-8 28 | set list listchars=tab:»·,trail:· 29 | else 30 | set list listchars=tab:>-,trail:. 31 | endif 32 | 33 | " disable mouse integration 34 | set mouse= 35 | -------------------------------------------------------------------------------- /vscode-settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.fontFamily": "'SF Mono', Monaco, Consolas, monospace", 3 | "editor.tabSize": 2, 4 | "editor.fontWeight": 500, 5 | "window.titleBarStyle": "native", 6 | "window.nativeTabs": true, 7 | "workbench.editor.showIcons": false, 8 | "problems.decorations.enabled": false, 9 | "editor.renderWhitespace": "all", 10 | "editor.multiCursorModifier": "ctrlCmd", 11 | "editor.wordWrap": "bounded", 12 | "editor.wordWrapColumn": 118, 13 | "editor.formatOnPaste": true, 14 | "editor.formatOnSave": true, 15 | "editor.formatOnSaveTimeout": 5000, 16 | "editor.formatOnType": true, 17 | "editor.renderLineHighlight": "none", 18 | "editor.glyphMargin": false, 19 | "editor.selectionHighlight": false, 20 | "editor.find.globalFindClipboard": true, 21 | "editor.renderControlCharacters": true, 22 | "editor.suggest.localityBonus": true, 23 | "editor.suggestSelection": "recentlyUsedByPrefix", 24 | "editor.quickSuggestions": { 25 | "other": true, 26 | "comments": true, 27 | "strings": true 28 | }, 29 | "editor.rulers": [ 30 | 80, 31 | 118 32 | ], 33 | "editor.scrollBeyondLastLine": false, 34 | "editor.showUnused": false, 35 | "editor.tabCompletion": "on", 36 | "workbench.editor.tabCloseButton": "left", 37 | "workbench.iconTheme": "macos-modern-big-sur-icon-theme", 38 | "workbench.startupEditor": "none", 39 | "workbench.editor.enablePreviewFromQuickOpen": false, 40 | "workbench.editor.closeOnFileDelete": false, 41 | "workbench.editor.revealIfOpen": true, 42 | "workbench.editor.tabSizing": "shrink", 43 | "workbench.fontAliasing": "auto", 44 | "window.restoreWindows": "all", 45 | "files.trimFinalNewlines": true, 46 | "files.trimTrailingWhitespace": true, 47 | "files.insertFinalNewline": true, 48 | "explorer.confirmDelete": false, 49 | "explorer.confirmDragAndDrop": false, 50 | "search.globalFindClipboard": false, 51 | "search.smartCase": true, 52 | "debug.inlineValues": "on", 53 | "breadcrumbs.enabled": true, 54 | "git.promptToSaveFilesBeforeCommit": "always", 55 | "spellright.language": [ 56 | "en_GB", 57 | "en_US" 58 | ], 59 | "spellright.groupDictionaries": false, 60 | "spellright.addToSystemDictionary": true, 61 | "search.runInExtensionHost": true, 62 | "spellright.documentTypes": [ 63 | "markdown", 64 | "latex", 65 | "plaintext", 66 | "git-commit" 67 | ], 68 | "git.enableCommitSigning": true, 69 | "git.fetchOnPull": true, 70 | "git.openDiffOnClick": false, 71 | "window.autoDetectColorScheme": true, 72 | "workbench.preferredLightColorTheme": "MacOS Modern Light - Xcode Default", 73 | "workbench.preferredDarkColorTheme": "MacOS Modern Dark - Xcode Default", 74 | "workbench.colorTheme": "MacOS Modern Light - Xcode Default", 75 | "workbench.productIconTheme": "macos-modern", 76 | "git.enableSmartCommit": true, 77 | "diffEditor.renderSideBySide": false, 78 | "terminal.integrated.profiles.linux": { 79 | "zsh": { 80 | "path": "zsh" 81 | } 82 | }, 83 | "terminal.integrated.profiles.osx": { 84 | "zsh": { 85 | "path": "zsh" 86 | } 87 | }, 88 | "security.workspace.trust.untrustedFiles": "open", 89 | "editor.inlineSuggest.enabled": true, 90 | "editor.bracketPairColorization.enabled": true, 91 | "terminal.integrated.scrollback": 8000, 92 | "terminal.integrated.persistentSessionScrollback": 1000, 93 | "files.autoSave": "afterDelay", 94 | "editor.guides.indentation": true, 95 | "editor.minimap.enabled": false, 96 | "debug.allowBreakpointsEverywhere": true, 97 | "debug.showBreakpointsInOverviewRuler": true, 98 | "workbench.editor.enablePreview": false, 99 | "githubPullRequests.pullBranch": "never", 100 | "github.copilot.enable": { 101 | "*": true, 102 | "markdown": true 103 | }, 104 | "git.openRepositoryInParentFolders": "always", 105 | "[dockerfile]": { 106 | "editor.defaultFormatter": "ms-azuretools.vscode-docker" 107 | }, 108 | "go.toolsManagement.autoUpdate": true, 109 | "terminal.integrated.tabs.enabled": false, 110 | "terminal.integrated.enableMultiLinePasteWarning": "never", 111 | "workbench.editor.tabActionLocation": "left", 112 | "accessibility.signals.terminalBell": { 113 | "sound": "on" 114 | }, 115 | "terminal.integrated.enableVisualBell": true, 116 | "github.copilot.editor.enableAutoCompletions": true, 117 | "rubyLsp.formatter": "rubocop", 118 | "[properties]": { 119 | "editor.formatOnSave": false, 120 | "editor.formatOnPaste": false, 121 | "editor.formatOnType": false 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /zed-settings.json: -------------------------------------------------------------------------------- 1 | // Zed settings 2 | // 3 | // For information on how to configure Zed, see the Zed 4 | // documentation: https://zed.dev/docs/configuring-zed 5 | // 6 | // To see all of Zed's default settings without changing your 7 | // custom settings, run the `open default settings` command 8 | // from the command palette or from `Zed` application menu. 9 | { 10 | "features": { 11 | "edit_prediction_provider": "zed" 12 | }, 13 | "autosave": "on_focus_change", 14 | "buffer_font_family": "SF Mono", 15 | "buffer_font_weight": 500, 16 | "buffer_font_size": 12, 17 | "current_line_highlight": "gutter", 18 | "cursor_blink": false, 19 | "default_dock_anchor": "right", 20 | "languages": { 21 | "Ruby": { 22 | "language_servers": [ 23 | "ruby-lsp", 24 | "rubocop", 25 | "!solargraph", 26 | "..." 27 | ] 28 | } 29 | }, 30 | "preferred_line_length": 118, 31 | "show_whitespaces": "boundary", 32 | "tab_bar": { 33 | "show_nav_history_buttons": false 34 | }, 35 | "tabs": { 36 | "close_position": "left", 37 | "file_icons": true, 38 | "git_status": true 39 | }, 40 | "theme": { 41 | "mode": "system", 42 | "light": "macOS Classic Light", 43 | "dark": "macOS Classic Dark" 44 | }, 45 | "terminal": { 46 | "dock": "right" 47 | }, 48 | "ui_font_size": 16, 49 | "wrap_guides": [ 50 | 118 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /zlogout.sh: -------------------------------------------------------------------------------- 1 | source ~/.logout 2 | -------------------------------------------------------------------------------- /zprofile.sh: -------------------------------------------------------------------------------- 1 | # load shared shell configuration 2 | [ -n "$SHPROFILE_LOADED" ] || source ~/.shprofile 3 | 4 | # Enable completions and allow insecure loading 5 | skip_global_compinit=1 6 | autoload -U compinit && compinit -u 7 | 8 | if [ -n "$HOMEBREW_PREFIX" ]; then 9 | FPATH="$HOMEBREW_PREFIX/share/zsh/site-functions:$FPATH" 10 | fi 11 | 12 | # Enable regex moving 13 | autoload -U zmv 14 | 15 | # Style ZSH output 16 | zstyle ':completion:*:descriptions' format '%U%B%F{red}%d%f%b%u' 17 | zstyle ':completion:*:warnings' format '%BSorry, no matches for: %d%b' 18 | 19 | # Case insensitive completion 20 | zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 21 | 22 | # Case insensitive globbing 23 | setopt no_case_glob 24 | 25 | # Expand parameters, commands and arithmetic in prompts 26 | setopt prompt_subst 27 | 28 | # Colorful prompt with Git and Subversion branch 29 | autoload -U colors && colors 30 | 31 | git_branch() { 32 | GIT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null) || return 33 | [ -n "$GIT_BRANCH" ] && echo "($GIT_BRANCH) " 34 | } 35 | 36 | if [ "$USER" = "root" ]; then 37 | export PROMPT='%{$fg_bold[magenta]%}%m %{$fg_bold[blue]%}# %b%f' 38 | elif [ -n "${SSH_CONNECTION}" ]; then 39 | export PROMPT='%{$fg_bold[cyan]%}%m %{$fg_bold[blue]%}# %b%f' 40 | else 41 | export PROMPT='%{$fg_bold[green]%}%m %{$fg_bold[blue]%}# %b%f' 42 | fi 43 | export RPROMPT='%{$fg_bold[red]%}$(git_branch)%b[%{$fg_bold[blue]%}%~%b%f]' 44 | 45 | # more macOS/Bash-like word jumps 46 | export WORDCHARS="" 47 | -------------------------------------------------------------------------------- /zshrc.sh: -------------------------------------------------------------------------------- 1 | # always source zprofile regardless of whether this is/isn't a login shell 2 | source ~/.zprofile 3 | 4 | # load shared shell configuration 5 | source ~/.shrc 6 | 7 | # History file 8 | export HISTFILE=~/.zsh_history 9 | 10 | # Don't show duplicate history entires 11 | setopt hist_find_no_dups 12 | 13 | # Remove unnecessary blanks from history 14 | setopt hist_reduce_blanks 15 | 16 | # Share history between instances 17 | setopt share_history 18 | 19 | # Don't hang up background jobs 20 | setopt no_hup 21 | 22 | # autocorrect command spelling 23 | setopt correct 24 | 25 | # use emacs bindings even with vim as EDITOR 26 | bindkey -e 27 | 28 | # fix backspace on Debian 29 | [ -n "$LINUX" ] && bindkey "^?" backward-delete-char 30 | 31 | # fix delete key on macOS 32 | [ -n "$MACOS" ] && bindkey '\e[3~' delete-char 33 | 34 | # alternate mappings for Ctrl-U/V to search the history 35 | bindkey "^u" history-beginning-search-backward 36 | bindkey "^v" history-beginning-search-forward 37 | 38 | # enable autosuggestions 39 | ZSH_AUTOSUGGESTIONS="$HOMEBREW_PREFIX/share/zsh-autosuggestions/zsh-autosuggestions.zsh" 40 | [ -f "$ZSH_AUTOSUGGESTIONS" ] && source "$ZSH_AUTOSUGGESTIONS" 41 | 42 | # More colours with grc 43 | # shellcheck disable=SC1090 44 | GRC_ZSH="$HOMEBREW_PREFIX/etc/grc.zsh" 45 | [ -f "$GRC_ZSH" ] && source "$GRC_ZSH" 46 | 47 | # zsh-specific aliases 48 | alias zmv="noglob zmv -vW" 49 | alias rake="noglob rake" 50 | alias be="noglob bundle exec" 51 | 52 | # to avoid non-zero exit code 53 | true 54 | --------------------------------------------------------------------------------