├── .ci └── Dockerfile.windowsservercore ├── .cirrus.yml ├── .envrc ├── .gitignore ├── .prettierrc.yml ├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── Makefile ├── README.md ├── Vagrantfile ├── assets └── askforpassworddelay.mobileconfig ├── bin ├── log ├── prep ├── prep.ps1 ├── update └── update.ps1 ├── data ├── alpine_base_pkgs.json ├── alpine_headless_pkgs.json ├── arch_base_pkgs.json ├── arch_graphical_aur_pkgs.json ├── arch_graphical_pkgs.json ├── arch_graphical_wayland_pkgs.json ├── arch_graphical_xorg_pkgs.json ├── arch_headless_aur_pkgs.json ├── arch_headless_pkgs.json ├── darwin_base_pkgs.json ├── darwin_graphical_apps.json ├── darwin_graphical_cask_pkgs.json ├── darwin_graphical_pkgs.json ├── darwin_headless_cask_pkgs.json ├── darwin_headless_pkgs.json ├── freebsd_base_pkgs.json ├── freebsd_headless_pkgs.json ├── homebrew_graphical_taps.json ├── homebrew_headless_taps.json ├── homesick_base_repos.json ├── openbsd_base_pkgs.json ├── openbsd_graphical_pkgs.json ├── openbsd_graphical_xorg_pkgs.json ├── openbsd_headless_pkgs.json ├── redhat_base_pkgs.json ├── redhat_headless_pkgs.json ├── rust_cargo_plugins.json ├── ubuntu_base_pkgs.json ├── ubuntu_headless_pkgs.json ├── windows_base_pkgs.json ├── windows_graphical_chocolatey_pkgs.json ├── windows_graphical_pkgs.json ├── windows_headless_chocolatey_pkgs.json └── windows_headless_pkgs.json ├── lib ├── arch.sh ├── common.ps1 ├── common.sh ├── darwin.sh ├── freebsd.sh ├── linux.sh ├── openbsd.sh ├── prep.ps1 ├── prep.sh ├── realpath.sh └── unix.sh ├── support ├── bin │ ├── ci │ └── freebsd-setup.sh ├── distros │ ├── docker │ │ ├── alpine.txt │ │ ├── arch.txt │ │ ├── centos.txt │ │ └── ubuntu.txt │ └── vagrant │ │ ├── freebsd.txt │ │ ├── macos.txt │ │ └── openbsd.txt ├── dockerfiles │ ├── Dockerfile.alpine │ ├── Dockerfile.arch │ ├── Dockerfile.centos │ └── Dockerfile.ubuntu └── lib │ ├── cli.sh │ ├── cli_docker.sh │ ├── cli_docker_build.sh │ ├── cli_docker_clean.sh │ ├── cli_docker_matrix.sh │ ├── cli_docker_run.sh │ ├── cli_vagrant.sh │ ├── cli_vagrant_build.sh │ ├── cli_vagrant_clean.sh │ ├── cli_vagrant_console.sh │ ├── cli_vagrant_matrix.sh │ ├── cmd_docker_build.sh │ ├── cmd_docker_clean.sh │ ├── cmd_docker_matrix.sh │ ├── cmd_docker_run.sh │ ├── cmd_vagrant_build.sh │ ├── cmd_vagrant_clean.sh │ ├── cmd_vagrant_console.sh │ ├── cmd_vagrant_matrix.sh │ ├── docker.sh │ └── vagrant.sh └── vendor ├── lib └── libsh.full.sh └── mk ├── base.mk ├── json.mk ├── libsh-vendor.mk └── shell.mk /.ci/Dockerfile.windowsservercore: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/windows/servercore:ltsc2019 2 | 3 | # Derrived from cirrusci/windowsservercore:2019 image with source at: 4 | # https://github.com/cirruslabs/docker-images-windows/blob/master/windowsservercore/Dockerfile 5 | # 6 | # This is a smaller base, namely without Chocolatey so as to test Chocolatey's 7 | # installation. 8 | 9 | RUN powershell -NoLogo -NoProfile -Command \ 10 | netsh interface ipv4 show interfaces ; \ 11 | netsh interface ipv4 set subinterface 18 mtu=1460 store=persistent ; \ 12 | netsh interface ipv4 show interfaces 13 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | check_task: 2 | container: 3 | image: fnichol/check-shell:latest 4 | setup_script: apk add jq 5 | check_script: make check 6 | 7 | task: 8 | name: profile-${PROFILE}-${DISTRO}-${VERSION} 9 | env: 10 | matrix: 11 | - PROFILE: base 12 | - PROFILE: headless 13 | matrix: 14 | - matrix: 15 | - env: 16 | matrix: 17 | - DISTRO: alpine 18 | VERSION: 3.15 19 | - DISTRO: arch 20 | VERSION: latest 21 | - DISTRO: centos 22 | VERSION: 8 23 | - DISTRO: ubuntu 24 | VERSION: 20.04 25 | container: 26 | dockerfile: support/dockerfiles/Dockerfile.$DISTRO 27 | docker_arguments: 28 | VERSION: $VERSION 29 | run_script: ./bin/prep "--profile=$PROFILE" 30 | - env: 31 | DISTRO: macos 32 | VERSION: 12.1 33 | osx_instance: 34 | image: monterey-base 35 | run_script: ./bin/prep "--profile=$PROFILE" --skip=update-system 36 | - env: 37 | DISTRO: freebsd 38 | VERSION: 13-0 39 | USER: jdoe 40 | # Temporary workaround for error `error: sysinfo not supported on 41 | # this platform` seen on FreeBSD platforms, affecting Rustup 42 | # 43 | # References: https://github.com/rust-lang/rustup/issues/2774 44 | RUSTUP_IO_THREADS: 1 45 | freebsd_instance: 46 | image_family: ${DISTRO}-${VERSION} 47 | prepare_script: ./support/bin/freebsd-setup.sh "--user=$USER" 48 | bash_script: pkg install --yes bash 49 | run_script: | 50 | sudo --preserve-env --non-interactive --set-home "--user=$USER" \ 51 | ./bin/prep "--profile=$PROFILE" 52 | - env: 53 | DISTRO: windows 54 | VERSION: servercore 55 | CIRRUS_SHELL: powershell 56 | windows_container: 57 | dockerfile: .ci/Dockerfile.windowsservercore 58 | os_version: 2019 59 | run_script: .\bin\prep.ps1 -Profile "$env:PROFILE" -NoReboot 60 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | export PATH="$(pwd)/bin:$(pwd)/support/bin:$PATH" 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant/ 2 | log/ 3 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | proseWrap: always 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at fnichol@nichol.ca. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | 78 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include vendor/mk/base.mk 2 | include vendor/mk/json.mk 3 | include vendor/mk/libsh-vendor.mk 4 | include vendor/mk/shell.mk 5 | 6 | SH_SOURCES += ./bin/log ./bin/prep ./bin/update 7 | 8 | build: 9 | .PHONY: build 10 | 11 | test: test-shell ## Runs all tests 12 | .PHONY: test 13 | 14 | check: check-shell check-json ## Checks all linting, styling, & other rules 15 | .PHONY: check 16 | 17 | clean: clean-shell ## Cleans up project 18 | .PHONY: clean 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Workstation Automation 2 | 3 | | | | 4 | | ------: | ----------------------------------------- | 5 | | CI | [![CI Status][badge-ci-overall]][ci] | 6 | | License | [![Crate license][badge-license]][github] | 7 | 8 | **Table of Contents** 9 | 10 | 11 | 12 | - [Supported Platforms](#supported-platforms) 13 | - [Installation](#installation) 14 | - [New System](#new-system) 15 | - [Unix Platforms](#unix-platforms) 16 | - [Windows Platforms](#windows-platforms) 17 | - [Existing System](#existing-system) 18 | - [Usage](#usage) 19 | - [Unix Platforms](#unix-platforms-1) 20 | - [Windows Platforms](#windows-platforms-1) 21 | - [Development and Testing](#development-and-testing) 22 | - [Alpine Linux](#alpine-linux) 23 | - [Arch Linux](#arch-linux) 24 | - [CentOS Linux](#centos-linux) 25 | - [FreeBSD](#freebsd) 26 | - [macOS](#macos) 27 | - [OpenBSD](#openbsd) 28 | - [Ubuntu Linux](#ubuntu-linux) 29 | - [Code of Conduct](#code-of-conduct) 30 | - [Issues](#issues) 31 | - [Contributing](#contributing) 32 | - [Authors](#authors) 33 | - [License](#license) 34 | 35 | 36 | 37 | ## Supported Platforms 38 | 39 | Currently, the following platforms are supported (older and newer version may 40 | also work): 41 | 42 | - Alpine Linux (3.15) 43 | - Arch Linux (rolling latest) 44 | - CentOS (8) 45 | - FreeBSD (13.0) 46 | - macOS (10.15) 47 | - OpenBSD (7.1) 48 | - Ubuntu Linux (20.04) 49 | - Windows 10 (21H2) 50 | 51 | ## Installation 52 | 53 | There are probably 2 use cases: running on a brand new system (before Git is 54 | even installed), and on an existing system (presumably with Git installed). 55 | 56 | ### New System 57 | 58 | #### Unix Platforms 59 | 60 | ```sh 61 | wget https://github.com/fnichol/workstation/archive/master.tar.gz 62 | tar xfz master.tar.gz 63 | cd workstation-master 64 | ``` 65 | 66 | Alternatively, if `wget` is not present you can use `curl`: 67 | 68 | ```sh 69 | curl -LO https://github.com/fnichol/workstation/archive/master.tar.gz 70 | tar xfz master.tar.gz 71 | cd workstation-master 72 | ``` 73 | 74 | #### Windows Platforms 75 | 76 | From a non-administrative PowerShell session: 77 | 78 | ```ps1 79 | irm https://github.com/fnichol/workstation/archive/master.zip -OutFile master.zip 80 | Expand-Archive master.zip 81 | cd master\workstation-master 82 | ``` 83 | 84 | ### Existing System 85 | 86 | On either Windows or Unix platforms: 87 | 88 | ```sh 89 | git clone https://github.com/fnichol/workstation.git 90 | cd workstation 91 | ``` 92 | 93 | ## Usage 94 | 95 | ### Unix Platforms 96 | 97 | To run the workstation prep with the `graphical` profile and set a hostname, 98 | provide your FQDN as the argument: 99 | 100 | ```sh 101 | ./bin/prep 102 | ``` 103 | 104 | If an FQDN isn't provided, then your hostname is left as-is. 105 | 106 | There are currently 3 profiles to select from `base`, `headless`, and 107 | `graphical` with each profile building on the previous one. For example, running 108 | the `headless` profile can be done with: 109 | 110 | ```sh 111 | ./bin/prep --profile=headless 112 | ``` 113 | 114 | A full usage is reported with the `--help` flag: 115 | 116 | ```console 117 | ❯ prep --help 118 | prep 0.5.0 119 | 120 | Workstation Setup 121 | 122 | USAGE: 123 | prep [FLAGS] [OPTIONS] [--] [] 124 | 125 | FLAGS: 126 | -h, --help Prints help information 127 | -V, --version Prints version information 128 | -v, --verbose Prints verbose output 129 | 130 | OPTIONS: 131 | -p, --profile= Setup profile name 132 | [values: base, headless, graphical] 133 | [default: graphical] 134 | -o, --only=[,..] Only run specific tasks 135 | [values: hostname, pkg-init, update-system, 136 | base-pkgs, preferences, keys, bashrc, 137 | base-dot-configs, base-finalize, 138 | headless-pkgs, rust, ruby, go, node, 139 | headless-finalize, graphical-pkgs, 140 | graphical-dot-configs, graphical-finalize, vim, 141 | finish-base, finish-headless, finish-graphical] 142 | -s, --skip=[,..] Skip specific tasks 143 | [values: hostname, pkg-init, update-system, 144 | base-pkgs, preferences, keys, bashrc, 145 | base-dot-configs, base-finalize, 146 | headless-pkgs, rust, ruby, go, node, 147 | headless-finalize, graphical-pkgs, 148 | graphical-dot-configs, graphical-finalize, vim, 149 | finish-base, finish-headless, finish-graphical] 150 | 151 | ARGS: 152 | The name for this workstation 153 | Task name to include or skip 154 | 155 | AUTHOR: 156 | Fletcher Nichol 157 | 158 | ``` 159 | 160 | To update the codebase to the current state of the main branch you can run: 161 | 162 | ```sh 163 | ./bin/update 164 | ``` 165 | 166 | ### Windows Platforms 167 | 168 | On new Windows platforms, you'll need to update your execution policy to allow 169 | the current user to run external PowerShell scripts: 170 | 171 | ```ps1 172 | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force 173 | ``` 174 | 175 | To run the workstation prep with the `graphical` profile and set a hostname, 176 | provide your hostname with the `-Hostname` option: 177 | 178 | ```ps1 179 | .\bin\prep -Hostname 180 | 181 | ``` 182 | 183 | If a hostname isn't provided, then your hostname is left as-is. 184 | 185 | There are currently 3 profiles to select from: `base`, `headless`, and 186 | `graphical` with each profile building on the previous one. For example, running 187 | the `headless` profile can be done with: 188 | 189 | ```ps1 190 | .\bin\prep -Profile headless 191 | 192 | ``` 193 | 194 | A full usage is provided using the `Get-Help` cmdlet with: 195 | 196 | ```console 197 | PS workstation> Get-Help .\bin\prep 198 | 199 | NAME 200 | .\bin\prep.ps1 201 | 202 | SYNOPSIS 203 | Workstation setup 204 | 205 | 206 | SYNTAX 207 | .\bin\prep.ps1 [[-Profile] ] [[-Skip] ] 208 | [[-Only] ] [[-Hostname] ] [-NoReboot] 209 | [] 210 | 211 | 212 | DESCRIPTION 213 | This program sets up a workstation 214 | 215 | 216 | RELATED LINKS 217 | 218 | REMARKS 219 | To see the examples, type: "get-help .\bin\prep.ps1 -examples". 220 | For more information, type: "get-help .\bin\prep.ps1 -detailed". 221 | For technical information, type: "get-help .\bin\prep.ps1 -full". 222 | 223 | ``` 224 | 225 | To update the codebase to the current state of the main branch you can run: 226 | 227 | ```ps1 228 | .\bin\update 229 | ``` 230 | 231 | ## Development and Testing 232 | 233 | ### Alpine Linux 234 | 235 | Build the `headless` profile using Docker by running: 236 | 237 | ```sh 238 | ./support/bin/ci docker build alpine 3.12 headless 239 | ``` 240 | 241 | You can log into the instance with: 242 | 243 | ```sh 244 | ./support/bin/ci docker run -D '--rm -ti' alpine 3.12 headless 245 | ``` 246 | 247 | ### Arch Linux 248 | 249 | Build the `headless` profile using Docker by running: 250 | 251 | ```sh 252 | ./support/bin/ci docker build arch latest headless 253 | ``` 254 | 255 | You can log into the instance with: 256 | 257 | ```sh 258 | ./support/bin/ci docker run -D '--rm -ti' arch latest headless 259 | ``` 260 | 261 | ### CentOS Linux 262 | 263 | Build the `headless` profile using Docker by running: 264 | 265 | ```sh 266 | ./support/bin/ci docker build centos 8 headless 267 | ``` 268 | 269 | You can log into the instance with: 270 | 271 | ```sh 272 | ./support/bin/ci docker run -D '--rm -ti' centos 8 headless 273 | ``` 274 | 275 | ### FreeBSD 276 | 277 | Build the `headless` profile using Vagrant by running: 278 | 279 | ```sh 280 | ./support/bin/ci vagrant build freebsd 12.1 headless 281 | ``` 282 | 283 | You can log into the instance with: 284 | 285 | ```sh 286 | ./support/bin/ci vagrant console freebsd 12.1 headless 287 | ``` 288 | 289 | ### macOS 290 | 291 | The Vagrant box will need to be built via the 292 | [Bento project](https://github.com/chef/bento) and added before this box will 293 | work. 294 | 295 | Build the `headless` profile using Vagrant by running: 296 | 297 | ```sh 298 | ./support/bin/ci vagrant build macos 10.12 headless 299 | ``` 300 | 301 | You can log into the instance with: 302 | 303 | ```sh 304 | ./support/bin/ci vagrant console macos 10.12 headless 305 | ``` 306 | 307 | ### OpenBSD 308 | 309 | Build the `headless` profile using Vagrant by running: 310 | 311 | ```sh 312 | ./support/bin/ci vagrant build openbsd 6.8 headless 313 | ``` 314 | 315 | You can log into the instance with: 316 | 317 | ```sh 318 | ./support/bin/ci vagrant console openbsd 6.8 headless 319 | ``` 320 | 321 | ### Ubuntu Linux 322 | 323 | Build the `headless` profile using Docker by running: 324 | 325 | ```sh 326 | ./support/bin/ci docker build ubuntu 20.04 headless 327 | ``` 328 | 329 | You can log into the instance with: 330 | 331 | ```sh 332 | ./support/bin/ci docker run -D '--rm -ti' ubuntu 20.04 headless 333 | ``` 334 | 335 | ## Code of Conduct 336 | 337 | This project adheres to the Contributor Covenant [code of 338 | conduct][code-of-conduct]. By participating, you are expected to uphold this 339 | code. Please report unacceptable behavior to fnichol@nichol.ca. 340 | 341 | ## Issues 342 | 343 | If you have any problems with or questions about this project, please contact us 344 | through a [GitHub issue][issues]. 345 | 346 | ## Contributing 347 | 348 | You are invited to contribute to new features, fixes, or updates, large or 349 | small; we are always thrilled to receive pull requests, and do our best to 350 | process them as fast as we can. 351 | 352 | Before you start to code, we recommend discussing your plans through a [GitHub 353 | issue][issues], especially for more ambitious contributions. This gives other 354 | contributors a chance to point you in the right direction, give you feedback on 355 | your design, and help you find out if someone else is working on the same thing. 356 | 357 | ## Authors 358 | 359 | Created and maintained by [Fletcher Nichol][fnichol] (). 360 | 361 | ## License 362 | 363 | Licensed under the Mozilla Public License Version 2.0 ([LICENSE.txt][license]). 364 | 365 | Unless you explicitly state otherwise, any contribution intentionally submitted 366 | for inclusion in the work by you, as defined in the MPL-2.0 license, shall be 367 | licensed as above, without any additional terms or conditions. 368 | 369 | [badge-check-format]: 370 | https://img.shields.io/cirrus/github/fnichol/workstation.svg?style=flat-square&task=check&script=format 371 | [badge-check-lint]: 372 | https://img.shields.io/cirrus/github/fnichol/workstation.svg?style=flat-square&task=check&script=lint 373 | [badge-ci-overall]: 374 | https://img.shields.io/cirrus/github/fnichol/workstation.svg?style=flat-square 375 | [badge-license]: https://img.shields.io/badge/License-MPL%202.0%20-blue.svg 376 | [ci]: https://cirrus-ci.com/github/fnichol/workstation 377 | [ci-master]: https://cirrus-ci.com/github/fnichol/workstation/master 378 | [code-of-conduct]: 379 | https://github.com/fnichol/workstation/blob/master/CODE_OF_CONDUCT.md 380 | [fnichol]: https://github.com/fnichol 381 | [github]: https://github.com/fnichol/workstation 382 | [issues]: https://github.com/fnichol/workstation/issues 383 | [license]: https://github.com/fnichol/workstation/blob/master/LICENSE.txt 384 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | # 4 | require 'pathname' 5 | 6 | Vagrant.configure("2") do |config| 7 | basedir = Pathname.new("./support/distros/vagrant") 8 | boxes = Dir.children(basedir) 9 | .sort 10 | .map {|filename| 11 | IO.readlines(basedir.join(filename)) 12 | .map {|line| "#{filename.sub(/\.txt$/, '')}-#{line.chomp}" } 13 | } 14 | .flatten 15 | .map {|kv| kv.split(",") } 16 | 17 | Hash[boxes].each do |name, box| 18 | %w{ 19 | base 20 | headless 21 | graphical 22 | }.each do |profile| 23 | config.vm.define "workstation-#{name}-#{profile}", autostart: false do |c| 24 | c.vm.box = box 25 | 26 | if name.to_s.start_with?("freebsd") 27 | c.vm.synced_folder ".", "/vagrant", type: "rsync" 28 | end 29 | 30 | inline = "cd /vagrant && ./bin/prep --profile=#{profile}" 31 | if name.to_s.start_with?("freebsd") 32 | inline << " && sudo chsh -s /usr/local/bin/bash vagrant" 33 | end 34 | if name.to_s.start_with?("openbsd") 35 | inline << " && chsh -s /usr/local/bin/bash" 36 | end 37 | 38 | c.vm.provision "shell", privileged: false, inline: inline 39 | 40 | c.vm.provider "vmware_desktop" do |p| 41 | p.vmx["numvcpus"] = "3" 42 | end 43 | c.vm.provider "virtualbox" do |p| 44 | p.cpus = 3 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /assets/askforpassworddelay.mobileconfig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayloadContent 6 | 7 | 8 | PayloadDisplayName 9 | Security & Privacy 10 | PayloadEnabled 11 | 12 | PayloadIdentifier 13 | {{domain}}.config.screensaver 14 | PayloadType 15 | com.apple.screensaver 16 | PayloadUUID 17 | 966eb7be-81bd-f8cc-f3e3-078d93f1b4a4 18 | PayloadVersion 19 | 1 20 | askForPassword 21 | 22 | 23 | 24 | PayloadDisplayName 25 | Security & Privacy 26 | PayloadEnabled 27 | 28 | PayloadIdentifier 29 | {{domain}}.config.screensaver 30 | PayloadType 31 | com.apple.screensaver 32 | PayloadUUID 33 | 966eb7be-81bd-f8cc-f3e3-078d93f1b4a5 34 | PayloadVersion 35 | 1 36 | askForPasswordDelay 37 | {{askForPasswordDelay}} 38 | 39 | 40 | PayloadDescription 41 | Enable password immediately after screen saver start 42 | PayloadDisplayName 43 | Screen saver password delay 44 | PayloadIdentifier 45 | {{domain}}.config.screensaver 46 | PayloadOrganization 47 | {{organization}} 48 | PayloadRemovalDisallowed 49 | 50 | PayloadScope 51 | System 52 | PayloadType 53 | Configuration 54 | PayloadUUID 55 | 0dc319a0-c331-0131-eeb5-000c294ab81b 56 | PayloadVersion 57 | 1 58 | 59 | 60 | -------------------------------------------------------------------------------- /bin/log: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | print_usage() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | echo "$program $version 10 | 11 | Logs the output of a program to a file 12 | 13 | USAGE: 14 | $program [FLAGS] [OPTIONS] [--] [ ..] 15 | 16 | FLAGS: 17 | -h, --help Prints help information 18 | -V, --version Prints version information 19 | -v, --verbose Prints verbose output 20 | 21 | OPTIONS: 22 | -p, --prefix= Prefix name for the log file 23 | [default: basename of program name] 24 | 25 | ARGS: 26 | Additional arguments passed to the program 27 | Program to run 28 | 29 | AUTHOR: 30 | $author 31 | " | sed 's/^ \{1,4\}//g' 32 | } 33 | 34 | main() { 35 | set -eu 36 | if [ -n "${DEBUG:-}" ]; then set -v; fi 37 | if [ -n "${TRACE:-}" ]; then set -xv; fi 38 | 39 | local program version author 40 | program="$(basename "$0")" 41 | version="0.1.0" 42 | author="Fletcher Nichol " 43 | 44 | # shellcheck source=lib/realpath.sh 45 | . "${0%/*}/../lib/realpath.sh" 46 | 47 | ROOT="$(realpath "${0%/*}/..")" 48 | 49 | # shellcheck source=vendor/lib/libsh.full.sh 50 | . "$ROOT/vendor/lib/libsh.full.sh" 51 | 52 | cli_invoke "$program" "$version" "$author" "$@" 53 | } 54 | 55 | cli_invoke() { 56 | local program version author prefix 57 | program="$1" 58 | shift 59 | version="$1" 60 | shift 61 | author="$1" 62 | shift 63 | 64 | VERBOSE="" 65 | 66 | OPTIND=1 67 | while getopts "hp:vV-:" arg; do 68 | case "$arg" in 69 | h) 70 | print_usage "$program" "$version" "$author" 71 | exit 0 72 | ;; 73 | p) 74 | prefix="$OPTARG" 75 | ;; 76 | v) 77 | VERBOSE=true 78 | ;; 79 | V) 80 | print_version "$program" "$version" 81 | exit 0 82 | ;; 83 | -) 84 | long_optarg="${OPTARG#*=}" 85 | case "$OPTARG" in 86 | help) 87 | print_usage "$program" "$version" "$author" 88 | exit 0 89 | ;; 90 | prefix=?*) 91 | prefix="$long_optarg" 92 | ;; 93 | prefix*) 94 | print_usage "$program" "$version" "$author" 95 | die "missing required argument for --$OPTARG option" 96 | ;; 97 | verbose) 98 | VERBOSE=true 99 | ;; 100 | version) 101 | print_version "$program" "$version" "true" 102 | exit 0 103 | ;; 104 | '') 105 | # "--" terminates argument processing 106 | break 107 | ;; 108 | *) 109 | print_usage "$program" "$version" "$author" >&2 110 | die "invalid argument --$OPTARG" 111 | ;; 112 | esac 113 | ;; 114 | \?) 115 | print_usage "$program" "$version" "$author" >&2 116 | die "invalid argument; arg=-$OPTARG" 117 | ;; 118 | esac 119 | done 120 | shift "$((OPTIND - 1))" 121 | 122 | if [ -z "${1:-}" ]; then 123 | print_usage "$program" "$version" "$author" >&2 124 | die "missing program argument" 125 | fi 126 | local program="$1" 127 | shift 128 | 129 | if [ -z "${prefix:-}" ]; then 130 | prefix="$(basename "$program")" 131 | fi 132 | 133 | log_program "$prefix" "$program" "$@" 134 | } 135 | 136 | log_program() { 137 | local prefix="$1" 138 | shift 139 | local program="$1" 140 | shift 141 | 142 | local timestamp 143 | timestamp="$(date -u +%FT%TZ)" 144 | 145 | exec 2>&1 146 | 147 | mkdir -p "$ROOT/log" 148 | if { script -h || true; } 2>&1 | grep -q '\-c[, \t]'; then 149 | if [ -n "$VERBOSE" ]; then set -x; fi 150 | exec script -c "$program $*" "$ROOT/log/${prefix}-$timestamp.log" 151 | else 152 | if [ -n "$VERBOSE" ]; then set -x; fi 153 | exec script "$ROOT/log/${prefix}-$timestamp.log" "$program" "$@" 154 | fi 155 | } 156 | 157 | main "$@" 158 | -------------------------------------------------------------------------------- /bin/prep: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | main() { 5 | set -eu 6 | if [ -n "${DEBUG:-}" ]; then set -v; fi 7 | if [ -n "${TRACE:-}" ]; then set -xv; fi 8 | 9 | local program version author root 10 | program="$(basename "$0")" 11 | version="0.5.0" 12 | author="Fletcher Nichol " 13 | 14 | # shellcheck source=lib/realpath.sh 15 | . "${0%/*}/../lib/realpath.sh" 16 | 17 | root="$(realpath "${0%/*}/..")" 18 | 19 | # shellcheck source=vendor/lib/libsh.full.sh 20 | . "$root/vendor/lib/libsh.full.sh" 21 | # shellcheck source=lib/common.sh 22 | . "$root/lib/common.sh" 23 | # shellcheck source=lib/prep.sh 24 | . "$root/lib/prep.sh" 25 | 26 | invoke_cli "$program" "$version" "$author" "$root" "$@" 27 | } 28 | 29 | main "$@" || exit 99 30 | -------------------------------------------------------------------------------- /bin/prep.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Workstation setup 4 | 5 | .DESCRIPTION 6 | This program sets up a workstation 7 | 8 | .PARAMETER Profile 9 | Setup profile 10 | [values: Base, Headless, Graphical] 11 | [default: Graphical] 12 | 13 | .PARAMETER Skip 14 | Tasks to skip 15 | [values: Hostname, PkgInit, UpdateSystem, BasePkgs, Preferences, 16 | SSH, BaseFinalize, HeadlessPkgs, Rust, Ruby, Go, 17 | Node, HeadlessFinalize, GraphicalPkgs, GraphicalFinalize] 18 | 19 | .PARAMETER Only 20 | Single tasks to run 21 | [values: Hostname, PkgInit, UpdateSystem, BasePkgs, Preferences, 22 | SSH, BaseFinalize, HeadlessPkgs, Rust, Ruby, Go, 23 | Node, HeadlessFinalize, GraphicalPkgs, GraphicalFinalize] 24 | 25 | .PARAMETER Hostname 26 | Hostname for system 27 | 28 | .PARAMETER NoReboot 29 | Avoid restarting system, even when it might be necessary 30 | #> 31 | 32 | param ( 33 | [ValidateSet("Base", "Headless", "Graphical")] 34 | [string]$Profile = "Graphical", 35 | 36 | [ValidateSet("Hostname", "PkgInit", "UpdateSystem", "BasePkgs", "Preferences", 37 | "SSH", "BaseFinalize", "HeadlessPkgs", "Rust", "Ruby", "Go", "Node", 38 | "HeadlessFinalize", "GraphicalPkgs", "GraphicalFinalize")] 39 | [AllowEmptyCollection()] 40 | [string[]]$Skip = @(), 41 | 42 | [ValidateSet("Hostname", "PkgInit", "UpdateSystem", "BasePkgs", "Preferences", 43 | "SSH", "BaseFinalize", "HeadlessPkgs", "Rust", "Ruby", "Go", "Node", 44 | "HeadlessFinalize", "GraphicalPkgs", "GraphicalFinalize")] 45 | [AllowEmptyCollection()] 46 | [string[]]$Only = @(), 47 | 48 | [string]$Hostname, 49 | 50 | [switch]$NoReboot 51 | ) 52 | 53 | function Invoke-Main { 54 | $script:program = "prep" 55 | 56 | . "$PSScriptRoot\..\lib\common.ps1" 57 | . "$PSScriptRoot\..\lib\prep.ps1" 58 | 59 | Init 60 | 61 | try { 62 | if (Test-InvokeTask "Hostname") { Set-Hostname } 63 | if (Test-InvokeTask "PkgInit") { Initialize-PackageSystem } 64 | if (Test-InvokeTask "UpdateSystem") { Update-System } 65 | if (Test-InvokeTask "BasePkgs") { Install-BasePackages } 66 | if (Test-InvokeTask "Preferences") { Set-Preferences } 67 | if (Test-InvokeTask "SSH") { Install-SSH } 68 | if (Test-InvokeTask "BaseFinalize") { Invoke-BaseFinalize } 69 | 70 | if (($Profile -eq "Headless") -or ($Profile -eq "Graphical")) { 71 | if (Test-InvokeTask "HeadlessPkgs") { Install-HeadlessPackages } 72 | if (Test-InvokeTask "Rust") { Install-Rust } 73 | if (Test-InvokeTask "Ruby") { Install-Ruby } 74 | if (Test-InvokeTask "Go") { Install-Go } 75 | if (Test-InvokeTask "Node") { Install-Node } 76 | if (Test-InvokeTask "HeadlessFinalize") { Invoke-HeadlessFinalize } 77 | } 78 | 79 | if ($Profile -eq "Graphical") { 80 | if (Test-InvokeTask "GraphicalPkgs") { Install-GraphicalPackages } 81 | if (Test-InvokeTask "GraphicalFinalize") { Invoke-GraphicalFinalize } 82 | } 83 | 84 | Finish 85 | } finally { 86 | Invoke-Cleanup 87 | } 88 | } 89 | 90 | Invoke-Main 91 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | print_usage() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | echo "$program $version 10 | 11 | Updates the working copy of this project 12 | 13 | USAGE: 14 | $program [FLAGS] [OPTIONS] [--] [ ..] 15 | 16 | FLAGS: 17 | -h, --help Prints help information 18 | -V, --version Prints version information 19 | 20 | AUTHOR: 21 | $author 22 | " | sed 's/^ \{1,4\}//g' 23 | } 24 | 25 | main() { 26 | set -eu 27 | if [ -n "${DEBUG:-}" ]; then set -v; fi 28 | if [ -n "${TRACE:-}" ]; then set -xv; fi 29 | 30 | local program version author root 31 | program="$(basename "$0")" 32 | version="0.5.0" 33 | author="Fletcher Nichol " 34 | 35 | # shellcheck source=lib/realpath.sh 36 | . "${0%/*}/../lib/realpath.sh" 37 | 38 | root="$(realpath "${0%/*}/..")" 39 | 40 | # shellcheck source=vendor/lib/libsh.full.sh 41 | . "$root/vendor/lib/libsh.full.sh" 42 | 43 | # Parse CLI arguments and set local variables 44 | parse_cli_args "$program" "$version" "$author" "$@" 45 | 46 | update 47 | } 48 | 49 | parse_cli_args() { 50 | local program version author 51 | program="$1" 52 | shift 53 | version="$1" 54 | shift 55 | author="$1" 56 | shift 57 | 58 | OPTIND=1 59 | while getopts "hV-:" arg; do 60 | case "$arg" in 61 | h) 62 | print_usage "$program" "$version" "$author" 63 | exit 0 64 | ;; 65 | V) 66 | print_version "$program" "$version" 67 | exit 0 68 | ;; 69 | -) 70 | case "$OPTARG" in 71 | help) 72 | print_usage "$program" "$version" "$author" 73 | exit 0 74 | ;; 75 | version) 76 | print_version "$program" "$version" "true" 77 | exit 0 78 | ;; 79 | '') 80 | # "--" terminates argument processing 81 | break 82 | ;; 83 | *) 84 | print_usage "$program" "$version" "$author" >&2 85 | die "invalid argument --$OPTARG" 86 | ;; 87 | esac 88 | ;; 89 | \?) 90 | print_usage "$program" "$version" "$author" >&2 91 | die "invalid argument; arg=-$OPTARG" 92 | ;; 93 | esac 94 | done 95 | shift "$((OPTIND - 1))" 96 | } 97 | 98 | update() { 99 | section "Updating repository checkout" 100 | 101 | need_cmd git 102 | 103 | info "git fetch origin" 104 | git fetch origin 105 | info "git rebase origin/master" 106 | git rebase origin/master 107 | 108 | section "Finished updating" 109 | } 110 | 111 | main "$@" || exit 99 112 | -------------------------------------------------------------------------------- /bin/update.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Updates the working copy of this project 4 | 5 | .DESCRIPTION 6 | This program updates the working copy of this project 7 | #> 8 | 9 | function Invoke-Main { 10 | $script:program = "update" 11 | 12 | . "$PSScriptRoot\..\lib\common.ps1" 13 | 14 | Write-HeaderLine "Updating repository checkout" 15 | 16 | Confirm-Command git 17 | Write-InfoLine "git fetch origin" 18 | git fetch origin 19 | Write-InfoLine "git rebase origin/master" 20 | git rebase origin/master 21 | 22 | Write-HeaderLine "Finished updating" 23 | } 24 | 25 | Invoke-Main 26 | -------------------------------------------------------------------------------- /data/alpine_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bash", 3 | "bash-completion", 4 | "bat", 5 | "curl", 6 | "gawk", 7 | "git", 8 | "grep", 9 | "htop", 10 | "iperf3", 11 | "mg", 12 | "mosh", 13 | "ncurses", 14 | "openssh", 15 | "psmisc", 16 | "pv", 17 | "ripgrep", 18 | "screen", 19 | "tmux", 20 | "tree", 21 | "wget", 22 | "unzip", 23 | "vim" 24 | ] 25 | -------------------------------------------------------------------------------- /data/alpine_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "ack", 3 | "axel", 4 | "b3sum", 5 | "build-base", 6 | "cmake", 7 | "fd", 8 | "flac", 9 | "fzf", 10 | "graphviz", 11 | "lame", 12 | "lynx", 13 | "mercurial", 14 | "neovim", 15 | "parallel", 16 | "rsnapshot", 17 | "rsync", 18 | "syncthing", 19 | "tmate", 20 | "unrar" 21 | ] 22 | -------------------------------------------------------------------------------- /data/arch_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bash", 3 | "bat", 4 | "curl", 5 | "gawk", 6 | "git", 7 | "git-delta", 8 | "gvim", 9 | "htop", 10 | "inetutils", 11 | "iperf3", 12 | "man", 13 | "mg", 14 | "mosh", 15 | "openssh", 16 | "psmisc", 17 | "pv", 18 | "ripgrep", 19 | "screen", 20 | "tmux", 21 | "tree", 22 | "wget", 23 | "unzip" 24 | ] 25 | -------------------------------------------------------------------------------- /data/arch_graphical_aur_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "#": ["Password manager and secure wallet (https://1password.com/)"], 4 | "name": "1password" 5 | }, 6 | { 7 | "#": [ 8 | "A cross-platform Apple Music experience built on Vue.js and written", 9 | "from the ground up with performance in mind.", 10 | "(https://cider.sh/)" 11 | ], 12 | "name": "cider2-bin" 13 | }, 14 | { 15 | "#": [ 16 | "Manage all your JetBrains Projects and Tools", 17 | "(https://www.jetbrains.com/toolbox/)" 18 | ], 19 | "name": "jetbrains-toolbox" 20 | }, 21 | { 22 | "#": ["Slack Desktop (Beta) for Linux (https://slack.com/downloads)"], 23 | "name": "slack-desktop" 24 | }, 25 | { 26 | "#": [ 27 | "An elegant Linux app for the Elgato Stream Deck with support for plugins.", 28 | "(https://github.com/StreamController/StreamController)" 29 | ], 30 | "name": "streamcontroller" 31 | }, 32 | { 33 | "#": [ 34 | "A small program for hiding the mouse cursor", 35 | "unclutter-xfixes is a rewrite of unclutter using the x11-xfixes extension.", 36 | "(https://github.com/Airblader/unclutter-xfixes)" 37 | ], 38 | "name": "unclutter-xfixes-git" 39 | }, 40 | { 41 | "#": [ 42 | "Video Conferencing and Web Conferencing Service", 43 | "(https://zoom.us)" 44 | ], 45 | "name": "zoom" 46 | } 47 | ] 48 | -------------------------------------------------------------------------------- /data/arch_graphical_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "#": [ 4 | "A cross-platform, GPU-accelerated terminal emulator", 5 | "(https://github.com/jwilm/alacritty)" 6 | ], 7 | "name": "alacritty" 8 | }, 9 | "chromium", 10 | { 11 | "#": [ 12 | "Session manager for the COSMIC desktop environment", 13 | "(https://github.com/pop-os/cosmic-session)" 14 | ], 15 | "name": "cosmic-session" 16 | }, 17 | { 18 | "#": [ 19 | "The CUPS Printing System - daemon package", 20 | "(https://openprinting.github.io/cups/)" 21 | ], 22 | "name": "cups" 23 | }, 24 | "darktable", 25 | { 26 | "#": [ 27 | "DesktopEntry Execution, is a program to generate and", 28 | "execute DesktopEntry files of the Application type", 29 | "(https://github.com/jceb/dex)" 30 | ], 31 | "name": "dex" 32 | }, 33 | { 34 | "#": [ 35 | "All-in-one voice and text chat for gamers that's free and secure.", 36 | "(https://discordapp.com/)" 37 | ], 38 | "name": "discord" 39 | }, 40 | { 41 | "#": [ 42 | "Customizable and lightweight notification-daemon", 43 | "(https://dunst-project.org/)" 44 | ], 45 | "name": "dunst" 46 | }, 47 | "firefox", 48 | { 49 | "#": [ 50 | "Free implementation of the Remote Desktop Protocol (RDP).", 51 | "Used as the RDP implementation with Remmina as the main GUI.", 52 | "(https://www.freerdp.com/)" 53 | ], 54 | "name": "freerdp" 55 | }, 56 | { 57 | "#": [ 58 | "Stores passwords and encryption keys", 59 | "(https://wiki.gnome.org/Projects/GnomeKeyring)" 60 | ], 61 | "name": "gnome-keyring" 62 | }, 63 | { 64 | "#": [ 65 | "View current processes and monitor system state", 66 | "(https://wiki.gnome.org/Apps/SystemMonitor)" 67 | ], 68 | "name": "gnome-system-monitor" 69 | }, 70 | "ifplugd", 71 | { 72 | "#": [ 73 | "Cross-platform C libraries that allow you to easily implement VNC", 74 | "server or client functionality.", 75 | "Used as the VNC implementation with Remmina as the main GUI.", 76 | "(https://libvnc.github.io/)" 77 | ], 78 | "name": "libvncserver" 79 | }, 80 | { 81 | "#": [ 82 | "Control brightness on backlit-controllers", 83 | "(https://github.com/haikarainen/light)" 84 | ], 85 | "name": "light" 86 | }, 87 | { 88 | "#": [ 89 | "A lightweight display manager", 90 | "(https://github.com/canonical/lightdm)" 91 | ], 92 | "name": "lightdm" 93 | }, 94 | { 95 | "#": [ 96 | "LightDM greeter that uses WebKit2 for theming via HTML/JavaScript.", 97 | "(https://github.com/antergos/web-greeter)" 98 | ], 99 | "name": "lightdm-webkit2-greeter" 100 | }, 101 | { 102 | "#": [ 103 | "Modern and full-featured LightDM theme", 104 | "(https://github.com/Litarvan/lightdm-webkit-theme-litarvan)" 105 | ], 106 | "name": "lightdm-webkit-theme-litarvan" 107 | }, 108 | "mopidy", 109 | "mpc", 110 | "ncmpcpp", 111 | { 112 | "#": [ 113 | "Network connection manager and user application", 114 | "(https://networkmanager.dev/)" 115 | ], 116 | "name": "networkmanager" 117 | }, 118 | { 119 | "#": [ 120 | "Applet for managing network connections", 121 | "(https://wiki.gnome.org/Projects/NetworkManager/)" 122 | ], 123 | "name": "network-manager-applet" 124 | }, 125 | { 126 | "#": [ 127 | "Google Noto emoji fonts.", 128 | "A font supporting emoji is required for full support with the", 129 | "starship prompt.", 130 | "(https://www.google.com/get/noto/)" 131 | ], 132 | "name": "noto-fonts-emoji" 133 | }, 134 | { 135 | "#": [ 136 | "A powerful knowledge base that works on top of a local folder", 137 | "of plain text Markdown files", 138 | "(https://obsidian.md/)" 139 | ], 140 | "name": "obsidian" 141 | }, 142 | { 143 | "#": [ 144 | "PulseAudio Volume Control", 145 | "(https://freedesktop.org/software/pulseaudio/pavucontrol/)" 146 | ], 147 | "name": "pavucontrol" 148 | }, 149 | "pgadmin4", 150 | { 151 | "#": [ 152 | "X compositor that may fix tearing issues.", 153 | "Used by `fnichol/dotx`'s i3 configuration.", 154 | "(https://github.com/yshui/picom)" 155 | ], 156 | "name": "picom" 157 | }, 158 | "playerctl", 159 | { 160 | "#": [ 161 | "Graphical hardware temperature monitoring application.", 162 | "(https://wpitchoune.net/psensor)" 163 | ], 164 | "name": "psensor" 165 | }, 166 | { 167 | "#": [ 168 | "Session / policy manager implementation for PipeWire", 169 | "", 170 | "NOTE: this package should be installed before the other PipeWire", 171 | " packages so that no explicit user choices get forced", 172 | "(https://pipewire.pages.freedesktop.org/wireplumber/)" 173 | ], 174 | "name": "wireplumber" 175 | }, 176 | { 177 | "#": [ 178 | "Low-latency audio/video router and processor", 179 | "(https://pipewire.org/)" 180 | ], 181 | "name": "pipewire" 182 | }, 183 | { 184 | "#": [ 185 | "Low-latency audio/video router and processor - ALSA configuration" 186 | ], 187 | "name": "pipewire-alsa" 188 | }, 189 | { 190 | "#": [ 191 | "Low-latency audio/video router and processor - JACK support" 192 | ], 193 | "name": "pipewire-jack" 194 | }, 195 | { 196 | "#": [ 197 | "Low-latency audio/video router and processor - PulseAudio replacement" 198 | ], 199 | "name": "pipewire-pulse" 200 | }, 201 | { 202 | "#": [ 203 | "PipeWire Graph Qt GUI Interface", 204 | "(https://gitlab.freedesktop.org/rncbc/qpwgraph)" 205 | ], 206 | "name": "qpwgraph" 207 | }, 208 | { 209 | "#": [ 210 | "Remote desktop client written in GTK+.", 211 | "(https://www.remmina.org/)" 212 | ], 213 | "name": "remmina" 214 | }, 215 | { 216 | "#": [ 217 | "GNOME application for managing keys in GNOME keyring", 218 | "(https://wiki.gnome.org/Apps/Seahorse)" 219 | ], 220 | "name": "seahorse" 221 | }, 222 | { 223 | "#": [ 224 | "a collection of performance monitoring tools", 225 | "(iostat,isag,mpstat,pidstat,sadf,sar)", 226 | "(http://pagesperso-orange.fr/sebastien.godard/)" 227 | ], 228 | "name": "sysstat" 229 | }, 230 | "ttf-inconsolata", 231 | { 232 | "#": [ 233 | "Patched font Inconsolata from the nerd-fonts library", 234 | "(https://github.com/ryanoasis/nerd-fonts)" 235 | ], 236 | "name": "ttf-inconsolata-nerd" 237 | }, 238 | "ttf-jetbrains-mono", 239 | { 240 | "#": [ 241 | "A Nerd Font patched version of JetBrains Mono", 242 | "(https://www.nerdfonts.com)" 243 | ], 244 | "name": "ttf-jetbrains-mono-nerd" 245 | }, 246 | "vlc", 247 | { 248 | "#": [ 249 | "Command line tools that assist applications with a variety of desktop", 250 | "integration tasks, including programs such as", 251 | "`xdg-open` and `xdg-settings`", 252 | "(https://www.freedesktop.org/wiki/Software/xdg-utils/)" 253 | ], 254 | "name": "xdg-utils" 255 | }, 256 | { 257 | "#": [ 258 | "Used to support clipboard copy/paste with the", 259 | "terminal for aliases such as `pbcopy` and `pbpaste`." 260 | ], 261 | "name": "xsel" 262 | }, 263 | { 264 | "#": [ 265 | "Display graphical dialog boxes from shell scripts.", 266 | "Used by `fnichol/dotx`'s i3 configuration.", 267 | "(https://gitlab.gnome.org/GNOME/zenity)" 268 | ], 269 | "name": "zenity" 270 | } 271 | ] 272 | -------------------------------------------------------------------------------- /data/arch_graphical_wayland_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "#": [ 4 | "Dynamic display configuration", 5 | "This is a Wayland equivalent for tools like autorandr", 6 | "(https://github.com/emersion/kanshi)" 7 | ], 8 | "name": "kanshi" 9 | }, 10 | { 11 | "#": "Plugin for QT 5 apps on Wayland", 12 | "name": "qt5-wayland" 13 | }, 14 | { 15 | "#": [ 16 | "Sway is a tiling Wayland compositor and a drop-in replacement for", 17 | "the i3 window manager for X11", 18 | "(https://swaywm.org/)" 19 | ], 20 | "name": "sway" 21 | }, 22 | { 23 | "#": [ 24 | "Idle management daemon for Wayland", 25 | "Idle mangement helps to to sleep, turn off screen, lock, etc.", 26 | "(https://github.com/swaywm/swayidle)" 27 | ], 28 | "name": "swayidle" 29 | }, 30 | { 31 | "#": "Screen locker for Wayland (https://github.com/swaywm/swaylock)", 32 | "name": "swaylock" 33 | }, 34 | { 35 | "#": [ 36 | "Wayland is intended as a simpler replacement for X, easier to develop", 37 | "and maintain", 38 | "(https://wayland.freedesktop.org/)" 39 | ], 40 | "name": "wayland" 41 | }, 42 | { 43 | "#": "Xwayland: runs X clients under Wayland", 44 | "name": "xorg-server-xwayland" 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /data/arch_graphical_xorg_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "#": [ 4 | "Auto-detect connected display hardware and load appropiate X11 setup", 5 | "using xrandr", 6 | "(https://github.com/phillipberndt/autorandr)" 7 | ], 8 | "name": "autorandr" 9 | }, 10 | { 11 | "#": [ 12 | "Background browser and setter for X windows", 13 | "(https://www.archlinux.org/packages/extra/x86_64/nitrogen/)" 14 | ], 15 | "name": "nitrogen" 16 | }, 17 | { 18 | "#": [ 19 | "Generic input driver for the X.Org server based on libinput", 20 | "Used in part to support customizing touchpads on physical laptops" 21 | ], 22 | "name": "xf86-input-libinput" 23 | }, 24 | { 25 | "#": "Create an index of scalable font files for X", 26 | "name": "xorg-mkfontscale" 27 | }, 28 | { 29 | "#": "Xorg X server", 30 | "name": "xorg-server" 31 | }, 32 | { 33 | "#": "X.Org initialisation program (i.e. startx)", 34 | "name": "xorg-xinit" 35 | }, 36 | { 37 | "#": [ 38 | "Small commandline tool to configure devices", 39 | "Used in part to support customizing touchpads on physical laptops" 40 | ], 41 | "name": "xorg-xinput" 42 | }, 43 | { 44 | "#": "User preference utility for X", 45 | "name": "xorg-xset" 46 | } 47 | ] 48 | -------------------------------------------------------------------------------- /data/arch_headless_aur_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "butane", 3 | "direnv", 4 | "hadolint-bin", 5 | "jo", 6 | "kind", 7 | "kubeval-bin", 8 | "powershell-bin", 9 | "zoxide" 10 | ] 11 | -------------------------------------------------------------------------------- /data/arch_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "aws-cli", 3 | "axel", 4 | "b3sum", 5 | "base-devel", 6 | "clang", 7 | "cmake", 8 | "docker", 9 | "docker-buildx", 10 | "docker-compose", 11 | "eksctl", 12 | "fd", 13 | "ffmpeg", 14 | "flac", 15 | "fzf", 16 | "github-cli", 17 | "gnupg", 18 | "graphviz", 19 | "helm", 20 | "kubectl", 21 | "lame", 22 | "libyaml", 23 | "lld", 24 | { 25 | "#": [ 26 | "Fast DNS library supporting recent RFCs.", 27 | "Includes drill which replaces dig.", 28 | "(https://www.nlnetlabs.nl/projects/ldns/)" 29 | ], 30 | "name": "ldns" 31 | }, 32 | "lynx", 33 | "mercurial", 34 | "mold", 35 | "minikube", 36 | "neovim", 37 | "openbsd-netcat", 38 | "packer", 39 | "parallel", 40 | "postgresql-libs", 41 | "protobuf", 42 | "python-pip", 43 | "rsnapshot", 44 | "rsync", 45 | "shellcheck", 46 | "shfmt", 47 | "skopeo", 48 | "sqlite", 49 | "sshuttle", 50 | "starship", 51 | "stylua", 52 | { 53 | "#": [ 54 | "Terminal UI stress test and monitoring tool.", 55 | "(https://github.com/amanusk/s-tui)" 56 | ], 57 | "name": "s-tui" 58 | }, 59 | "syncthing", 60 | "tailscale", 61 | "unrar", 62 | "watchexec", 63 | "websocat", 64 | "youtube-dl" 65 | ] 66 | -------------------------------------------------------------------------------- /data/darwin_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bash", 3 | "bash-completion", 4 | "bat", 5 | "curl", 6 | "gawk", 7 | "git", 8 | "git-delta", 9 | "htop", 10 | "iperf3", 11 | "mg", 12 | "mosh", 13 | "ncurses", 14 | "pstree", 15 | "pv", 16 | "ripgrep", 17 | "tmux", 18 | "tree", 19 | "wget" 20 | ] 21 | -------------------------------------------------------------------------------- /data/darwin_graphical_apps.json: -------------------------------------------------------------------------------- 1 | { 2 | "GIF Keyboard": "1043270657", 3 | "Kindle": "405399194", 4 | "Mactracker": "430255202", 5 | "Microsoft Remote Desktop": "1295203466", 6 | "Monosnap": "540348655", 7 | "Slack": "803453959", 8 | "Speedtest": "1153157709", 9 | "Steam Link": "1246969117", 10 | "Tailscale": "1475387142", 11 | "VOX": "461369673" 12 | } 13 | -------------------------------------------------------------------------------- /data/darwin_graphical_cask_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "1password", 3 | "adobe-dng-converter", 4 | "alacritty", 5 | "astropad", 6 | "disk-inventory-x", 7 | "homebrew/cask-drivers/elgato-stream-deck", 8 | "element", 9 | "firefox", 10 | "font-inconsolata", 11 | "font-inconsolata-nerd-font", 12 | "font-jetbrains-mono", 13 | "font-jetbrains-mono-nerd-font", 14 | "google-chrome", 15 | "homebrew/cask/syncthing", 16 | "keycastr", 17 | "kid3", 18 | "lastfm", 19 | "little-snitch", 20 | "minecraft", 21 | "obsidian", 22 | "plex", 23 | "private-internet-access", 24 | "screenflow", 25 | "send-to-kindle", 26 | "signal", 27 | "sound-control", 28 | "steam", 29 | "tor-browser", 30 | "visual-studio-code", 31 | "vlc", 32 | "zoom" 33 | ] 34 | -------------------------------------------------------------------------------- /data/darwin_graphical_pkgs.json: -------------------------------------------------------------------------------- 1 | ["aerospace"] 2 | -------------------------------------------------------------------------------- /data/darwin_headless_cask_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "dropshare", 3 | "homebrew/cask/docker", 4 | "pgadmin4", 5 | "powershell", 6 | "vagrant", 7 | "vagrant-vmware-utility", 8 | "vmware-fusion" 9 | ] 10 | -------------------------------------------------------------------------------- /data/darwin_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "awscli", 3 | "axel", 4 | "b3sum", 5 | "butane", 6 | "cmake", 7 | "coreutils", 8 | "direnv", 9 | "fd", 10 | "ffmpeg", 11 | "flac", 12 | "fzf", 13 | "gh", 14 | "gnu-tar", 15 | "gnupg", 16 | "graphviz", 17 | "hadolint", 18 | "helm", 19 | "jo", 20 | "kubernetes-cli", 21 | "kubeval", 22 | "lame", 23 | "libxml2", 24 | "lynx", 25 | "make", 26 | "mercurial", 27 | "minikube", 28 | "neovim", 29 | "openjdk", 30 | "packer", 31 | "parallel", 32 | "pkg-config", 33 | "protobuf", 34 | "rsnapshot", 35 | "rsync", 36 | "shellcheck", 37 | "shfmt", 38 | "skopeo", 39 | "sqlite", 40 | "sshuttle", 41 | "starship", 42 | "stylua", 43 | "syncthing", 44 | "taglib", 45 | "tmate", 46 | "ttygif", 47 | "ttyrec", 48 | "watch", 49 | "watchexec", 50 | "yamllint", 51 | "youtube-dl", 52 | "zoxide" 53 | ] 54 | -------------------------------------------------------------------------------- /data/freebsd_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bash", 3 | "bash-completion", 4 | "bat", 5 | "curl", 6 | "git", 7 | "git-delta", 8 | "htop", 9 | "iperf3", 10 | "mg", 11 | "mosh", 12 | "openssh-portable", 13 | "pv", 14 | "ripgrep", 15 | "screen", 16 | "tmux", 17 | "tree", 18 | "wget", 19 | "vim-console" 20 | ] 21 | -------------------------------------------------------------------------------- /data/freebsd_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "autoconf", 3 | "axel", 4 | "b3sum", 5 | "cmake", 6 | "ctags", 7 | "direnv", 8 | "fd-find", 9 | "ffmpeg", 10 | "flac", 11 | "fzf", 12 | "gettext", 13 | "gmake", 14 | "graphviz", 15 | "helm", 16 | "hs-ShellCheck", 17 | "jo", 18 | "libxml2", 19 | "libyaml", 20 | "lynx", 21 | "m4", 22 | "mercurial", 23 | "neovim", 24 | "parallel", 25 | "rsnapshot", 26 | "rsync", 27 | "shfmt", 28 | "sqlite3", 29 | "starship", 30 | "syncthing", 31 | "tailscale", 32 | "tmate", 33 | "unrar", 34 | "zoxide" 35 | ] 36 | -------------------------------------------------------------------------------- /data/homebrew_graphical_taps.json: -------------------------------------------------------------------------------- 1 | [ 2 | "homebrew/cask-fonts", 3 | "nikitabobko/tap" 4 | ] 5 | -------------------------------------------------------------------------------- /data/homebrew_headless_taps.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /data/homesick_base_repos.json: -------------------------------------------------------------------------------- 1 | ["fnichol/dotfiles", "fnichol/dotneovim", "fnichol/dotvim"] 2 | -------------------------------------------------------------------------------- /data/openbsd_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bash", 3 | "curl", 4 | "delta", 5 | "gawk", 6 | "git", 7 | "ggrep", 8 | "htop", 9 | "iperf3", 10 | "mawk", 11 | "mosh", 12 | "pftop", 13 | "pstree", 14 | "pv", 15 | "ripgrep", 16 | "rsnapshot", 17 | "rsync--", 18 | "screen--", 19 | "speedtest-cli", 20 | "tree", 21 | "wget", 22 | "unzip--", 23 | "vim--no_x11", 24 | "wireguard-tools", 25 | "wol" 26 | ] 27 | -------------------------------------------------------------------------------- /data/openbsd_graphical_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "chromium", 3 | "darktable", 4 | "firefox", 5 | { 6 | "#": [ 7 | "GMPC is a frontend for the mpd (Music Player Daemon). It's focused on", 8 | "being fast and easy to use, while still making optimal use of all the", 9 | "functions in mpd.", 10 | "http://gmpclient.org/" 11 | ], 12 | "name": "gmpc" 13 | }, 14 | "ncmpcpp", 15 | { 16 | "#": [ 17 | "Overpass is an open source webfont family inspired by Highway Gothic,", 18 | "sponsored by Red Hat and created by Delve Fonts.", 19 | "https://overpassfont.org/" 20 | ], 21 | "name": "overpass" 22 | }, 23 | "pulseaudio", 24 | { 25 | "#": [ 26 | "Inconsolata is a monospace font, designed for code listings and the", 27 | "like, in print. There are a great many 'programmer fonts' designed", 28 | "primarily for use on the screen, but in most cases do not have the", 29 | "attention to detail for high resolution rendering.", 30 | "https://www.levien.com/type/myfonts/inconsolata.html" 31 | ], 32 | "name": "inconsolata-font" 33 | }, 34 | { 35 | "#": [ 36 | "JetBrains Mono. A typeface for developers", 37 | "1. Increased height for a better reading experience", 38 | "2. Adapted to reading code", 39 | "3. code-specific ligatures", 40 | "4. 145 languages", 41 | "5. weights with matching italics", 42 | "6. JetBrains Mono is free & open source", 43 | "https://www.jetbrains.com/lp/mono/" 44 | ], 45 | "name": "jetbrains-mono" 46 | }, 47 | "vlc" 48 | ] 49 | -------------------------------------------------------------------------------- /data/openbsd_graphical_xorg_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "#": [ 4 | "i3 is a tiling window manager for X11.", 5 | "This is a package group which installs the i3-gaps fork", 6 | "(https://i3wm.org/)" 7 | ], 8 | "name": "i3-gaps" 9 | }, 10 | { 11 | "#": [ 12 | "i3lock is a simple screen locker like slock. After starting it, you", 13 | "will see a white screen (you can configure the color/an image). You", 14 | "can return to your screen by entering your password.", 15 | "(https://openports.se/x11/i3lock)" 16 | ], 17 | "name": "i3lock" 18 | }, 19 | { 20 | "#": [ 21 | "i3status is a small program (about 1500 SLOC) for generating a status", 22 | "bar for dzen2, xmobar or similar programs. It is designed to be very", 23 | "efficient by issuing a very small number of system calls, as one", 24 | "generally wants to update such a status line every second. This ensures", 25 | "that even under high load, your status bar is updated correctly. Also,", 26 | "it saves a bit of energy by not hogging your CPU as much as spawning", 27 | "the corresponding amount of shell commands would.", 28 | "(https://openports.se/x11/i3status)" 29 | ], 30 | "name": "i3status" 31 | }, 32 | { 33 | "#": [ 34 | "Background browser and setter for X windows", 35 | "(https://openports.se/x11/nitrogen)" 36 | ], 37 | "name": "nitrogen" 38 | }, 39 | { 40 | "#": [ 41 | "Compton is a lightweight, standalone composite manager, suitable for use", 42 | "with window managers that do not natively provide compositing", 43 | "functionality.", 44 | "(https://openports.se/x11/compton)" 45 | ], 46 | "name": "compton" 47 | }, 48 | { 49 | "#": [ 50 | "A small program for hiding the mouse cursor", 51 | "(https://openports.se/x11/unclutter)" 52 | ], 53 | "name": "unclutter" 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /data/openbsd_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "awscli", 3 | "autoconf-2.71", 4 | "axel", 5 | "bison", 6 | "bzip2", 7 | "cmake", 8 | "flac", 9 | "fzf", 10 | "gmake", 11 | "gnupg", 12 | "gpatch", 13 | "gtar--", 14 | "jo", 15 | "lame", 16 | "libxml", 17 | "libyaml", 18 | "lynx", 19 | "m4", 20 | "mercurial", 21 | "neovim", 22 | "packer", 23 | "parallel", 24 | "protobuf", 25 | "ripgrep", 26 | "rsnapshot", 27 | "rsync", 28 | "shellcheck", 29 | "sqlite3", 30 | "syncthing", 31 | "tmate", 32 | "unrar", 33 | "youtube-dl" 34 | ] 35 | -------------------------------------------------------------------------------- /data/redhat_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bash", 3 | "curl", 4 | "gawk", 5 | "git", 6 | "iperf3", 7 | "psmisc", 8 | "pv", 9 | "screen", 10 | "tmux", 11 | "tree", 12 | "wget", 13 | "unzip", 14 | "vim" 15 | ] 16 | -------------------------------------------------------------------------------- /data/redhat_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "autoconf", 3 | "bison", 4 | "bzip2", 5 | "cmake", 6 | "flac", 7 | "flex", 8 | "gcc", 9 | "gcc-c++", 10 | "gettext", 11 | "graphviz", 12 | "kernel-devel", 13 | "libxml2-devel", 14 | "libxslt-devel", 15 | "lynx", 16 | "m4", 17 | "make", 18 | "mercurial", 19 | "ncurses-devel", 20 | "openssl-devel", 21 | "patch", 22 | "rsync", 23 | "sqlite" 24 | ] 25 | -------------------------------------------------------------------------------- /data/rust_cargo_plugins.json: -------------------------------------------------------------------------------- 1 | [ 2 | "cargo-edit", 3 | "cargo-nextest", 4 | "cargo-outdated", 5 | "cargo-readme", 6 | "cargo-udeps", 7 | "cargo-update", 8 | "cargo-watch" 9 | ] 10 | -------------------------------------------------------------------------------- /data/ubuntu_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bash", 3 | "bash-completion", 4 | "curl", 5 | "gawk", 6 | "git", 7 | "htop", 8 | "iperf3", 9 | "mg", 10 | "mosh", 11 | "psmisc", 12 | "pv", 13 | "screen", 14 | "tmux", 15 | "tree", 16 | "wget", 17 | "unzip", 18 | "vim" 19 | ] 20 | -------------------------------------------------------------------------------- /data/ubuntu_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "axel", 3 | "build-essential", 4 | "cmake", 5 | "direnv", 6 | "flac", 7 | "graphviz", 8 | "hadolint", 9 | "jo", 10 | "lame", 11 | "libssl-dev", 12 | "libxml2", 13 | "lynx", 14 | "mercurial", 15 | "neovim", 16 | "parallel", 17 | "pkg-config", 18 | "rsnapshot", 19 | "rsync", 20 | "shellcheck", 21 | "sqlite3", 22 | "sshuttle", 23 | "syncthing", 24 | "tmate", 25 | "unrar" 26 | ] 27 | -------------------------------------------------------------------------------- /data/windows_base_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "bat", 3 | "delta", 4 | "git", 5 | "iperf3", 6 | "ripgrep" 7 | ] 8 | -------------------------------------------------------------------------------- /data/windows_graphical_chocolatey_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "1password", 3 | "cinebench", 4 | "divvy", 5 | "epicgameslauncher", 6 | "msiafterburner", 7 | "origin", 8 | "pgadmin4", 9 | "steam" 10 | ] 11 | -------------------------------------------------------------------------------- /data/windows_graphical_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "alacritty", 3 | "coretemp", 4 | "discord", 5 | "firefox", 6 | "googlechrome", 7 | "gpu-z", 8 | "hwinfo", 9 | "Inconsolata-NF", 10 | "JetBrains-Mono", 11 | "JetBrainsMono-NF", 12 | "obsidian", 13 | "plex-desktop", 14 | "slack", 15 | "streamdeck", 16 | "synctrayzor", 17 | "vlc", 18 | "windows-terminal", 19 | "zoom" 20 | ] 21 | -------------------------------------------------------------------------------- /data/windows_headless_chocolatey_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "tailscale", 3 | "vcredist2005", 4 | "vcredist140", 5 | "visualstudio2022buildtools", 6 | "visualstudio2022-workload-vctools" 7 | ] 8 | -------------------------------------------------------------------------------- /data/windows_headless_pkgs.json: -------------------------------------------------------------------------------- 1 | [ 2 | "b3sum", 3 | "cmake", 4 | "direnv", 5 | "fd", 6 | "fzf", 7 | "hadolint", 8 | "jo", 9 | "neovim", 10 | "pstools", 11 | "shellcheck", 12 | "shfmt", 13 | "starship", 14 | "stylua", 15 | "watchexec", 16 | "zoxide" 17 | ] 18 | -------------------------------------------------------------------------------- /lib/arch.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | arch_set_hostname() { 5 | need_cmd grep 6 | need_cmd hostnamectl 7 | need_cmd sed 8 | need_cmd sudo 9 | need_cmd tee 10 | 11 | local name="$1" 12 | local fqdn="$2" 13 | 14 | local old_hostname 15 | old_hostname="$(hostnamectl --static)" 16 | 17 | if [ "$old_hostname" != "$name" ]; then 18 | echo "$name" | sudo tee /etc/hostname >/dev/null 19 | sudo hostnamectl set-hostname "$name" 20 | if ! grep -q -w "$fqdn" /etc/hosts; then 21 | sudo sed -i "1i 127.0.0.1\\t${fqdn}\\t${name}" /etc/hosts 22 | fi 23 | fi 24 | } 25 | 26 | arch_setup_package_system() { 27 | indent sudo pacman -Sy --noconfirm 28 | arch_build_paru 29 | } 30 | 31 | arch_update_system() { 32 | indent sudo pacman -Su --noconfirm 33 | indent paru -Su --noconfirm 34 | 35 | if [ -x /usr/bin/upgrade-kernel ]; then 36 | sudo /usr/bin/upgrade-kernel 37 | fi 38 | } 39 | 40 | arch_install_base_packages() { 41 | local data_path="$1" 42 | 43 | install_pkg jq 44 | install_pkgs_from_json "$data_path/arch_base_pkgs.json" 45 | } 46 | 47 | arch_install_headless_packages() { 48 | local data_path="$1" 49 | 50 | install_pkgs_from_json "$data_path/arch_headless_pkgs.json" 51 | arch_install_aur_pkgs_from_json "$data_path/arch_headless_aur_pkgs.json" 52 | } 53 | 54 | arch_finalize_headless_setup() { 55 | local svc 56 | 57 | # As prescribed by System76's support article for Arch Linux 58 | # 59 | # See: https://support.system76.com/articles/system76-software 60 | if [ "$(cat /sys/class/dmi/id/product_name)" = "Thelio Major" ]; then 61 | need_cmd getent 62 | 63 | arch_install_aur_pkg system76-driver 64 | svc=system76 65 | arch_enable_service "$svc" 66 | arch_start_service "$svc" 67 | 68 | if ! getent group adm | grep -q "$USER"; then 69 | need_cmd gpasswd 70 | 71 | info "Adding '$USER' to adm group" 72 | indent sudo gpasswd -a "$USER" adm 73 | fi 74 | 75 | svc=system76-firmware 76 | arch_install_pkg "$svc" 77 | arch_enable_service "${svc}-daemon" 78 | 79 | arch_install_aur_pkg firmware-manager 80 | arch_install_aur_pkg system76-dkms 81 | arch_install_aur_pkg system76-io-dkms 82 | 83 | svc=com.system76.PowerDaemon.service 84 | arch_install_aur_pkg "system76-power" 85 | arch_enable_service "$svc" 86 | arch_start_service "$svc" 87 | 88 | arch_install_aur_pkg pm-utils 89 | fi 90 | 91 | svc=tailscaled.service 92 | if [ -f "/usr/lib/systemd/system/$svc" ]; then 93 | arch_enable_service "$svc" 94 | arch_start_service "$svc" 95 | fi 96 | 97 | svc="syncthing@$USER.service" 98 | arch_enable_service "$svc" 99 | arch_start_service "$svc" 100 | } 101 | 102 | arch_install_graphical_packages() { 103 | local data_path="$1" 104 | 105 | install_pkgs_from_json "$data_path/arch_graphical_pkgs.json" 106 | arch_install_aur_pkgs_from_json "$data_path/arch_graphical_aur_pkgs.json" 107 | # TODO: determine how to swap between Wayland and Xorg 108 | install_pkgs_from_json "$data_path/arch_graphical_xorg_pkgs.json" 109 | } 110 | 111 | arch_finalize_graphical_setup() { 112 | local svc 113 | 114 | if [ "$(cat /sys/class/dmi/id/product_name)" = "XPS 13 9370" ]; then 115 | need_cmd cut 116 | need_cmd getent 117 | need_cmd grep 118 | 119 | # Battery status 120 | install_pkg acpi 121 | 122 | # Setup power management 123 | install_pkg powertop 124 | if [ ! -f /etc/systemd/system/powertop.service ]; then 125 | info "Setting up Powertop for power management tuning" 126 | cat <<-'EOF' | sudo tee /etc/systemd/system/powertop.service >/dev/null 127 | [Unit] 128 | Description=Powertop tunings 129 | 130 | [Service] 131 | ExecStart=/usr/bin/powertop --auto-tune 132 | RemainAfterExit=true 133 | 134 | [Install] 135 | WantedBy=multi-user.target 136 | EOF 137 | 138 | arch_enable_service powertop 139 | arch_start_service powertop 140 | fi 141 | 142 | if [ ! -f /etc/udev/rules.d/backlight.rules ]; then 143 | info "Setting up udev backlight rule" 144 | cat <<-'EOF' | sudo tee /etc/udev/rules.d/backlight.rules >/dev/null 145 | ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="intel_backlight", RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness" 146 | ACTION=="add", SUBSYSTEM=="backlight", KERNEL=="intel_backlight", RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness" 147 | EOF 148 | fi 149 | 150 | if ! getent group video | cut -d : -f 4 | grep -q "$USER"; then 151 | need_cmd sudo 152 | need_cmd usermod 153 | 154 | info "Adding $USER to the video group" 155 | sudo usermod --append --groups video "$USER" 156 | fi 157 | fi 158 | 159 | if [ "$(cat /sys/class/dmi/id/sys_vendor)" = "VMware, Inc." ]; then 160 | # Required for copy and pasting between the host and guest 161 | install_pkg gtkmm3 162 | install_pkg libxtst 163 | install_pkg xf86-input-vmmouse 164 | # Required for autofit guest screen resolution for both Xorg & Wayland 165 | # See: https://bugs.archlinux.org/task/57473 166 | install_pkg xf86-video-vmware 167 | 168 | arch_enable_service vmware-vmblock-fuse.service 169 | arch_start_service vmware-vmblock-fuse.service 170 | fi 171 | 172 | svc=cosmic-greeter.service 173 | if [ -f "/usr/lib/systemd/system/$svc" ]; then 174 | arch_enable_service "$svc" 175 | fi 176 | 177 | svc=NetworkManager.service 178 | if [ -f "/usr/lib/systemd/system/$svc" ]; then 179 | arch_enable_service "$svc" 180 | arch_start_service "$svc" 181 | fi 182 | 183 | svc=cups.service 184 | if [ -f "/usr/lib/systemd/system/$svc" ]; then 185 | arch_enable_service "$svc" 186 | arch_start_service "$svc" 187 | fi 188 | 189 | local xinitrc_d=/etc/X11/xinit/xinitrc.d/90-gnome-keyring-daemon.sh 190 | if [ ! -f "$xinitrc_d" ]; then 191 | need_cmd chmod 192 | need_cmd tee 193 | 194 | info "Creating '$xinitrc_d'" 195 | cat <<-'EOF' | sudo tee "$xinitrc_d" >/dev/null 196 | #!/bin/sh 197 | 198 | if [ -x /usr/bin/gnome-keyring-daemon ]; then 199 | eval $(/usr/bin/gnome-keyring-daemon --start --components=pkcs11,secrets,ssh) 200 | export SSH_AUTH_SOCK 201 | fi 202 | EOF 203 | sudo chmod 0755 "$xinitrc_d" 204 | fi 205 | 206 | if ! grep -q 'pam_gnome_keyring.so auto_start$' /etc/pam.d/login; then 207 | need_cmd awk 208 | need_cmd install 209 | 210 | local tmp_login 211 | tmp_login="$(mktemp_file)" 212 | cleanup_file "$tmp_login" 213 | 214 | info "Starting gnome-keyring in PAM at console login" 215 | awk ' 216 | /^auth *include *system-local-login$/ { 217 | print 218 | print "auth optional pam_gnome_keyring.so" 219 | next 220 | } 221 | /^password *include *system-local-login$/ { 222 | print 223 | print "session optional pam_gnome_keyring.so auto_start" 224 | next 225 | } 226 | { print } 227 | ' /etc/pam.d/login >"$tmp_login" 228 | sudo install -m 644 -o root -g root "$tmp_login" /etc/pam.d/login 229 | fi 230 | 231 | local xinitrc_d=/etc/X11/xinit/xinitrc.d/95-user-xinitrc.d.sh 232 | if [ ! -f "$xinitrc_d" ]; then 233 | need_cmd chmod 234 | need_cmd tee 235 | 236 | info "Creating '$xinitrc_d'" 237 | cat <<-'EOF' | sudo tee "$xinitrc_d" >/dev/null 238 | #!/bin/sh 239 | 240 | if [ -d $HOME/.xinit/xinitrc.d ] ; then 241 | for f in $HOME/.xinit/xinitrc.d/?*.sh ; do 242 | [ -x "$f" ] && . "$f" 243 | done 244 | unset f 245 | fi 246 | EOF 247 | sudo chmod 0755 "$xinitrc_d" 248 | fi 249 | } 250 | 251 | arch_build_paru() { 252 | need_cmd pacman 253 | 254 | if pacman -Qi paru >/dev/null 2>&1; then 255 | return 0 256 | fi 257 | 258 | sudo pacman -S --noconfirm --needed base-devel git 259 | 260 | local build_dir 261 | build_dir="$(mktemp_directory)" 262 | cleanup_directory "$build_dir" 263 | 264 | info "Installing Paru package" 265 | indent git clone https://aur.archlinux.org/paru-bin.git "$build_dir" 266 | (cd "$build_dir" && makepkg --syncdeps --install --noconfirm --clean) 267 | } 268 | 269 | arch_install_pkg() { 270 | need_cmd pacman 271 | 272 | local pkg="$1" 273 | 274 | if pacman -Qi "$pkg" >/dev/null 2>&1; then 275 | # This is a package and it is installed 276 | return 0 277 | fi 278 | 279 | if pacman -Sg "$pkg" >/dev/null 2>&1; then 280 | # This is a package group, so ensure each package is installed 281 | pacman -Sg "$pkg" \ 282 | | cut -d ' ' -f 2 \ 283 | | while read -r p; do arch_install_pkg "$p" || return 1; done 284 | return 0 285 | fi 286 | 287 | need_cmd sudo 288 | 289 | info "Installing package '$pkg'" 290 | indent sudo pacman -S --noconfirm "$pkg" 291 | } 292 | 293 | arch_install_aur_pkg() { 294 | need_cmd pacman 295 | 296 | local pkg="$1" 297 | 298 | if pacman -Qi "$pkg" >/dev/null 2>&1; then 299 | return 0 300 | fi 301 | 302 | need_cmd sudo 303 | need_cmd paru 304 | 305 | info "Installing AUR package '$pkg'" 306 | indent paru -S --noconfirm "$pkg" 307 | } 308 | 309 | arch_install_aur_pkgs_from_json() { 310 | local json="$1" 311 | 312 | json_items "$json" | while read -r pkg; do 313 | arch_install_aur_pkg "$pkg" 314 | done 315 | } 316 | 317 | arch_enable_service() { 318 | local svc="$1" 319 | 320 | need_cmd sudo 321 | need_cmd systemctl 322 | 323 | if ! systemctl is-enabled "$svc" >/dev/null; then 324 | info "Enabling '$svc' service" 325 | indent sudo systemctl enable "$svc" 326 | fi 327 | } 328 | 329 | arch_start_service() { 330 | local svc="$1" 331 | 332 | need_cmd sudo 333 | need_cmd systemctl 334 | 335 | if ! systemctl is-active "$svc" >/dev/null; then 336 | info "Starting '$svc' service" 337 | indent sudo systemctl start "$svc" 338 | fi 339 | } 340 | -------------------------------------------------------------------------------- /lib/common.ps1: -------------------------------------------------------------------------------- 1 | function Test-Command([Parameter(Mandatory=$True)] [string]$Command) { 2 | Get-Command "$Command" -ErrorAction SilentlyContinue 3 | } 4 | 5 | function Confirm-Command([Parameter(Mandatory=$True)] [string]$Command) { 6 | if (-not (Test-Command "$Command")) { 7 | Write-Failure "Required command '$Command' not found" 8 | } 9 | } 10 | 11 | function Test-AdministrativePrivileges { 12 | $currentPrincipal = New-Object Security.Principal.WindowsPrincipal( 13 | [Security.Principal.WindowsIdentity]::GetCurrent()) 14 | $admin = [Security.Principal.WindowsBuiltInRole]::Administrator 15 | 16 | $currentPrincipal.IsInRole($admin) 17 | } 18 | 19 | function Assert-NonAdministrativePrivileges { 20 | if (Test-AdministrativePrivileges) { 21 | Write-WarnLine "$program must *not* be run in a PowerShell session with " + 22 | "administrator privileges. Please re-run to try again." 23 | Write-Failure "Program run with administrator privileges" 24 | } 25 | } 26 | 27 | function Get-Sudo { 28 | Write-HeaderLine "Starting gsudo credentials cache session" 29 | 30 | Confirm-Command "gsudo" 31 | gsudo cache on 32 | } 33 | 34 | function Test-InvokeTask([string]$Task) { 35 | if ($Only.Length -gt 0) { 36 | (($Only.Contains($Task)) -and (-not ($Skip.Contains($Task)))) 37 | } else { 38 | -not ($Skip.Contains($Task)) 39 | } 40 | } 41 | 42 | function Invoke-Cleanup { 43 | if (Get-Command gsudo -ErrorAction SilentlyContinue) { 44 | Write-HeaderLine "Stopping gsudo credentials cache session" 45 | # Ends the gsudo credentials cache session 46 | gsudo cache off 47 | } 48 | } 49 | 50 | function Write-Failure([Parameter(Mandatory=$True)] [string]$Message) { 51 | Write-Error "$Message" 52 | throw 53 | } 54 | 55 | function Write-HeaderLine($Message) { 56 | Write-Host "--- " -ForegroundColor Cyan -NoNewline 57 | Write-Host "$Message" -ForegroundColor White 58 | } 59 | 60 | function Write-InfoLine($Message) { 61 | Write-Host " - " -ForegroundColor Cyan -NoNewline 62 | Write-Host "$Message" -ForegroundColor White 63 | } 64 | 65 | function Write-WarnLine($Message) { 66 | Write-Warning "$Message" 67 | } 68 | -------------------------------------------------------------------------------- /lib/common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | ensure_not_root() { 5 | local program="$1" 6 | 7 | need_cmd id 8 | 9 | if [ "$(id -u)" -eq 0 ]; then 10 | # shellcheck disable=SC2154 11 | warn "$program must be run as a non-root user, please re-run to try again." 12 | die "Program run with root permissions" 13 | fi 14 | } 15 | 16 | get_sudo() { 17 | local hostname="$1" 18 | 19 | need_cmd uname 20 | 21 | case "$(uname -s)" in 22 | OpenBSD) 23 | need_cmd doas 24 | 25 | doas true 26 | ;; 27 | *) 28 | need_cmd sudo 29 | 30 | sudo -p "[sudo required for some tasks] Password for %u@$hostname: " true 31 | ;; 32 | esac 33 | } 34 | 35 | # Keep-alive: update existing sudo time stamp if set, otherwise do nothing. 36 | # See: https://gist.github.com/cowboy/3118588 37 | keep_sudo() { 38 | local cmd 39 | 40 | need_cmd uname 41 | 42 | case "$(uname -s)" in 43 | OpenBSD) 44 | need_cmd doas 45 | cmd="doas" 46 | ;; 47 | *) 48 | need_cmd sudo 49 | cmd="sudo" 50 | ;; 51 | esac 52 | 53 | while true; do 54 | "$cmd" -n true 55 | sleep 60 56 | kill -0 "$$" || exit 57 | done 2>/dev/null & 58 | } 59 | 60 | sorted_git_tags() { 61 | local repo="$1" 62 | 63 | need_cmd awk 64 | need_cmd git 65 | 66 | # The `--sort` option on `git ls-remote` was introduced in Git 2.18.0, so 67 | # if it's older then we'll have to use GNU/sort's `--version-sort` to help. 68 | # Oi 69 | local version 70 | version="$(git --version | awk '{ print $NF }')" 71 | if version_ge "$version" 2 18; then 72 | git ls-remote --tags --sort=version:refname "$repo" 73 | else 74 | need_cmd sort 75 | 76 | git ls-remote --tags "$repo" \ 77 | | sort --field-separator='/' --key=3 --version-sort 78 | fi 79 | } 80 | 81 | version_ge() { 82 | local version="$1" 83 | local maj="$2" 84 | local min="$3" 85 | 86 | [ "$(echo "$version" | awk -F'.' '{ print $1 }')" -ge "$maj" ] \ 87 | && [ "$(echo "$version" | awk -F'.' '{ print $2 }')" -ge "$min" ] 88 | } 89 | -------------------------------------------------------------------------------- /lib/darwin.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | darwin_check_tmux() { 5 | if [ -n "${TMUX:-}" ]; then 6 | # shellcheck disable=SC2154 7 | warn "Program must not be run under tmux for mas to work correctly." 8 | die "Program run under tmux session" 9 | fi 10 | } 11 | 12 | darwin_set_hostname() { 13 | need_cmd sudo 14 | need_cmd scutil 15 | need_cmd defaults 16 | 17 | local name="$1" 18 | local fqdn="$2" 19 | 20 | local smb="/Library/Preferences/SystemConfiguration/com.apple.smb.server" 21 | if [ "$(scutil --get HostName)" != "$fqdn" ]; then 22 | sudo scutil --set HostName "$fqdn" 23 | fi 24 | if [ "$(scutil --get ComputerName)" != "$name" ]; then 25 | sudo scutil --set ComputerName "$name" 26 | fi 27 | if [ "$(scutil --get LocalHostName)" != "$name" ]; then 28 | sudo scutil --set LocalHostName "$name" 29 | fi 30 | if [ "$(defaults read "$smb" NetBIOSName)" != "$name" ]; then 31 | sudo defaults write "$smb" NetBIOSName -string "$name" 32 | fi 33 | } 34 | 35 | darwin_setup_package_system() { 36 | darwin_install_xcode_cli_tools 37 | darwin_install_rosetta 38 | darwin_install_homebrew 39 | } 40 | 41 | darwin_update_system() { 42 | indent softwareupdate --install --all 43 | indent env HOMEBREW_NO_AUTO_UPDATE=true brew upgrade 44 | indent env HOMEBREW_NO_AUTO_UPDATE=true brew upgrade --cask 45 | } 46 | 47 | darwin_install_base_packages() { 48 | local data_path="$1" 49 | 50 | install_pkg jq 51 | install_pkgs_from_json "$data_path/darwin_base_pkgs.json" 52 | } 53 | 54 | darwin_set_preferences() { 55 | need_cmd defaults 56 | need_cmd profiles 57 | need_cmd sed 58 | 59 | local asset_path="$1" 60 | 61 | info "Enable screen saver hot corner (bottom left)" 62 | defaults write com.apple.dock wvous-bl-corner -int 5 63 | 64 | info "Disable screen saver hot corner (top right)" 65 | defaults write com.apple.dock wvous-tr-corner -int 6 66 | 67 | info "Automatically show and hide the Dock" 68 | defaults write com.apple.dock autohide -bool true 69 | 70 | info "Set icon size of Dock images" 71 | defaults write com.apple.dock tilesize -int 34 72 | 73 | info "Set large icon size of Dock images" 74 | defaults write com.apple.dock largesize -float 44 75 | 76 | info "Enable Dock magnification" 77 | defaults write com.apple.dock magnification -bool true 78 | 79 | info "Disable recent apps in the Dock" 80 | defaults write com.apple.dock show-recents -bool false 81 | 82 | info "Enable password immediately after screen saver starts" 83 | local domain=com.fnichol 84 | local organization=fnichol 85 | local askForPasswordDelay=0 86 | local config 87 | config="$(mktemp_file)" 88 | cleanup_file "$config" 89 | # shellcheck disable=SC2154 90 | sed \ 91 | -e "s,{{domain}},$domain,g" \ 92 | -e "s,{{organization}},$organization,g" \ 93 | -e "s,{{askForPasswordDelay}},$askForPasswordDelay,g" \ 94 | "$asset_path/askforpassworddelay.mobileconfig" \ 95 | >"$config" 96 | profiles -I -F "$config" 97 | 98 | info "Disable press-and-hold for keys in favor of key repeat" 99 | defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false 100 | 101 | info "Set a blazingly fast keyboard repeat rate" 102 | defaults write NSGlobalDomain KeyRepeat -int 1 103 | defaults write NSGlobalDomain InitialKeyRepeat -int 10 104 | 105 | info "Enable full keyboard access for all controls" 106 | defaults write NSGlobalDomain AppleKeyboardUIMode -int 3 107 | 108 | info "Disable annoying UI error sounds." 109 | defaults write com.apple.systemsound com.apple.sound.beep.volume -int 0 110 | defaults write com.apple.sound.beep feedback -int 0 111 | defaults write com.apple.systemsound com.apple.sound.uiaudio.enabled -int 0 112 | 113 | info "Set the menu bar date format" 114 | defaults write com.apple.menuextra.clock DateFormat -string "HH:mm::ss" 115 | defaults write com.apple.menuextra.clock FlashDateSeparators -bool false 116 | defaults write com.apple.menuextra.clock IsAnalog -bool false 117 | defaults write com.apple.menuextra.clock ShowAMPM -bool true 118 | defaults write com.apple.menuextra.clock Show24Hour -bool true 119 | defaults write com.apple.menuextra.clock ShowSeconds -bool true 120 | defaults write com.apple.menuextra.clock ShowDayOfWeek -bool false 121 | 122 | info "Announce time on the hour" 123 | defaults write com.apple.speech.synthesis.general.prefs \ 124 | TimeAnnouncementPrefs -dict \ 125 | TimeAnnouncementsEnabled -bool true \ 126 | TimeAnnouncementsIntervalIdentifier -string "EveryHourInterval" \ 127 | TimeAnnouncementsPhraseIdentifier -string "ShortTime" 128 | 129 | info "Save to disk (not to iCloud) by default" 130 | defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false 131 | 132 | info "Expand Save panel by default" 133 | defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode -bool true 134 | defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode2 -bool true 135 | 136 | info "Expand Print panel by default" 137 | defaults write NSGlobalDomain PMPrintingExpandedStateForPrint -bool true 138 | defaults write NSGlobalDomain PMPrintingExpandedStateForPrint2 -bool true 139 | 140 | info "Save screenshots to the desktop" 141 | defaults write com.apple.screencapture location -string "\$HOME/Desktop" 142 | 143 | info "Save screenshots in PNG format" 144 | defaults write com.apple.screencapture type -string "png" 145 | 146 | info "Disable shadow in screenshots" 147 | defaults write com.apple.screencapture disable-shadow -bool true 148 | 149 | info "Show all filename extensions in Finder" 150 | defaults write NSGlobalDomain AppleShowAllExtensions -bool true 151 | 152 | info "Avoid creating .DS_Store files on network volumes" 153 | defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true 154 | 155 | info "Avoid creating .DS_Store files on USB volumes" 156 | defaults write com.apple.desktopservices DSDontWriteUSBStores -bool true 157 | 158 | info "Check for software updates daily" 159 | defaults write com.apple.SoftwareUpdate ScheduleFrequency -int 1 160 | 161 | info "Show icons for external hard drives on the Desktop" 162 | defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool true 163 | 164 | info "Show icons for servers on the Desktop" 165 | defaults write com.apple.finder ShowMountedServersOnDesktop -bool true 166 | 167 | info "Show icons for removable media on the Desktop" 168 | defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool true 169 | 170 | info "Show Finder status bar" 171 | defaults write com.apple.finder ShowStatusBar -bool true 172 | 173 | info "Disable warning when changing a file extension" 174 | defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false 175 | 176 | info "Use list view in all Finder windows by default" 177 | defaults write com.apple.finder FXPreferredViewStyle -string "Nlsv" 178 | 179 | info "Empty trash securely by default" 180 | defaults write com.apple.finder EmptyTrashSecurely -bool true 181 | 182 | info "Disable crash reporter." 183 | defaults write com.apple.CrashReporter DialogType none 184 | 185 | info "Disable smart dashes" 186 | defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false 187 | 188 | info "Use all function keys as function keys by default" 189 | defaults write -g com.apple.keyboard.fnState -bool true 190 | 191 | info "Remove animation for switching between spaces" 192 | defaults write com.apple.Accessibility ReduceMotionEnabled -int 1 193 | defaults write com.apple.universalaccess reduceMotion -int 1 194 | 195 | info "Disable rearranging of spaces" 196 | defaults write com.apple.dock "mru-spaces" -int 0 197 | } 198 | 199 | darwin_finalize_base_setup() { 200 | darwin_set_bash_shell 201 | darwin_install_terminfo_entries 202 | } 203 | 204 | darwin_finalize_headless_setup() { 205 | need_cmd brew 206 | 207 | local src dst 208 | src="$(brew --prefix)/opt/openjdk/libexec/openjdk.jdk" 209 | dst=/Library/Java/JavaVirtualMachines/openjdk.jdk 210 | 211 | if [ -d "$src" ] && [ ! -L "$dst" ]; then 212 | info "Symlinking OpenJDK to $dst" 213 | need_cmd ln 214 | sudo ln -snf "$src" "$dst" 215 | fi 216 | } 217 | 218 | darwin_finalize_graphical_setup() { 219 | return 0 220 | } 221 | 222 | darwin_install_headless_packages() { 223 | local data_path="$1" 224 | 225 | darwin_add_homebrew_taps_from_json "$data_path/homebrew_headless_taps.json" 226 | install_pkgs_from_json "$data_path/darwin_headless_pkgs.json" 227 | darwin_install_cask_pkgs_from_json "$data_path/darwin_headless_cask_pkgs.json" 228 | darwin_install_beets 229 | } 230 | 231 | darwin_install_graphical_packages() { 232 | local data_path="$1" 233 | 234 | darwin_add_homebrew_taps_from_json "$data_path/homebrew_graphical_taps.json" 235 | install_pkgs_from_json "$data_path/darwin_graphical_pkgs.json" 236 | darwin_install_cask_pkgs_from_json "$data_path/darwin_graphical_cask_pkgs.json" 237 | darwin_install_apps_from_json "$data_path/darwin_graphical_apps.json" 238 | killall Dock 239 | killall Finder 240 | } 241 | 242 | # Implementation graciously borrowed and modified from the build-essential Chef 243 | # cookbook which has been graciously borrowed and modified from Tim Sutton's 244 | # osx-vm-templates project. Newer fallback case (which sadly seems to be the 245 | # new default) is from Homebrew's `install.sh` 246 | # 247 | # Source: https://github.com/chef-cookbooks/build-essential/blob/a4f9621020e930a0e4fa0ccb5b7957dbef8ab347/libraries/xcode_command_line_tools.rb#L182-L188 248 | # Source: https://github.com/timsutton/osx-vm-templates/blob/d029e89e04871b6c7a6c1cd0ec5beb7fa976f345/scripts/xcode-cli-tools.sh 249 | # Source: https://github.com/Homebrew/install/blob/c017ced9ca817138cc03acabb59454a0a0ca889e/install.sh#L870-L878 250 | darwin_install_xcode_cli_tools() { 251 | if [ -e "/Library/Developer/CommandLineTools/usr/bin/git" ]; then 252 | return 0 253 | fi 254 | 255 | need_cmd awk 256 | need_cmd grep 257 | need_cmd head 258 | need_cmd rm 259 | need_cmd sed 260 | need_cmd softwareupdate 261 | need_cmd sudo 262 | need_cmd touch 263 | need_cmd tr 264 | need_cmd /usr/bin/xcode-select 265 | 266 | local product 267 | 268 | info "Installing Xcode Command Line Tools" 269 | 270 | # Create the placeholder file that's checked by the CLI Tools update .dist 271 | # code in Apple's SUS catalog 272 | touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress 273 | # Find the CLI Tools update 274 | product="$(softwareupdate -l \ 275 | | grep "\*.*Command Line" \ 276 | | tail -n 1 \ 277 | | awk -F"*" '{print $2}' \ 278 | | sed -e 's/^ *//' \ 279 | | tr -d '\n')" 280 | if [ -n "$product" ]; then 281 | # Install the update 282 | indent softwareupdate -i "$product" --verbose 283 | sudo /usr/bin/xcode-select --switch /Library/Developer/CommandLineTools 284 | fi 285 | # Remove the placeholder to prevent perpetual appearance in the update 286 | # utility 287 | rm -f /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress 288 | 289 | if [ -z "$product" ]; then 290 | info "Installing Command Line Tools via GUI (sorry!)" 291 | sudo /usr/bin/xcode-select --install 292 | echo 293 | info "Press 'Enter' when the installation is completed" 294 | echo 295 | read -r 296 | sudo /usr/bin/xcode-select --switch /Library/Developer/CommandLineTools 297 | fi 298 | } 299 | 300 | darwin_install_rosetta() { 301 | # shellcheck disable=2154 302 | if [ "$_arch" = "arm64" ]; then 303 | if ! pgrep oahd >/dev/null 2>&1; then 304 | info "Installing Rosetta 2 on Apple silicon" 305 | /usr/sbin/softwareupdate --install-rosetta --agree-to-license 306 | fi 307 | fi 308 | } 309 | 310 | darwin_install_homebrew() { 311 | if ! command -v brew >/dev/null; then 312 | need_cmd bash 313 | 314 | local install_sh 315 | install_sh="$(mktemp_file)" 316 | cleanup_file "$install_sh" 317 | 318 | info "Installing Homebrew" 319 | download \ 320 | https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh \ 321 | "$install_sh" 322 | indent bash "$install_sh" "$cache" 350 | fi 351 | 352 | if grep -E -q "^$pkg\s+" "$cache"; then 353 | # If an installed package was found in the cache, early return 354 | return 0 355 | else 356 | # About to install a package, so invalidate cache to ensure it is 357 | # repopulated on next call 358 | rm -f "$cache" 359 | fi 360 | elif [ "$(brew list --versions "$pkg" | wc -l)" -ne 0 ]; then 361 | # No cache file, but an installed package was found, so early return 362 | return 0 363 | fi 364 | 365 | if [ -n "$extra_args" ]; then 366 | info "Installing package '$pkg' ($extra_args)" 367 | # shellcheck disable=2086 368 | indent env HOMEBREW_NO_AUTO_UPDATE=true brew install \ 369 | "$pkg" $extra_args "$cache" 399 | fi 400 | 401 | if grep -E -q "^$pkg_name\s+" "$cache"; then 402 | # If an installed package was found in the cache, early return 403 | return 0 404 | else 405 | # About to install a package, so invalidate cache to ensure it is 406 | # repopulated on next call 407 | rm -f "$cache" 408 | fi 409 | elif [ "$(brew cask list --versions "$pkg_name" | wc -l)" -ne 0 ]; then 410 | # No cache file, but an installed package was found, so early return 411 | return 0 412 | fi 413 | 414 | if [ -n "$extra_args" ]; then 415 | info "Installing cask package '$pkg' ($extra_args)" 416 | # shellcheck disable=2086 417 | indent env HOMEBREW_NO_AUTO_UPDATE=true brew install \ 418 | "$pkg" $extra_args "$cache" 440 | fi 441 | 442 | if grep -E -q "^${id}$" "$cache"; then 443 | # If an installed package was found in the cache, early return 444 | return 0 445 | else 446 | # About to install a package, so invalidate cache to ensure it is 447 | # repopulated on next call 448 | rm -f "$cache" 449 | fi 450 | elif mas list | cut -d ' ' -f 1 | grep -q "^${id}$"; then 451 | # No cache file, but an installed package was found, so early return 452 | return 0 453 | fi 454 | 455 | info "Installing App '$pkg' ($id)" 456 | indent mas install "$id" 457 | } 458 | 459 | darwin_add_homebrew_tap() { 460 | need_cmd brew 461 | need_cmd grep 462 | 463 | local tap="$1" 464 | 465 | if [ -n "${2:-}" ]; then 466 | # Cache file was provided 467 | local cache="$2" 468 | 469 | if [ ! -f "$cache" ]; then 470 | # If cache file doesn't exist, then populate it 471 | brew tap >"$cache" 472 | fi 473 | 474 | if grep -E -q "^$tap$" "$cache"; then 475 | # If an tap was found in the cache, early return 476 | return 0 477 | else 478 | # About to add a tap, so invalidate cache to ensure it is 479 | # repopulated on next call 480 | rm -f "$cache" 481 | fi 482 | elif brew tap | grep -E -q "^$tap$"; then 483 | # No cache file, but a tap was found, so early return 484 | return 0 485 | fi 486 | 487 | info "Adding homebrew tap '$tap'" 488 | indent env HOMEBREW_NO_AUTO_UPDATE=true brew tap "$tap" 489 | } 490 | 491 | darwin_install_apps_from_json() { 492 | need_cmd awk 493 | need_cmd jq 494 | need_cmd sw_vers 495 | 496 | local ver_maj 497 | local app 498 | local id 499 | local json="$1" 500 | local cache 501 | cache="$(mktemp_file)" 502 | cleanup_file "$cache" 503 | # Ensure no file exists 504 | rm -f "$cache" 505 | 506 | install_pkg mas 507 | 508 | ver_maj="$(sw_vers -productVersion | awk -F. '{ print $1 }')" 509 | 510 | if [ "$ver_maj" -lt 12 ]; then 511 | if ! mas account | grep -q '@'; then 512 | die "Not logged into App Store" 513 | fi 514 | fi 515 | 516 | jq -r '. | to_entries | .[] | @sh "app=\(.key); id=\(.value)"' "$json" \ 517 | | while read -r vars; do 518 | eval "$vars" 519 | darwin_install_app "$app" "$id" "$cache" 520 | done 521 | } 522 | 523 | darwin_install_cask_pkgs_from_json() { 524 | local json="$1" 525 | local cache 526 | cache="$(mktemp_file)" 527 | cleanup_file "$cache" 528 | # Ensure no file exists 529 | rm -f "$cache" 530 | 531 | json_items "$json" | while read -r pkg; do 532 | darwin_install_cask_pkg "$pkg" "$cache" 533 | done 534 | } 535 | 536 | darwin_add_homebrew_taps_from_json() { 537 | local json="$1" 538 | local cache 539 | cache="$(mktemp_file)" 540 | cleanup_file "$cache" 541 | # Ensure no file exists 542 | rm -f "$cache" 543 | 544 | json_items "$json" | while read -r tap; do 545 | darwin_add_homebrew_tap "$tap" "$cache" 546 | done 547 | } 548 | 549 | darwin_install_beets() { 550 | install_pkg python 551 | install_pkg ffmpeg 552 | install_pkg lame 553 | 554 | install_beets_pip_pkgs 555 | } 556 | 557 | darwin_set_bash_shell() { 558 | need_cmd brew 559 | need_cmd chsh 560 | need_cmd cut 561 | need_cmd dscacheutil 562 | need_cmd grep 563 | 564 | local bash_shell 565 | bash_shell="$(brew --prefix)/bin/bash" 566 | 567 | if ! grep -q "^${bash_shell}$" /etc/shells; then 568 | info "Adding '$bash_shell' to /etc/shells" 569 | echo "$bash_shell" | sudo tee -a /etc/shells >/dev/null 570 | fi 571 | 572 | local current_shell 573 | current_shell="$(dscacheutil -q user -a name "$USER" | grep ^shell: \ 574 | | cut -d' ' -f 2)" 575 | 576 | if [ "$current_shell" != "$bash_shell" ]; then 577 | info "Setting '$bash_shell' as default shell for '$USER'" 578 | indent sudo chsh -s "$bash_shell" "$USER" 579 | fi 580 | } 581 | 582 | # Ensure that common and useful terminfo entries are present on system's 583 | # ncurses database which is old and out-of-date. 584 | # 585 | # See: 586 | # https://gpanders.com/blog/the-definitive-guide-to-using-tmux-256color-on-macos/ 587 | darwin_install_terminfo_entries() { 588 | local entries="alacritty alacritty-direct tmux-256color screen-256color" 589 | local entry 590 | 591 | need_cmd brew 592 | need_cmd sudo 593 | 594 | for entry in $entries; do 595 | if ! /usr/bin/infocmp "$entry" >/dev/null 2>&1; then 596 | local info 597 | info="$(mktemp_file)" 598 | cleanup_file "$info" 599 | 600 | info "Adding '$entry' to terminfo" 601 | "$(brew --prefix ncurses)/bin/infocmp" -x "$entry" >"$info" 602 | sudo /usr/bin/tic -x "$info" 603 | fi 604 | done 605 | } 606 | -------------------------------------------------------------------------------- /lib/freebsd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | freebsd_set_hostname() { 5 | local fqdn="$1" 6 | 7 | need_cmd grep 8 | 9 | sudo hostname "$fqdn" 10 | 11 | if grep -q '^hostname=' /etc/rc.conf >/dev/null; then 12 | need_cmd sed 13 | sudo sed -i '' "s/^hostname=.*$/hostname=\"$fqdn\"/" /etc/rc.conf 14 | else 15 | need_cmd tee 16 | echo "hostname=\"$fqdn\"" | sudo tee -a /etc/rc.conf >/dev/null 17 | fi 18 | } 19 | 20 | freebsd_setup_package_system() { 21 | indent sudo pkg update 22 | } 23 | 24 | freebsd_update_system() { 25 | indent sudo pkg upgrade --yes --no-repo-update 26 | } 27 | 28 | freebsd_install_base_packages() { 29 | local data_path="$1" 30 | 31 | install_pkg jq 32 | install_pkgs_from_json "$data_path/freebsd_base_pkgs.json" 33 | } 34 | 35 | freebsd_install_headless_packages() { 36 | local data_path="$1" 37 | 38 | install_pkgs_from_json "$data_path/freebsd_headless_pkgs.json" 39 | freebsd_install_beets 40 | } 41 | 42 | freebsd_install_node() { 43 | install_pkg node 44 | install_pkg npm 45 | } 46 | 47 | freebsd_install_pkg() { 48 | need_cmd pkg 49 | 50 | local pkg="$1" 51 | 52 | if pkg info -e "$pkg" >/dev/null 2>&1; then 53 | return 0 54 | fi 55 | 56 | info "Installing package '$pkg'" 57 | indent sudo pkg install --yes --no-repo-update "$pkg" 58 | } 59 | 60 | freebsd_install_beets() { 61 | need_cmd uname 62 | 63 | local release 64 | release="$(uname -r)" 65 | 66 | case "$release" in 67 | 12.*) 68 | install_pkg py37-pip 69 | install_pkg py37-gdbm 70 | ;; 71 | 11.*) 72 | install_pkg py36-pip 73 | install_pkg py36-gdbm 74 | ;; 75 | *) 76 | warn "Installing beets on $release no yet supported, skipping" 77 | ;; 78 | esac 79 | 80 | install_pkg ffmpeg 81 | 82 | # TODO: support compiling lame pkg 83 | # See: fnichol/workstation#15 84 | # install_pkg lame 85 | 86 | install_beets_pip_pkgs 87 | } 88 | -------------------------------------------------------------------------------- /lib/linux.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | alpine_install_pkg() { 5 | need_cmd apk 6 | need_cmd sudo 7 | 8 | local pkg="$1" 9 | 10 | if apk info | grep -q "^${pkg}$" >/dev/null 2>&1; then 11 | return 0 12 | fi 13 | 14 | info "Installing package '$pkg'" 15 | indent sudo apk add "$pkg" 16 | } 17 | 18 | redhat_install_jq() { 19 | install_pkg wget 20 | 21 | if [ ! -f /usr/local/bin/jq ]; then 22 | local jq_bin 23 | jq_bin="$(mktemp_file)" 24 | cleanup_file "$jq_bin" 25 | 26 | info "Installing jq" 27 | download \ 28 | https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 \ 29 | "$jq_bin" 30 | sudo cp "$jq_bin" /usr/local/bin/jq 31 | sudo chmod 0755 /usr/local/bin/jq 32 | fi 33 | } 34 | 35 | redhat_install_pkg() { 36 | local pkg="$1" 37 | 38 | if sudo yum list installed "$pkg" >/dev/null 2>&1; then 39 | return 0 40 | fi 41 | 42 | info "Installing package '$pkg'" 43 | indent sudo yum install -y "$pkg" 44 | } 45 | 46 | ubuntu_install_pkg() { 47 | local pkg="$1" 48 | local status 49 | 50 | need_cmd apt-get 51 | need_cmd dpkg-query 52 | need_cmd sudo 53 | 54 | # Thanks to https://askubuntu.com/a/668229 for the inspiration 55 | if status="$( 56 | dpkg-query --show --showformat='${db:Status-Status}\n' "$pkg" 2>/dev/null 57 | )"; then 58 | if [ "$status" = "installed" ]; then 59 | return 0 60 | fi 61 | fi 62 | 63 | info "Installing package '$pkg'" 64 | indent sudo env DEBIAN_FRONTEND=noninteractive apt-get install -y "$pkg" 65 | } 66 | -------------------------------------------------------------------------------- /lib/openbsd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | openbsd_set_hostname() { 5 | local fqdn="$1" 6 | 7 | need_cmd doas 8 | need_cmd tee 9 | 10 | doas hostname "$fqdn" 11 | echo "$fqdn" | doas tee /etc/myname >/dev/null 12 | } 13 | 14 | openbsd_update_system() { 15 | need_cmd doas 16 | need_cmd pkg_add 17 | 18 | indent doas pkg_add -u 19 | } 20 | 21 | openbsd_install_base_packages() { 22 | local data_path="$1" 23 | 24 | install_pkg jq 25 | install_pkgs_from_json "$data_path/openbsd_base_pkgs.json" 26 | } 27 | 28 | openbsd_finalize_base_setup() { 29 | openbsd_install_alacritty_terminfo 30 | } 31 | 32 | openbsd_install_alacritty_terminfo() { 33 | need_cmd infocmp 34 | 35 | if ! infocmp alacritty >/dev/null 2>&1; then 36 | local alacrity_info 37 | alacrity_info="$(mktemp_file)" 38 | cleanup_file "$alacrity_info" 39 | 40 | info "Adding alacritty to terminfo" 41 | download \ 42 | https://raw.githubusercontent.com/alacritty/alacritty/master/extra/alacritty.info \ 43 | "$alacrity_info" 44 | indent doas tic -xe alacritty,alacritty-direct "$alacrity_info" 45 | fi 46 | } 47 | 48 | openbsd_install_headless_packages() { 49 | local data_path="$1" 50 | 51 | install_pkgs_from_json "$data_path/openbsd_headless_pkgs.json" 52 | } 53 | 54 | openbsd_install_rust() { 55 | install_pkg rust 56 | install_pkg rust-analyzer 57 | install_pkg rust-clippy 58 | install_pkg rust-gdb 59 | install_pkg rust-rustfmt 60 | install_pkg rust-src 61 | } 62 | 63 | openbsd_install_node() { 64 | install_pkg node 65 | } 66 | 67 | openbsd_install_graphical_packages() { 68 | local data_path="$1" 69 | 70 | openbsd_install_xenocara 71 | install_pkgs_from_json "$data_path/openbsd_graphical_pkgs.json" 72 | install_pkgs_from_json "$data_path/openbsd_graphical_xorg_pkgs.json" 73 | } 74 | 75 | openbsd_install_xenocara() { 76 | if [ -x /usr/X11R6/bin/xterm ]; then 77 | return 0 78 | fi 79 | 80 | need_cmd rm 81 | need_cmd sed 82 | need_cmd tar 83 | need_cmd uname 84 | 85 | local url suffix tgz tmptgz 86 | suffix="$(uname -r | sed 's/\.//g').tgz" 87 | 88 | url="$(cat /etc/installurl)" 89 | url="$url/$(uname -r)" 90 | url="$url/$(uname -m)" 91 | 92 | for tgz in xbase xfont xserv xshare; do 93 | tmptgz="$(mktemp_file)" 94 | cleanup_file "$tmptgz" 95 | download "$url/$tgz$suffix" "$tmptgz" 96 | info "Extracting $tgz$suffix" 97 | doas tar xzphf "$tmptgz" -C / 98 | rm -f "$tmptgz" 99 | done 100 | } 101 | 102 | openbsd_install_pkg() { 103 | local pkg="$1" 104 | 105 | need_cmd pkg_add 106 | need_cmd pkg_info 107 | need_cmd grep 108 | 109 | local pkg_stem pkg_version pkg_flavor 110 | eval "$(openbsd_pkg_parts "$pkg")" 111 | 112 | local pkg_spec 113 | if [ -z "$pkg_version" ]; then 114 | pkg_spec="$pkg_stem->0-$pkg_flavor" 115 | else 116 | pkg_spec="$pkg_stem-$pkg_version-$pkg_flavor" 117 | fi 118 | 119 | local output 120 | if output="$(pkg_info -e "$pkg_spec")"; then 121 | local grep_expr 122 | if [ -n "$pkg_flavor" ]; then 123 | grep_expr="^inst:$pkg_stem-[0-9].*-$pkg_flavor$" 124 | else 125 | grep_expr="^inst:$pkg_stem-[0-9][^-]*$" 126 | fi 127 | 128 | if echo "$output" | grep -q "$grep_expr" >/dev/null; then 129 | return 0 130 | fi 131 | fi 132 | 133 | info "Installing package '$pkg'" 134 | indent doas pkg_add -Iv "$pkg" 135 | } 136 | 137 | openbsd_pkg_parts() { 138 | local name="$1" 139 | 140 | need_cmd perl 141 | 142 | perl -- - "$name" <<-'EOF' 143 | my $name = shift; 144 | 145 | if ($name =~ /^(.*?)-(\d[^-]*)?(?:-([^-]*)?)?$/) { 146 | my $stem = $1; 147 | my $version = $2 || ""; 148 | my $flavor = $3 || ""; 149 | 150 | print "pkg_stem='", $stem, "'\n"; 151 | print "pkg_version='", $version, "'\n"; 152 | print "pkg_flavor='", $flavor, "'\n"; 153 | } else { 154 | print "pkg_stem='", $name, "'\n"; 155 | print "pkg_version=''\n"; 156 | print "pkg_flavor=''\n"; 157 | } 158 | EOF 159 | } 160 | -------------------------------------------------------------------------------- /lib/prep.ps1: -------------------------------------------------------------------------------- 1 | function Init { 2 | if ($Hostname) { 3 | $name = "$Hostname" 4 | } else { 5 | $name = (Get-WmiObject win32_computersystem).DNSHostName + 6 | "." + 7 | (Get-WmiObject win32_computersystem).Domain 8 | } 9 | 10 | $script:dataPath = "$PSScriptRoot\..\data" 11 | 12 | Write-HeaderLine "Setting up workstation '$name'" 13 | 14 | Assert-NonAdministrativePrivileges 15 | Install-Gsudo 16 | Get-Sudo 17 | } 18 | 19 | function Install-Gsudo { 20 | if (-not (Test-Command gsudo)) { 21 | Install-Scoop 22 | Install-Package gsudo 23 | } 24 | } 25 | 26 | function Set-Hostname { 27 | if (-not $Hostname) { 28 | return 29 | } 30 | 31 | $current = (Get-ComputerInfo).CsName 32 | if (-not ("$current" -eq "$Hostname")) { 33 | Write-HeaderLine "Setting hostname to '$Hostname" 34 | $newname = $Hostname.Split('.')[0] 35 | Invoke-gsudo { Rename-Computer -NewName "$using:newname" } 36 | 37 | if (-not ($NoReboot)) { 38 | Write-WarnLine "" 39 | Write-WarnLine ` 40 | "Setting hostname requires restart. Reboot, then re-run $program" 41 | Write-WarnLine "" 42 | Restart-Computer -Force 43 | } 44 | } 45 | } 46 | 47 | function Initialize-PackageSystem { 48 | Write-HeaderLine "Setting up package systems" 49 | 50 | Install-Scoop 51 | Install-Chocolatey 52 | } 53 | 54 | function Install-Scoop { 55 | if (-not (Test-Command scoop)) { 56 | Write-InfoLine "Installing the Scoop command-line installer" 57 | 58 | Set-ExecutionPolicy RemoteSigned -scope CurrentUser -Force 59 | Invoke-RestMethod https://get.scoop.sh | Invoke-Expression 60 | } 61 | 62 | $buckets = @(scoop bucket list | ForEach-Object { $_.Name }) 63 | foreach ($bucket in @("extras", "nerd-fonts", "sysinternals")) { 64 | if (-not ($buckets -contains "$bucket")) { 65 | Install-Package git 66 | Write-InfoLine "Adding Scoop bucket '$bucket" 67 | scoop bucket add "$bucket" 68 | } 69 | } 70 | } 71 | 72 | function Install-Chocolatey { 73 | if (-not (Test-Command choco)) { 74 | Write-InfoLine "Installing the Chocolatey package manager" 75 | 76 | Invoke-gsudo { 77 | $env:chocolateyUseWindowsCompression = 'true' 78 | Set-ExecutionPolicy Bypass -Scope Process -Force 79 | [System.Net.ServicePointManager]::SecurityProtocol = 80 | [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 81 | Invoke-Expression ((New-Object System.Net.WebClient). 82 | DownloadString('https://chocolatey.org/install.ps1')) 83 | } 84 | } 85 | 86 | $binpath = "$env:SystemDrive\ProgramData\chocolatey\bin" 87 | if (-not (("$env:PATH" -split ";") -contains "$binpath")) { 88 | Write-InfoLine "Adding '$binpath' to env:PATH" 89 | $env:PATH += ";$binpath" 90 | } 91 | 92 | $feature = "allowGlobalConfirmation" 93 | $confirmDisabled = choco feature list -r ` 94 | | Select-String -Pattern "^$feature\|Disabled\|" -Quiet 95 | if ($confirmDisabled) { 96 | Write-InfoLine "Enabling Chocolatey feature: '$feature'" 97 | Invoke-gsudo { choco feature enable --name=$using:feature } 98 | } 99 | } 100 | 101 | function Update-System { 102 | Write-HeaderLine "Applying system updates" 103 | 104 | if (-not (Get-Module -ListAvailable -Name PSWindowsUpdate)) { 105 | Write-InfoLine "Installing PSWindowsUpdate commandlet" 106 | Invoke-gsudo { 107 | Install-PackageProvider -Name NuGet -Force 108 | Install-Module PSWindowsUpdate -Force 109 | } 110 | } 111 | 112 | Write-InfoLine "Installing Windows updates" 113 | # TODO fn: stop this from blocking 114 | if ($NoReboot) { 115 | Invoke-gsudo { Get-WUInstall -AcceptAll -Verbose } 116 | } else { 117 | Invoke-gsudo { Get-WUInstall -AcceptAll -Verbose -AutoReboot } 118 | } 119 | 120 | Write-InfoLine "Updating Scoop packages" 121 | Confirm-Command scoop 122 | Install-Package git 123 | scoop update 124 | scoop update --all 125 | 126 | Write-InfoLine "Updating Chocolatey packages" 127 | Confirm-Command choco 128 | Invoke-gsudo { choco upgrade all } 129 | } 130 | 131 | function Install-BasePackages { 132 | Write-HeaderLine "Installing base packages" 133 | Install-PkgsFromJson "$dataPath\windows_base_pkgs.json" 134 | 135 | if (-not (Get-Module -ListAvailable -Name posh-git)) { 136 | Write-InfoLine "Installing posh-git module" 137 | Invoke-gsudo { Install-Module posh-git -Force } 138 | } 139 | } 140 | 141 | function Set-Preferences { 142 | Write-HeaderLine "Setting preferences" 143 | 144 | # TODO fn: implement! 145 | Write-Host "Set-Preferences not implemented yet" 146 | } 147 | 148 | # @TODO(fnichol): finish up! 149 | function Install-SSH { 150 | Write-HeaderLine "Setting up SSH" 151 | 152 | if (-not (Test-Path $env:SystemDrive\Windows\System32\OpenSSH)) { 153 | Write-InfoLine "Installing OpenSSH.Client" 154 | Add-WindowsCapability -Online -Name OpenSSH.Client* 155 | Write-InfoLine "Installing OpenSSH.Client" 156 | Add-WindowsCapability -Online -Name OpenSSH.Server* 157 | 158 | Write-InfoLine "Starting and enabling sshd service" 159 | Start-Service -Name sshd 160 | Set-Service -Name sshd -StartupType 'Automatic' 161 | 162 | Write-InfoLine "Setting default shell to PowerShell for OpenSSH" 163 | New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell ` 164 | -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" ` 165 | -PropertyType String -Force 166 | } 167 | } 168 | 169 | function Invoke-BaseFinalize { 170 | Write-HeaderLine "Finalizing base setup" 171 | } 172 | 173 | function Install-HeadlessPackages { 174 | Write-HeaderLine "Installing headless packages" 175 | Install-ChocolateyPkgsFromJson ` 176 | "$dataPath\windows_headless_chocolatey_pkgs.json" 177 | Install-PkgsFromJson "$dataPath\windows_headless_pkgs.json" 178 | Install-Wsl 179 | } 180 | 181 | function Install-Wsl { 182 | $wslstate = Invoke-gsudo { 183 | (Get-WindowsOptionalFeature -Online ` 184 | -FeatureName Microsoft-Windows-Subsystem-Linux).State 185 | } 186 | 187 | # Install Windows Subsystem for Linux if it is disabled 188 | if ($wslstate.Value -eq "Disabled") { 189 | Write-InfoLine "Enabling 'wsl'" 190 | Invoke-gsudo { 191 | Enable-WindowsOptionalFeature -Online ` 192 | -FeatureName Microsoft-Windows-Subsystem-Linux -NoRestart 193 | } 194 | 195 | if (-not ($NoReboot)) { 196 | Write-WarnLine "" 197 | Write-WarnLine "Enabling WSL requires restart. Reboot, then re-run $program" 198 | Write-WarnLine "" 199 | Restart-Computer -Force 200 | } 201 | } 202 | 203 | Install-Package "archwsl" 204 | } 205 | 206 | function Install-Rust { 207 | $cargo_home = "$env:USERPROFILE\.cargo" 208 | $rustup = "$cargo_home\bin\rustup.exe" 209 | $cargo = "$cargo_home\bin\cargo.exe" 210 | 211 | Write-HeaderLine "Setting up Rust" 212 | 213 | if (-not (Test-Path "$rustup")) { 214 | Write-InfoLine "Installing Rust" 215 | & ([scriptblock]::Create((New-Object System.Net.WebClient).DownloadString( 216 | 'https://gist.github.com/fnichol/699d3c2930649a9932f71bab8a315b31/raw/rustup-init.ps1') 217 | )) -y --default-toolchain stable 218 | } 219 | 220 | & "$rustup" self update 221 | & "$rustup" update 222 | 223 | & "$rustup" component add rust-src 224 | & "$rustup" component add rustfmt 225 | 226 | $plugins = Get-Content "$dataPath\rust_cargo_plugins.json" ` 227 | | ConvertFrom-Json 228 | foreach ($plugin in $plugins) { 229 | if (-not (& "$cargo" install --list | Select-String -Pattern "$plugin")) { 230 | Write-InfoLine "Installing $plugin" 231 | & "$cargo" install --locked "$plugin" 232 | } 233 | } 234 | 235 | Write-InfoLine "Updating Cargo plugins" 236 | & "$cargo" install-update --all 237 | } 238 | 239 | function Install-Ruby { 240 | Write-HeaderLine "Setting up Ruby" 241 | Install-Package "ruby" 242 | } 243 | 244 | function Install-Go { 245 | Write-HeaderLine "Setting up Go" 246 | Install-Package "go" 247 | } 248 | 249 | function Install-Node { 250 | Write-HeaderLine "Setting up Node" 251 | Install-Package "nodejs" 252 | } 253 | 254 | function Invoke-HeadlessFinalize { 255 | Write-HeaderLine "Finalizing headless setup" 256 | } 257 | 258 | function Install-GraphicalPackages { 259 | Write-HeaderLine "Installing graphical packages" 260 | Install-ChocolateyPkgsFromJson ` 261 | "$dataPath\windows_graphical_chocolatey_pkgs.json" 262 | Install-PkgsFromJson "$dataPath\windows_graphical_pkgs.json" 263 | } 264 | 265 | function Invoke-GraphicalFinalize { 266 | Write-HeaderLine "Finalizing graphical setup" 267 | } 268 | 269 | function Finish { 270 | Write-HeaderLine "Finished setting up workstation, enjoy!" 271 | } 272 | 273 | function Install-Package($Pkg) { 274 | Install-WindowsPackage "$Pkg" 275 | } 276 | 277 | function Install-WindowsPackage($Pkg) { 278 | Install-ScoopPackage($Pkg) 279 | } 280 | 281 | function Install-ScoopPackage($Pkg) { 282 | $installed = @( 283 | (scoop export | ConvertFrom-Json).apps | ForEach-Object { $_.Name } 284 | ) 285 | 286 | if ($installed -contains "$Pkg") { 287 | return 288 | } 289 | 290 | Write-InfoLine "Installing Scoop package '$Pkg'" 291 | scoop install "$Pkg" 292 | } 293 | 294 | function Install-ChocolateyPackage($Pkg) { 295 | $installed = @( 296 | @(choco list --limit-output --local-only) | 297 | ForEach-Object { $_.split('|')[0] } 298 | ) 299 | 300 | if ($installed -contains "$Pkg") { 301 | return 302 | } 303 | 304 | Write-InfoLine "Installing Chocolatey package '$Pkg'" 305 | Invoke-gsudo { choco install -y "$using:Pkg" } 306 | } 307 | 308 | function Install-PkgsFromJson($Json) { 309 | $pkgs = Get-Content "$Json" | ConvertFrom-Json 310 | 311 | foreach ($pkg in $pkgs) { 312 | Install-Package "$pkg" 313 | } 314 | } 315 | 316 | function Install-ChocolateyPkgsFromJson($Json) { 317 | $pkgs = Get-Content "$Json" | ConvertFrom-Json 318 | 319 | foreach ($pkg in $pkgs) { 320 | Install-ChocolateyPackage "$pkg" 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /lib/realpath.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # shellcheck disable=SC3043 3 | # 4 | # Implementation drawn from `sh-realpath`: a portable, pure shell 5 | # implementation of realpath. Modified: 6 | # 7 | # * Add support to work with `set -e` 8 | # * Add shellcheck ignore for `local` 9 | # * Apply consistent code formatting via `shfmt -i 2 -ci -bn` 10 | # * Remove removed portable `readlink` implementation. 11 | # 12 | # Original Source: 13 | # 14 | # * Fork: https://github.com/mkropat/sh-realpath/blob/bb72bec5370fa5905cebb47e91937dd25d96d0d7/realpath.sh 15 | # * See: https://github.com/mkropat/sh-realpath 16 | # 17 | # Original license: 18 | # 19 | # The MIT License (MIT) 20 | # 21 | # Copyright (c) 2014 Michael Kropat 22 | 23 | # Permission is hereby granted, free of charge, to any person obtaining a copy 24 | # of this software and associated documentation files (the "Software"), to deal 25 | # in the Software without restriction, including without limitation the rights 26 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 27 | # copies of the Software, and to permit persons to whom the Software is 28 | # furnished to do so, subject to the following conditions: 29 | 30 | # The above copyright notice and this permission notice shall be included in 31 | # all copies or substantial portions of the Software. 32 | 33 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 38 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 39 | # THE SOFTWARE. 40 | 41 | realpath() { 42 | canonicalize_path "$(resolve_symlinks "$1")" 43 | } 44 | 45 | resolve_symlinks() { 46 | _resolve_symlinks "$1" 47 | } 48 | 49 | _resolve_symlinks() { 50 | _assert_no_path_cycles "$@" || return 51 | 52 | local dir_context path 53 | if path=$(readlink -- "$1"); then 54 | dir_context=$(dirname -- "$1") 55 | _resolve_symlinks \ 56 | "$(_prepend_dir_context_if_necessary "$dir_context" "$path")" "$@" 57 | else 58 | printf '%s\n' "$1" 59 | fi 60 | } 61 | 62 | _prepend_dir_context_if_necessary() { 63 | if [ "$1" = . ]; then 64 | printf '%s\n' "$2" 65 | else 66 | _prepend_path_if_relative "$1" "$2" 67 | fi 68 | } 69 | 70 | _prepend_path_if_relative() { 71 | case "$2" in 72 | /*) printf '%s\n' "$2" ;; 73 | *) printf '%s\n' "$1/$2" ;; 74 | esac 75 | } 76 | 77 | _assert_no_path_cycles() { 78 | local target path 79 | 80 | target=$1 81 | shift 82 | 83 | for path in "$@"; do 84 | if [ "$path" = "$target" ]; then 85 | return 1 86 | fi 87 | done 88 | } 89 | 90 | canonicalize_path() { 91 | if [ -d "$1" ]; then 92 | _canonicalize_dir_path "$1" 93 | else 94 | _canonicalize_file_path "$1" 95 | fi 96 | } 97 | 98 | _canonicalize_dir_path() { 99 | (cd "$1" 2>/dev/null && pwd -P) 100 | } 101 | 102 | _canonicalize_file_path() { 103 | local dir file 104 | dir=$(dirname -- "$1") 105 | file=$(basename -- "$1") 106 | (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file") 107 | } 108 | -------------------------------------------------------------------------------- /lib/unix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | unix_install_chruby() { 5 | local ver 6 | ver="$(unix_latest_chruby_version)" 7 | 8 | if check_cmd chruby-exec; then 9 | local installed_ver 10 | installed_ver="$(chruby-exec --version | awk '{print $NF}')" 11 | if [ "$installed_ver" = "$ver" ]; then 12 | info "Current chruby version '$ver' is installed" 13 | return 0 14 | fi 15 | fi 16 | 17 | need_cmd make 18 | need_cmd rm 19 | need_cmd tar 20 | need_cmd uname 21 | 22 | local sudo 23 | case "$(uname -s)" in 24 | OpenBSD) 25 | need_cmd doas 26 | sudo="doas" 27 | ;; 28 | *) 29 | need_cmd sudo 30 | sudo="sudo" 31 | ;; 32 | esac 33 | 34 | local tar 35 | tar="$(mktemp_file)" 36 | cleanup_file "$tar" 37 | 38 | info "Installing chruby $ver" 39 | download \ 40 | "https://github.com/postmodern/chruby/archive/v${ver}.tar.gz" \ 41 | "$tar" 42 | tar -xzf "$tar" -C /tmp 43 | (cd "/tmp/chruby-$ver" && indent "$sudo" make install) 44 | rm -rf "/tmp/chruby-$ver" 45 | } 46 | 47 | unix_install_ruby_install() { 48 | local ver 49 | ver="$(unix_latest_ruby_install_version)" 50 | 51 | if check_cmd ruby-install; then 52 | local installed_ver 53 | installed_ver="$(ruby-install --version | awk '{print $NF}')" 54 | if [ "$installed_ver" = "$ver" ]; then 55 | info "Current ruby-install version '$ver' is installed" 56 | return 0 57 | fi 58 | fi 59 | 60 | need_cmd make 61 | need_cmd rm 62 | need_cmd tar 63 | need_cmd uname 64 | 65 | local sudo 66 | case "$(uname -s)" in 67 | OpenBSD) 68 | need_cmd doas 69 | sudo="doas" 70 | ;; 71 | *) 72 | need_cmd sudo 73 | sudo="sudo" 74 | ;; 75 | esac 76 | 77 | local tar 78 | tar="$(mktemp_file)" 79 | cleanup_file "$tar" 80 | 81 | info "Installing ruby-install $ver" 82 | download \ 83 | "https://github.com/postmodern/ruby-install/archive/v${ver}.tar.gz" \ 84 | "$tar" 85 | tar -xzf "$tar" -C /tmp 86 | (cd "/tmp/ruby-install-$ver" && indent "$sudo" make install) 87 | rm -rf "/tmp/ruby-install-$ver" 88 | } 89 | 90 | unix_install_volta() { 91 | local volta="$1" 92 | 93 | local ver 94 | ver="$(unix_latest_volta_version)" 95 | 96 | if [ -x "$volta" ]; then 97 | local installed_ver 98 | installed_ver="$("$volta" --version)" 99 | if [ "$installed_ver" = "$ver" ]; then 100 | info "Current Volta version '$ver' is installed" 101 | return 0 102 | fi 103 | fi 104 | 105 | need_cmd bash 106 | 107 | local install_sh 108 | install_sh="$(mktemp_file)" 109 | cleanup_file "$install_sh" 110 | 111 | info "Installing Volta '$ver'" 112 | download https://get.volta.sh "$install_sh" 113 | indent bash "$install_sh" --skip-setup 114 | } 115 | 116 | unix_latest_chruby_version() { 117 | unix_latest_git_tag "https://github.com/postmodern/chruby" 118 | } 119 | 120 | unix_latest_ruby_install_version() { 121 | unix_latest_git_tag "https://github.com/postmodern/ruby-install" 122 | } 123 | 124 | unix_latest_volta_version() { 125 | unix_latest_github_release "volta-cli/volta" 126 | } 127 | 128 | unix_latest_git_tag() { 129 | local repo="$1" 130 | 131 | need_cmd awk 132 | 133 | sorted_git_tags "$repo" | awk -F/ ' 134 | ($NF ~ /^v[0-9]+\./ && $NF !~ /\^\{\}$/) { last = $NF } 135 | END { sub(/^v/, "", last); print last }' 136 | } 137 | 138 | unix_latest_github_release() { 139 | local repo="$1" 140 | local latest 141 | latest="$(mktemp_file)" 142 | cleanup_file "$latest" 143 | 144 | need_cmd jq 145 | need_cmd sed 146 | 147 | download "https://api.github.com/repos/$repo/releases/latest" \ 148 | "$latest" >/dev/null 149 | jq -r .tag_name <"$latest" | sed 's/^v//' 150 | } 151 | -------------------------------------------------------------------------------- /support/bin/ci: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | main() { 5 | set -eu 6 | if [ -n "${DEBUG:-}" ]; then set -v; fi 7 | if [ -n "${TRACE:-}" ]; then set -xv; fi 8 | 9 | local program version author 10 | program="$(basename "$0")" 11 | version="0.1.0" 12 | author="Fletcher Nichol " 13 | 14 | # shellcheck source=lib/realpath.sh 15 | . "${0%/*}/../../lib/realpath.sh" 16 | 17 | ROOT="$(realpath "${0%/*}/..")" 18 | 19 | # shellcheck source=support/lib/cli.sh 20 | . "$ROOT/lib/cli.sh" 21 | # shellcheck source=vendor/lib/libsh.full.sh 22 | . "$ROOT/../vendor/lib/libsh.full.sh" 23 | 24 | cli__invoke_main "$program" "$version" "$author" "$@" 25 | } 26 | 27 | main "$@" 28 | -------------------------------------------------------------------------------- /support/bin/freebsd-setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | print_usage() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | echo "$program $version 10 | 11 | Sets up the initial FreeBSD environment with a non-root user 12 | 13 | USAGE: 14 | $program [FLAGS] [OPTIONS] [--] 15 | 16 | FLAGS: 17 | -h, --help Prints help information 18 | -V, --version Prints version information 19 | -v, --verbose Prints verbose output 20 | 21 | OPTIONS: 22 | -u, --user= Non-root user to be created and used 23 | [default: jdoe] 24 | -g, --group= Non-root group to be created and used 25 | [default: jdoe] 26 | 27 | AUTHOR: 28 | $author 29 | " | sed 's/^ \{1,4\}//g' 30 | } 31 | 32 | main() { 33 | set -eu 34 | if [ -n "${DEBUG:-}" ]; then set -v; fi 35 | if [ -n "${TRACE:-}" ]; then set -xv; fi 36 | 37 | local program version author 38 | program="$(basename "$0")" 39 | version="0.1.0" 40 | author="Fletcher Nichol " 41 | 42 | cli_invoke "$program" "$version" "$author" "$@" 43 | } 44 | 45 | cli_invoke() { 46 | local program version author user group 47 | program="$1" 48 | shift 49 | version="$1" 50 | shift 51 | author="$1" 52 | shift 53 | 54 | VERBOSE="" 55 | user="jdoe" 56 | group="$user" 57 | 58 | OPTIND=1 59 | while getopts "hg:u:vV-:" arg; do 60 | case "$arg" in 61 | h) 62 | print_usage "$program" "$version" "$author" 63 | return 0 64 | ;; 65 | g) 66 | group="$OPTARG" 67 | ;; 68 | u) 69 | user="$OPTARG" 70 | ;; 71 | v) 72 | VERBOSE=true 73 | ;; 74 | V) 75 | print_version "$program" "$version" "${VERBOSE:-}" 76 | return 0 77 | ;; 78 | -) 79 | long_optarg="${OPTARG#*=}" 80 | case "$OPTARG" in 81 | help) 82 | print_usage "$program" "$version" "$author" 83 | return 0 84 | ;; 85 | group=?*) 86 | group="$long_optarg" 87 | ;; 88 | group*) 89 | print_usage "$program" "$version" "$author" 90 | fail "missing required argument for --$OPTARG option" 91 | ;; 92 | user=?*) 93 | user="$long_optarg" 94 | ;; 95 | user*) 96 | print_usage "$program" "$version" "$author" 97 | fail "missing required argument for --$OPTARG option" 98 | ;; 99 | verbose) 100 | VERBOSE=true 101 | ;; 102 | version) 103 | print_version "$program" "$version" "${VERBOSE:-}" 104 | return 0 105 | ;; 106 | '') 107 | # "--" terminates argument processing 108 | break 109 | ;; 110 | *) 111 | print_usage "$program" "$version" "$author" >&2 112 | fail "invalid argument --$OPTARG" 113 | ;; 114 | esac 115 | ;; 116 | \?) 117 | print_usage "$program" "$version" "$author" >&2 118 | fail "invalid argument; arg=-$OPTARG" 119 | ;; 120 | esac 121 | done 122 | shift "$((OPTIND - 1))" 123 | 124 | setup "$user" "$group" 125 | } 126 | 127 | setup() { 128 | local user="$1" 129 | shift 130 | local group="$1" 131 | shift 132 | 133 | pkg install --yes sudo 134 | 135 | pw groupadd -n "$group" 136 | pw useradd -n "$user" -c "$user" -g "$group" -m -s "/bin/sh" 137 | pw usermod -n "$user" -G wheel 138 | 139 | sed -i.bak 's/^# \(%wheel .*NOPASSWD.*\)$/\1/' /usr/local/etc/sudoers 140 | rm -f /usr/local/etc/sudoers.bak 141 | } 142 | 143 | # Inspired by Cargo's version implementation, see: https://git.io/fjsOh 144 | print_version() { 145 | local program="$1" 146 | local version="$2" 147 | local verbose="$3" 148 | 149 | if command -v git >/dev/null; then 150 | local date sha 151 | date="$(git show -s --format=%cd --date=short)" 152 | sha="$(git show -s --format=%h)" 153 | if ! git diff-index --quiet HEAD --; then 154 | sha="${sha}-dirty" 155 | fi 156 | 157 | echo "$program $version ($sha $date)" 158 | 159 | if [ -n "$verbose" ]; then 160 | local long_sha 161 | long_sha="$(git show -s --format=%H)" 162 | case "$sha" in 163 | *-dirty) long_sha="${long_sha}-dirty" ;; 164 | esac 165 | 166 | echo "release: $version" 167 | echo "commit-hash: $long_sha" 168 | echo "commit-date: $date" 169 | fi 170 | else 171 | echo "$program $version" 172 | 173 | if [ -n "$verbose" ]; then 174 | echo "release: $version" 175 | fi 176 | fi 177 | } 178 | 179 | fail() { 180 | echo "" >&2 181 | echo "xxx $1" >&2 182 | echo "" >&2 183 | return 1 184 | } 185 | 186 | main "$@" 187 | -------------------------------------------------------------------------------- /support/distros/docker/alpine.txt: -------------------------------------------------------------------------------- 1 | 3.15 2 | 3.14 3 | 3.13 4 | 3.12 5 | 3.11 6 | 3.10 7 | 3.9 8 | 3.8 9 | 3.7 10 | 3.6 11 | edge 12 | -------------------------------------------------------------------------------- /support/distros/docker/arch.txt: -------------------------------------------------------------------------------- 1 | latest 2 | -------------------------------------------------------------------------------- /support/distros/docker/centos.txt: -------------------------------------------------------------------------------- 1 | 8.2 2 | 8.1.1911 3 | 7.6.1810 4 | 7.5.1804 5 | 7.4.1708 6 | 7.0.1406 7 | 6.10 8 | -------------------------------------------------------------------------------- /support/distros/docker/ubuntu.txt: -------------------------------------------------------------------------------- 1 | 20.10 2 | 20.04 3 | 18.04 4 | 16.04 5 | 14.04 6 | rolling 7 | -------------------------------------------------------------------------------- /support/distros/vagrant/freebsd.txt: -------------------------------------------------------------------------------- 1 | 13.0,bento/freebsd-13.0 2 | 12.1,bento/freebsd-12.1 3 | 11.4,bento/freebsd-11.4 4 | -------------------------------------------------------------------------------- /support/distros/vagrant/macos.txt: -------------------------------------------------------------------------------- 1 | 10.12,bento/macos-10.12 2 | -------------------------------------------------------------------------------- /support/distros/vagrant/openbsd.txt: -------------------------------------------------------------------------------- 1 | 7.0,fnichol/openbsd-7.0 2 | 6.9,fnichol/openbsd-6.9 3 | 6.8,fnichol/openbsd-6.8 4 | 6.7,fnichol/openbsd-6.7 5 | -------------------------------------------------------------------------------- /support/dockerfiles/Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | ARG VERSION=3.12 2 | FROM alpine:$VERSION 3 | 4 | ARG VERSION 5 | ARG USER=jdoe 6 | ARG GROUP=$USER 7 | 8 | RUN apk add --no-cache sudo \ 9 | && echo "%wheel ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/01_wheel \ 10 | && addgroup -g 1001 "$GROUP" \ 11 | && adduser -D -s /bin/sh -G "$GROUP" -u 1001 "$USER" \ 12 | && adduser "$USER" wheel \ 13 | && echo "${USER}:${GROUP}" | chpasswd 14 | 15 | USER ${USER}:${GROUP} 16 | ENV USER="$USER" 17 | WORKDIR /home/$USER 18 | 19 | CMD ["/bin/sh", "-l"] 20 | -------------------------------------------------------------------------------- /support/dockerfiles/Dockerfile.arch: -------------------------------------------------------------------------------- 1 | ARG VERSION=base-20220925.0.89186 2 | FROM archlinux:$VERSION 3 | 4 | ARG VERSION 5 | ARG USER=jdoe 6 | ARG GROUP=$USER 7 | 8 | # hadolint ignore=DL4006 9 | RUN pacman -Syyu --noconfirm \ 10 | && pacman -Scc --noconfirm \ 11 | && pacman-key --init \ 12 | && pacman-key --populate archlinux \ 13 | && pacman -S --noconfirm sudo \ 14 | && echo "%wheel ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/01_wheel \ 15 | && groupadd --gid 1001 "$GROUP" \ 16 | && useradd --create-home --shell /bin/sh --groups wheel --gid "$GROUP" --uid 1001 "$USER" \ 17 | && echo "${USER}:${GROUP}" | chpasswd 18 | 19 | USER ${USER}:${GROUP} 20 | ENV USER="$USER" 21 | WORKDIR /home/$USER 22 | 23 | CMD ["/bin/sh", "-l"] 24 | -------------------------------------------------------------------------------- /support/dockerfiles/Dockerfile.centos: -------------------------------------------------------------------------------- 1 | ARG VERSION=8 2 | FROM centos:$VERSION 3 | 4 | ARG VERSION 5 | ARG USER=jdoe 6 | ARG GROUP=$USER 7 | 8 | RUN yum install -y sudo \ 9 | && echo "%adm ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/01_adm \ 10 | && groupadd --gid 1001 "$GROUP" \ 11 | && useradd --create-home --shell /bin/sh --groups adm --gid "$GROUP" --uid 1001 "$USER" \ 12 | && echo "${USER}:${GROUP}" | chpasswd 13 | 14 | USER ${USER}:${GROUP} 15 | ENV USER="$USER" 16 | WORKDIR /home/$USER 17 | 18 | CMD ["/bin/sh", "-l"] 19 | -------------------------------------------------------------------------------- /support/dockerfiles/Dockerfile.ubuntu: -------------------------------------------------------------------------------- 1 | ARG VERSION=20.04 2 | FROM ubuntu:$VERSION 3 | 4 | ARG VERSION 5 | ARG USER=jdoe 6 | ARG GROUP=$USER 7 | 8 | RUN apt-get update \ 9 | && apt-get install -y sudo \ 10 | && echo "%staff ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/01_staff \ 11 | && addgroup --gid 1001 "$GROUP" \ 12 | && adduser --disabled-password --gecos "$USER" --shell /bin/sh --gid 1001 --uid 1001 "$USER" \ 13 | && adduser "$USER" staff \ 14 | && echo "${USER}:${GROUP}" | chpasswd 15 | 16 | USER ${USER}:${GROUP} 17 | ENV USER="$USER" 18 | WORKDIR /home/$USER 19 | 20 | CMD ["/bin/sh", "-l"] 21 | -------------------------------------------------------------------------------- /support/lib/cli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # https://stackoverflow.com/a/28466267 5 | 6 | _print_usage_main() { 7 | local program="$1" 8 | local version="$2" 9 | local author="$3" 10 | 11 | echo "$program $version 12 | 13 | Continuous integration automation tooling 14 | 15 | USAGE: 16 | $program [FLAGS] [--] [ARG ..] 17 | 18 | FLAGS: 19 | -h, --help Prints help information 20 | -V, --version Prints version information 21 | -v, --verbose Prints verbose output 22 | 23 | SUBCOMMANDS: 24 | docker Manages Docker images and containers 25 | help Prints help information 26 | vagrant Manages Vagrant virtual machines 27 | version Prints version information 28 | 29 | SUBCOMMAND HELP: 30 | $program --help 31 | 32 | AUTHOR: 33 | $author 34 | " | sed 's/^ \{1,4\}//g' 35 | } 36 | 37 | cli__invoke_main() { 38 | local program version author 39 | program="$1" 40 | shift 41 | version="$1" 42 | shift 43 | author="$1" 44 | shift 45 | 46 | OPTIND=1 47 | while getopts "hvV-:" arg; do 48 | case "$arg" in 49 | h) 50 | _print_usage_main "$program" "$version" "$author" 51 | return 0 52 | ;; 53 | v) 54 | VERBOSE=true 55 | ;; 56 | V) 57 | print_version "$program" "$version" 58 | return 0 59 | ;; 60 | -) 61 | case "$OPTARG" in 62 | help) 63 | _print_usage_main "$program" "$version" "$author" 64 | return 0 65 | ;; 66 | verbose) 67 | VERBOSE=true 68 | ;; 69 | version) 70 | print_version "$program" "$version" "true" 71 | return 0 72 | ;; 73 | '') 74 | # "--" terminates argument processing 75 | break 76 | ;; 77 | *) 78 | _print_usage_main "$program" "$version" "$author" >&2 79 | die "invalid argument --$OPTARG" 80 | ;; 81 | esac 82 | ;; 83 | \?) 84 | _print_usage_main "$program" "$version" "$author" >&2 85 | die "invalid argument; arg=-$OPTARG" 86 | ;; 87 | esac 88 | done 89 | shift "$((OPTIND - 1))" 90 | 91 | if [ -z "${VERBOSE:-}" ]; then 92 | VERBOSE="" 93 | fi 94 | 95 | case "${1:-}" in 96 | docker) 97 | shift 98 | 99 | # shellcheck source=support/lib/cli_docker.sh 100 | . "$ROOT/lib/cli_docker.sh" 101 | 102 | cli_docker__invoke "$program" "$version" "$author" "$@" 103 | ;; 104 | vagrant) 105 | shift 106 | 107 | # shellcheck source=support/lib/cli_vagrant.sh 108 | . "$ROOT/lib/cli_vagrant.sh" 109 | 110 | cli_vagrant__invoke "$program" "$version" "$author" "$@" 111 | ;; 112 | help) 113 | _print_usage_main "$program" "$version" "$author" 114 | return 0 115 | ;; 116 | version) 117 | print_version "$program" "$version" "true" 118 | return 0 119 | ;; 120 | '') 121 | _print_usage_main "$program" "$version" "$author" >&2 122 | die "missing subcommand argument" 123 | ;; 124 | *) 125 | _print_usage_main "$program" "$version" "$author" >&2 126 | die "invalid argument; arg=${1:-}" 127 | ;; 128 | esac 129 | } 130 | -------------------------------------------------------------------------------- /support/lib/cli_docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_docker() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | echo "${program}-docker $version 10 | 11 | Manages Docker images and containers 12 | 13 | USAGE: 14 | $program docker [FLAGS] [--] [ARG ..] 15 | 16 | FLAGS: 17 | -h, --help Prints help information 18 | 19 | SUBCOMMANDS: 20 | build Builds a Docker image for distro from a workstation run 21 | clean Cleans Docker images for a distro and any related containers 22 | help Prints help information 23 | matrix Prints a list of distro/version/variant lines 24 | run Runs a Docker container from a built image 25 | 26 | SUBCOMMAND HELP: 27 | $program docker --help 28 | 29 | AUTHOR: 30 | $author 31 | " | sed 's/^ \{1,4\}//g' 32 | } 33 | 34 | cli_docker__invoke() { 35 | local program version author 36 | program="$1" 37 | shift 38 | version="$1" 39 | shift 40 | author="$1" 41 | shift 42 | 43 | OPTIND=1 44 | while getopts "h-:" arg; do 45 | case "$arg" in 46 | h) 47 | _print_usage_docker "$program" "$version" "$author" 48 | return 0 49 | ;; 50 | -) 51 | case "$OPTARG" in 52 | help) 53 | _print_usage_docker "$program" "$version" "$author" 54 | return 0 55 | ;; 56 | '') 57 | # "--" terminates argument processing 58 | break 59 | ;; 60 | *) 61 | _print_usage_docker "$program" "$version" "$author" >&2 62 | die "invalid argument --$OPTARG" 63 | ;; 64 | esac 65 | ;; 66 | \?) 67 | _print_usage_docker "$program" "$version" "$author" >&2 68 | die "invalid argument; arg=-$OPTARG" 69 | ;; 70 | esac 71 | done 72 | shift "$((OPTIND - 1))" 73 | 74 | case "${1:-}" in 75 | build) 76 | shift 77 | 78 | # shellcheck source=support/lib/cli_docker_build.sh 79 | . "$ROOT/lib/cli_docker_build.sh" 80 | 81 | cli_docker_build__invoke "$program" "$version" "$author" "$@" 82 | ;; 83 | clean) 84 | shift 85 | 86 | # shellcheck source=support/lib/cli_docker_clean.sh 87 | . "$ROOT/lib/cli_docker_clean.sh" 88 | 89 | cli_docker_clean__invoke "$program" "$version" "$author" "$@" 90 | ;; 91 | matrix) 92 | shift 93 | 94 | # shellcheck source=support/lib/cli_docker_matrix.sh 95 | . "$ROOT/lib/cli_docker_matrix.sh" 96 | 97 | cli_docker_matrix__invoke "$program" "$version" "$author" "$@" 98 | ;; 99 | run) 100 | shift 101 | 102 | # shellcheck source=support/lib/cli_docker_run.sh 103 | . "$ROOT/lib/cli_docker_run.sh" 104 | 105 | cli_docker_run__invoke "$program" "$version" "$author" "$@" 106 | ;; 107 | help) 108 | _print_usage_docker "$program" "$version" "$author" 109 | return 0 110 | ;; 111 | '') 112 | _print_usage_docker "$program" "$version" "$author" >&2 113 | die "missing subcommand argument" 114 | ;; 115 | *) 116 | _print_usage_docker "$program" "$version" "$author" >&2 117 | die "invalid argument; arg=${1:-}" 118 | ;; 119 | esac 120 | } 121 | -------------------------------------------------------------------------------- /support/lib/cli_docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_docker_build() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(docker__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(docker__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-docker-build $version 15 | 16 | Builds a Docker image for a distro from a workstation run 17 | 18 | USAGE: 19 | $program docker build [FLAGS] [--] [ [ []]] 20 | 21 | FLAGS: 22 | -F, --force Builds whether or not a previous image exists 23 | -h, --help Prints help information 24 | 25 | ARGS: 26 | Name of Linux distribution 27 | [values: $distros] 28 | Variant of workstation setup 29 | [values: $variants] 30 | Release version of a Linux distribution 31 | 32 | AUTHOR: 33 | $author 34 | " | sed 's/^ \{1,4\}//g' 35 | } 36 | 37 | cli_docker_build__invoke() { 38 | # shellcheck source=support/lib/cmd_docker_build.sh 39 | . "$ROOT/lib/cmd_docker_build.sh" 40 | 41 | local program version author force 42 | program="$1" 43 | shift 44 | version="$1" 45 | shift 46 | author="$1" 47 | shift 48 | force="" 49 | 50 | OPTIND=1 51 | while getopts "Fh-:" arg; do 52 | case "$arg" in 53 | F) 54 | force=true 55 | ;; 56 | h) 57 | _print_usage_docker_build "$program" "$version" "$author" 58 | return 0 59 | ;; 60 | -) 61 | case "$OPTARG" in 62 | force) 63 | force=true 64 | ;; 65 | help) 66 | _print_usage_docker_build "$program" "$version" "$author" 67 | return 0 68 | ;; 69 | '') 70 | # "--" terminates argument processing 71 | break 72 | ;; 73 | *) 74 | _print_usage_docker_build "$program" "$version" "$author" >&2 75 | die "invalid argument --$OPTARG" 76 | ;; 77 | esac 78 | ;; 79 | \?) 80 | _print_usage_docker_build "$program" "$version" "$author" >&2 81 | die "invalid argument; arg=-$OPTARG" 82 | ;; 83 | esac 84 | done 85 | shift "$((OPTIND - 1))" 86 | 87 | case "${1:-}" in 88 | help) 89 | _print_usage_docker_build "$program" "$version" "$author" 90 | return 0 91 | ;; 92 | '') 93 | cmd_docker_build__for "_" "_" "_" "$force" 94 | return 0 95 | ;; 96 | *) 97 | if ! docker__valid_distros | grep -q "^$1$"; then 98 | _print_usage_docker_build "$program" "$version" "$author" >&2 99 | die "invalid distro value; distro=$1" 100 | fi 101 | 102 | local distro="$1" 103 | ;; 104 | esac 105 | shift 106 | 107 | case "${1:-}" in 108 | '') 109 | cmd_docker_build__for "$distro" "_" "_" "$force" 110 | return 0 111 | ;; 112 | *) 113 | if ! docker__valid_versions_for "$distro" | grep -q "^$1$"; then 114 | _print_usage_docker_build "$program" "$version" "$author" >&2 115 | die "invalid version value; version=$1" 116 | fi 117 | 118 | local distro_version="$1" 119 | ;; 120 | esac 121 | shift 122 | 123 | if [ -z "${1:-}" ]; then 124 | cmd_docker_build__for "$distro" "$distro_version" "_" "$force" 125 | return 0 126 | fi 127 | local variant="$1" 128 | shift 129 | 130 | cmd_docker_build__for "$distro" "$distro_version" "$variant" "$force" 131 | } 132 | -------------------------------------------------------------------------------- /support/lib/cli_docker_clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_docker_clean() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(docker__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(docker__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-docker-clean $version 15 | 16 | Cleans Docker images for a distro and any related containers 17 | 18 | USAGE: 19 | $program docker clean [FLAGS] [--] [ [ []]] 20 | 21 | FLAGS: 22 | -h, --help Prints help information 23 | -y, --yes Answer yes for all questions 24 | 25 | ARGS: 26 | Name of Linux distribution 27 | [values: $distros] 28 | Variant of workstation setup 29 | [values: $variants] 30 | Release version of a Linux distribution 31 | 32 | AUTHOR: 33 | $author 34 | " | sed 's/^ \{1,4\}//g' 35 | } 36 | 37 | cli_docker_clean__invoke() { 38 | # shellcheck source=support/lib/cmd_docker_clean.sh 39 | . "$ROOT/lib/cmd_docker_clean.sh" 40 | 41 | local program version author answer_yes 42 | program="$1" 43 | shift 44 | version="$1" 45 | shift 46 | author="$1" 47 | shift 48 | answer_yes="" 49 | 50 | OPTIND=1 51 | while getopts "hy-:" arg; do 52 | case "$arg" in 53 | h) 54 | _print_usage_docker_clean "$program" "$version" "$author" 55 | return 0 56 | ;; 57 | y) 58 | answer_yes=true 59 | ;; 60 | -) 61 | case "$OPTARG" in 62 | help) 63 | _print_usage_docker_clean "$program" "$version" "$author" 64 | return 0 65 | ;; 66 | yes) 67 | answer_yes=true 68 | ;; 69 | '') 70 | # "--" terminates argument processing 71 | break 72 | ;; 73 | *) 74 | _print_usage_docker_clean "$program" "$version" "$author" >&2 75 | die "invalid argument --$OPTARG" 76 | ;; 77 | esac 78 | ;; 79 | \?) 80 | _print_usage_docker_clean "$program" "$version" "$author" >&2 81 | die "invalid argument; arg=-$OPTARG" 82 | ;; 83 | esac 84 | done 85 | shift "$((OPTIND - 1))" 86 | 87 | case "${1:-}" in 88 | help) 89 | _print_usage_docker_clean "$program" "$version" "$author" 90 | return 0 91 | ;; 92 | '') 93 | cmd_docker_clean__for "_" "_" "_" "$answer_yes" 94 | return 0 95 | ;; 96 | *) 97 | if ! docker__valid_distros | grep -q "^$1$"; then 98 | _print_usage_docker_clean "$program" "$version" "$author" >&2 99 | die "invalid distro value; distro=$1" 100 | fi 101 | 102 | local distro="$1" 103 | ;; 104 | esac 105 | shift 106 | 107 | case "${1:-}" in 108 | '') 109 | cmd_docker_clean__for "$distro" "_" "_" "$answer_yes" 110 | return 0 111 | ;; 112 | *) 113 | if ! docker__valid_versions_for "$distro" | grep -q "^$1$"; then 114 | _print_usage_docker_clean "$program" "$version" "$author" >&2 115 | die "invalid version value; version=$1" 116 | fi 117 | 118 | local distro_version="$1" 119 | ;; 120 | esac 121 | shift 122 | 123 | if [ -z "${1:-}" ]; then 124 | cmd_docker_clean__for "$distro" "$distro_version" "_" "$answer_yes" 125 | return 0 126 | fi 127 | local variant="$1" 128 | shift 129 | 130 | cmd_docker_clean__for "$distro" "$distro_version" "$variant" "$answer_yes" 131 | } 132 | -------------------------------------------------------------------------------- /support/lib/cli_docker_matrix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_docker_matrix() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(docker__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(docker__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-docker-matrix $version 15 | 16 | Prints a list of distro/version/variant lines 17 | 18 | USAGE: 19 | $program docker matrix [FLAGS] [--] [ [ []]] 20 | 21 | FLAGS: 22 | -h, --help Prints help information 23 | 24 | ARGS: 25 | Name of Linux distribution 26 | [values: $distros] 27 | Variant of workstation setup 28 | [values: $variants] 29 | Release version of a Linux distribution 30 | 31 | AUTHOR: 32 | $author 33 | " | sed 's/^ \{1,4\}//g' 34 | } 35 | 36 | cli_docker_matrix__invoke() { 37 | # shellcheck source=support/lib/cmd_docker_matrix.sh 38 | . "$ROOT/lib/cmd_docker_matrix.sh" 39 | 40 | local program version author 41 | program="$1" 42 | shift 43 | version="$1" 44 | shift 45 | author="$1" 46 | shift 47 | 48 | OPTIND=1 49 | while getopts "h-:" arg; do 50 | case "$arg" in 51 | h) 52 | _print_usage_docker_matrix "$program" "$version" "$author" 53 | return 0 54 | ;; 55 | -) 56 | case "$OPTARG" in 57 | help) 58 | _print_usage_docker_matrix "$program" "$version" "$author" 59 | return 0 60 | ;; 61 | '') 62 | # "--" terminates argument processing 63 | break 64 | ;; 65 | *) 66 | _print_usage_docker_matrix "$program" "$version" "$author" >&2 67 | die "invalid argument --$OPTARG" 68 | ;; 69 | esac 70 | ;; 71 | \?) 72 | _print_usage_docker_matrix "$program" "$version" "$author" >&2 73 | die "invalid argument; arg=-$OPTARG" 74 | ;; 75 | esac 76 | done 77 | shift "$((OPTIND - 1))" 78 | 79 | case "${1:-}" in 80 | help) 81 | _print_usage_docker_matrix "$program" "$version" "$author" 82 | return 0 83 | ;; 84 | '') 85 | cmd_docker_matrix__for "_" "_" "_" 86 | return 0 87 | ;; 88 | *) 89 | if ! docker__valid_distros | grep -q "^$1$"; then 90 | _print_usage_docker_matrix "$program" "$version" "$author" >&2 91 | die "invalid distro value; distro=$1" 92 | fi 93 | 94 | local distro="$1" 95 | ;; 96 | esac 97 | shift 98 | 99 | case "${1:-}" in 100 | '') 101 | cmd_docker_matrix__for "$distro" "_" "_" 102 | return 0 103 | ;; 104 | *) 105 | if ! docker__valid_versions_for "$distro" | grep -q "^$1$"; then 106 | _print_usage_docker_matrix "$program" "$version" "$author" >&2 107 | die "invalid version value; version=$1" 108 | fi 109 | 110 | local distro_version="$1" 111 | ;; 112 | esac 113 | shift 114 | 115 | if [ -z "${1:-}" ]; then 116 | cmd_docker_matrix__for "$distro" "$distro_version" "_" 117 | return 0 118 | fi 119 | local variant="$1" 120 | shift 121 | 122 | cmd_docker_matrix__for "$distro" "$distro_version" "$variant" 123 | } 124 | -------------------------------------------------------------------------------- /support/lib/cli_docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_docker_run() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(docker__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(docker__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-docker-run $version 15 | 16 | Runs a Docker container from a built image 17 | 18 | USAGE: 19 | $program docker run [FLAGS] [OPTIONS] [--] [ ..] 20 | 21 | FLAGS: 22 | -h, --help Prints help information 23 | -v, --verbose Prints verbose output 24 | 25 | OPTIONS: 26 | -D, --docker-opts= Addition options passed to \`docker run\` 27 | 28 | ARGS: 29 | Additional arguments passed to the container 30 | Name of Linux distribution 31 | [values: $distros] 32 | Variant of workstation setup 33 | [values: $variants] 34 | Release version of a Linux distribution 35 | 36 | AUTHOR: 37 | $author 38 | " | sed 's/^ \{1,4\}//g' 39 | } 40 | 41 | cli_docker_run__invoke() { 42 | # shellcheck source=support/lib/cmd_docker_run.sh 43 | . "$ROOT/lib/cmd_docker_run.sh" 44 | 45 | local program version author 46 | program="$1" 47 | shift 48 | version="$1" 49 | shift 50 | author="$1" 51 | shift 52 | 53 | OPTIND=1 54 | while getopts "D:hv-:" arg; do 55 | case "$arg" in 56 | D) 57 | local opts="$OPTARG" 58 | ;; 59 | h) 60 | _print_usage_docker_run "$program" "$version" "$author" 61 | return 0 62 | ;; 63 | v) 64 | VERBOSE=true 65 | ;; 66 | -) 67 | long_optarg="${OPTARG#*=}" 68 | case "$OPTARG" in 69 | docker-opts=?*) 70 | local opts="$long_optarg" 71 | ;; 72 | docker-opts*) 73 | _print_usage_docker_run "$program" "$version" "$author" 74 | die "missing required argument for --$OPTARG option" 75 | ;; 76 | help) 77 | _print_usage_docker_run "$program" "$version" "$author" 78 | return 0 79 | ;; 80 | verbose) 81 | VERBOSE=true 82 | ;; 83 | '') 84 | # "--" terminates argument processing 85 | break 86 | ;; 87 | *) 88 | _print_usage_docker_run "$program" "$version" "$author" >&2 89 | die "invalid argument --$OPTARG" 90 | ;; 91 | esac 92 | ;; 93 | \?) 94 | _print_usage_docker_run "$program" "$version" "$author" >&2 95 | die "invalid argument; arg=-$OPTARG" 96 | ;; 97 | esac 98 | done 99 | shift "$((OPTIND - 1))" 100 | 101 | case "${1:-}" in 102 | help) 103 | _print_usage_docker_run "$program" "$version" "$author" 104 | return 0 105 | ;; 106 | '') 107 | _print_usage_docker_run "$program" "$version" "$author" >&2 108 | die "missing distro argument" 109 | ;; 110 | *) 111 | if ! docker__valid_distros | grep -q "^$1$"; then 112 | _print_usage_docker_run "$program" "$version" "$author" >&2 113 | die "invalid distro value; distro=$1" 114 | fi 115 | 116 | local distro="$1" 117 | ;; 118 | esac 119 | shift 120 | 121 | case "${1:-}" in 122 | '') 123 | _print_usage_docker_run "$program" "$version" "$author" >&2 124 | die "missing version argument" 125 | ;; 126 | *) 127 | if ! docker__valid_versions_for "$distro" | grep -q "^$1$"; then 128 | _print_usage_docker_run "$program" "$version" "$author" >&2 129 | die "invalid version value; version=$1" 130 | fi 131 | 132 | local distro_version="$1" 133 | ;; 134 | esac 135 | shift 136 | 137 | if [ -z "${1:-}" ]; then 138 | _print_usage_docker_run "$program" "$version" "$author" >&2 139 | die "missing variant argument" 140 | fi 141 | local variant="$1" 142 | shift 143 | 144 | local img 145 | img="$(docker__img_variant_name "$distro" "$distro_version" "$variant")" 146 | 147 | cmd_docker_run__exec "$img" "${opts:-}" "$@" 148 | } 149 | -------------------------------------------------------------------------------- /support/lib/cli_vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_vagrant() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | echo "${program}-vagrant $version 10 | 11 | Manages Vagrant virtual machines 12 | 13 | USAGE: 14 | $program vagrant [FLAGS] [--] [ARG ..] 15 | 16 | FLAGS: 17 | -h, --help Prints help information 18 | 19 | SUBCOMMANDS: 20 | build Builds Vagrant virtual machines for a distro 21 | console Launches a console session in the built virtual machine 22 | clean Cleans and destroys Vagrant virtual machines 23 | help Prints help information 24 | matrix Prints a list of distro/version/variant lines 25 | 26 | SUBCOMMAND HELP: 27 | $program vagrant --help 28 | 29 | AUTHOR: 30 | $author 31 | " | sed 's/^ \{1,4\}//g' 32 | } 33 | 34 | cli_vagrant__invoke() { 35 | local program version author 36 | program="$1" 37 | shift 38 | version="$1" 39 | shift 40 | author="$1" 41 | shift 42 | 43 | OPTIND=1 44 | while getopts "h-:" arg; do 45 | case "$arg" in 46 | h) 47 | _print_usage_vagrant "$program" "$version" "$author" 48 | return 0 49 | ;; 50 | -) 51 | case "$OPTARG" in 52 | help) 53 | _print_usage_vagrant "$program" "$version" "$author" 54 | return 0 55 | ;; 56 | '') 57 | # "--" terminates argument processing 58 | break 59 | ;; 60 | *) 61 | _print_usage_vagrant "$program" "$version" "$author" >&2 62 | die "invalid argument --$OPTARG" 63 | ;; 64 | esac 65 | ;; 66 | \?) 67 | _print_usage_vagrant "$program" "$version" "$author" >&2 68 | die "invalid argument; arg=-$OPTARG" 69 | ;; 70 | esac 71 | done 72 | shift "$((OPTIND - 1))" 73 | 74 | case "${1:-}" in 75 | build) 76 | shift 77 | 78 | # shellcheck source=support/lib/cli_vagrant_build.sh 79 | . "$ROOT/lib/cli_vagrant_build.sh" 80 | 81 | cli_vagrant_build__invoke "$program" "$version" "$author" "$@" 82 | ;; 83 | clean) 84 | shift 85 | 86 | # shellcheck source=support/lib/cli_vagrant_clean.sh 87 | . "$ROOT/lib/cli_vagrant_clean.sh" 88 | 89 | cli_vagrant_clean__invoke "$program" "$version" "$author" "$@" 90 | ;; 91 | console) 92 | shift 93 | 94 | # shellcheck source=support/lib/cli_vagrant_console.sh 95 | . "$ROOT/lib/cli_vagrant_console.sh" 96 | 97 | cli_vagrant_console__invoke "$program" "$version" "$author" "$@" 98 | ;; 99 | matrix) 100 | shift 101 | 102 | # shellcheck source=support/lib/cli_vagrant_matrix.sh 103 | . "$ROOT/lib/cli_vagrant_matrix.sh" 104 | 105 | cli_vagrant_matrix__invoke "$program" "$version" "$author" "$@" 106 | ;; 107 | help) 108 | _print_usage_vagrant "$program" "$version" "$author" 109 | return 0 110 | ;; 111 | '') 112 | _print_usage_vagrant "$program" "$version" "$author" >&2 113 | die "missing subcommand argument" 114 | ;; 115 | *) 116 | _print_usage_vagrant "$program" "$version" "$author" >&2 117 | die "invalid argument; arg=${1:-}" 118 | ;; 119 | esac 120 | } 121 | -------------------------------------------------------------------------------- /support/lib/cli_vagrant_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_vagrant_build() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(vagrant__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(vagrant__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-vagrant-build $version 15 | 16 | Builds Vagrant virtual machines for a distro 17 | 18 | USAGE: 19 | $program docker build [FLAGS] [--] [ [ []]] 20 | 21 | FLAGS: 22 | -F, --force Builds whether or not a previous image exists 23 | -h, --help Prints help information 24 | 25 | ARGS: 26 | Name of Linux distribution 27 | [values: $distros] 28 | Variant of workstation setup 29 | [values: $variants] 30 | Release version of a Linux distribution 31 | 32 | AUTHOR: 33 | $author 34 | " | sed 's/^ \{1,4\}//g' 35 | } 36 | 37 | cli_vagrant_build__invoke() { 38 | # shellcheck source=support/lib/cmd_vagrant_build.sh 39 | . "$ROOT/lib/cmd_vagrant_build.sh" 40 | 41 | local program version author force 42 | program="$1" 43 | shift 44 | version="$1" 45 | shift 46 | author="$1" 47 | shift 48 | force="" 49 | 50 | OPTIND=1 51 | while getopts "Fh-:" arg; do 52 | case "$arg" in 53 | F) 54 | force=true 55 | ;; 56 | h) 57 | _print_usage_vagrant_build "$program" "$version" "$author" 58 | return 0 59 | ;; 60 | -) 61 | case "$OPTARG" in 62 | force) 63 | force=true 64 | ;; 65 | help) 66 | _print_usage_vagrant_build "$program" "$version" "$author" 67 | return 0 68 | ;; 69 | '') 70 | # "--" terminates argument processing 71 | break 72 | ;; 73 | *) 74 | _print_usage_vagrant_build "$program" "$version" "$author" >&2 75 | die "invalid argument --$OPTARG" 76 | ;; 77 | esac 78 | ;; 79 | \?) 80 | _print_usage_vagrant_build "$program" "$version" "$author" >&2 81 | die "invalid argument; arg=-$OPTARG" 82 | ;; 83 | esac 84 | done 85 | shift "$((OPTIND - 1))" 86 | 87 | case "${1:-}" in 88 | help) 89 | _print_usage_vagrant_build "$program" "$version" "$author" 90 | return 0 91 | ;; 92 | '') 93 | cmd_vagrant_build__for "_" "_" "_" "$force" 94 | return 0 95 | ;; 96 | *) 97 | if ! vagrant__valid_distros | grep -q "^$1$"; then 98 | _print_usage_vagrant_build "$program" "$version" "$author" >&2 99 | die "invalid distro value; distro=$1" 100 | fi 101 | 102 | local distro="$1" 103 | ;; 104 | esac 105 | shift 106 | 107 | case "${1:-}" in 108 | '') 109 | cmd_vagrant_build__for "$distro" "_" "_" "$force" 110 | return 0 111 | ;; 112 | *) 113 | if ! vagrant__valid_versions_for "$distro" | grep -q "^$1$"; then 114 | _print_usage_vagrant_build "$program" "$version" "$author" >&2 115 | die "invalid version value; version=$1" 116 | fi 117 | 118 | local distro_version="$1" 119 | ;; 120 | esac 121 | shift 122 | 123 | if [ -z "${1:-}" ]; then 124 | cmd_vagrant_build__for "$distro" "$distro_version" "_" "$force" 125 | return 0 126 | fi 127 | local variant="$1" 128 | shift 129 | 130 | cmd_vagrant_build__for "$distro" "$distro_version" "$variant" "$force" 131 | } 132 | -------------------------------------------------------------------------------- /support/lib/cli_vagrant_clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_vagrant_clean() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(vagrant__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(vagrant__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-vagrant-clean $version 15 | 16 | Cleans and destroys Vagrant virtual machines 17 | 18 | USAGE: 19 | $program vagrant clean [FLAGS] [--] [ [ []]] 20 | 21 | FLAGS: 22 | -h, --help Prints help information 23 | -y, --yes Answer yes for all questions 24 | 25 | ARGS: 26 | Name of Linux distribution 27 | [values: $distros] 28 | Variant of workstation setup 29 | [values: $variants] 30 | Release version of a Linux distribution 31 | 32 | AUTHOR: 33 | $author 34 | " | sed 's/^ \{1,4\}//g' 35 | } 36 | 37 | cli_vagrant_clean__invoke() { 38 | # shellcheck source=support/lib/cmd_vagrant_clean.sh 39 | . "$ROOT/lib/cmd_vagrant_clean.sh" 40 | 41 | local program version author answer_yes 42 | program="$1" 43 | shift 44 | version="$1" 45 | shift 46 | author="$1" 47 | shift 48 | answer_yes="" 49 | 50 | OPTIND=1 51 | while getopts "hy-:" arg; do 52 | case "$arg" in 53 | h) 54 | _print_usage_vagrant_clean "$program" "$version" "$author" 55 | return 0 56 | ;; 57 | y) 58 | answer_yes=true 59 | ;; 60 | -) 61 | case "$OPTARG" in 62 | help) 63 | _print_usage_vagrant_clean "$program" "$version" "$author" 64 | return 0 65 | ;; 66 | yes) 67 | answer_yes=true 68 | ;; 69 | '') 70 | # "--" terminates argument processing 71 | break 72 | ;; 73 | *) 74 | _print_usage_vagrant_clean "$program" "$version" "$author" >&2 75 | die "invalid argument --$OPTARG" 76 | ;; 77 | esac 78 | ;; 79 | \?) 80 | _print_usage_vagrant_clean "$program" "$version" "$author" >&2 81 | die "invalid argument; arg=-$OPTARG" 82 | ;; 83 | esac 84 | done 85 | shift "$((OPTIND - 1))" 86 | 87 | case "${1:-}" in 88 | help) 89 | _print_usage_vagrant_clean "$program" "$version" "$author" 90 | return 0 91 | ;; 92 | '') 93 | cmd_vagrant_clean__for "_" "_" "_" "$answer_yes" 94 | return 0 95 | ;; 96 | *) 97 | if ! vagrant__valid_distros | grep -q "^$1$"; then 98 | _print_usage_vagrant_clean "$program" "$version" "$author" >&2 99 | die "invalid distro value; distro=$1" 100 | fi 101 | 102 | local distro="$1" 103 | ;; 104 | esac 105 | shift 106 | 107 | case "${1:-}" in 108 | '') 109 | cmd_vagrant_clean__for "$distro" "_" "_" "$answer_yes" 110 | return 0 111 | ;; 112 | *) 113 | if ! vagrant__valid_versions_for "$distro" | grep -q "^$1$"; then 114 | _print_usage_vagrant_clean "$program" "$version" "$author" >&2 115 | die "invalid version value; version=$1" 116 | fi 117 | 118 | local distro_version="$1" 119 | ;; 120 | esac 121 | shift 122 | 123 | if [ -z "${1:-}" ]; then 124 | cmd_vagrant_clean__for "$distro" "$distro_version" "_" "$answer_yes" 125 | return 0 126 | fi 127 | local variant="$1" 128 | shift 129 | 130 | cmd_vagrant_clean__for "$distro" "$distro_version" "$variant" "$answer_yes" 131 | } 132 | -------------------------------------------------------------------------------- /support/lib/cli_vagrant_console.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_vagrant_console() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(vagrant__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(vagrant__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-vagrant-console $version 15 | 16 | Launches a console session in the built virtual machine 17 | 18 | USAGE: 19 | $program vagrant console [FLAGS] [OPTIONS] [--] [ ..] 20 | 21 | FLAGS: 22 | -h, --help Prints help information 23 | -v, --verbose Prints verbose output 24 | 25 | ARGS: 26 | Additional arguments passed to the virtual machine 27 | Name of Linux distribution 28 | [values: $distros] 29 | Variant of workstation setup 30 | [values: $variants] 31 | Release version of a Linux distribution 32 | 33 | AUTHOR: 34 | $author 35 | " | sed 's/^ \{1,4\}//g' 36 | } 37 | 38 | cli_vagrant_console__invoke() { 39 | # shellcheck source=support/lib/cmd_vagrant_console.sh 40 | . "$ROOT/lib/cmd_vagrant_console.sh" 41 | 42 | local program version author 43 | program="$1" 44 | shift 45 | version="$1" 46 | shift 47 | author="$1" 48 | shift 49 | 50 | OPTIND=1 51 | while getopts "hv-:" arg; do 52 | case "$arg" in 53 | h) 54 | _print_usage_vagrant_console "$program" "$version" "$author" 55 | return 0 56 | ;; 57 | v) 58 | VERBOSE=true 59 | ;; 60 | -) 61 | case "$OPTARG" in 62 | help) 63 | _print_usage_vagrant_console "$program" "$version" "$author" 64 | return 0 65 | ;; 66 | verbose) 67 | VERBOSE=true 68 | ;; 69 | '') 70 | # "--" terminates argument processing 71 | break 72 | ;; 73 | *) 74 | _print_usage_vagrant_console "$program" "$version" "$author" >&2 75 | die "invalid argument --$OPTARG" 76 | ;; 77 | esac 78 | ;; 79 | \?) 80 | _print_usage_vagrant_console "$program" "$version" "$author" >&2 81 | die "invalid argument; arg=-$OPTARG" 82 | ;; 83 | esac 84 | done 85 | shift "$((OPTIND - 1))" 86 | 87 | case "${1:-}" in 88 | help) 89 | _print_usage_vagrant_console "$program" "$version" "$author" 90 | return 0 91 | ;; 92 | '') 93 | _print_usage_vagrant_console "$program" "$version" "$author" >&2 94 | die "missing distro argument" 95 | ;; 96 | *) 97 | if ! vagrant__valid_distros | grep -q "^$1$"; then 98 | _print_usage_vagrant_console "$program" "$version" "$author" >&2 99 | die "invalid distro value; distro=$1" 100 | fi 101 | 102 | local distro="$1" 103 | ;; 104 | esac 105 | shift 106 | 107 | case "${1:-}" in 108 | '') 109 | _print_usage_vagrant_console "$program" "$version" "$author" >&2 110 | die "missing version argument" 111 | ;; 112 | *) 113 | if ! vagrant__valid_versions_for "$distro" | grep -q "^$1$"; then 114 | _print_usage_vagrant_console "$program" "$version" "$author" >&2 115 | die "invalid version value; version=$1" 116 | fi 117 | 118 | local distro_version="$1" 119 | ;; 120 | esac 121 | shift 122 | 123 | if [ -z "${1:-}" ]; then 124 | _print_usage_vagrant_console "$program" "$version" "$author" >&2 125 | die "missing variant argument" 126 | fi 127 | local variant="$1" 128 | shift 129 | 130 | local vm 131 | vm="$(vagrant__vm_variant_name "$distro" "$distro_version" "$variant")" 132 | 133 | cmd_vagrant_console__exec "$vm" "${opts:-}" "$@" 134 | } 135 | -------------------------------------------------------------------------------- /support/lib/cli_vagrant_matrix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | _print_usage_vagrant_matrix() { 5 | local program="$1" 6 | local version="$2" 7 | local author="$3" 8 | 9 | local distros 10 | distros="$(vagrant__valid_distros | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 11 | local variants 12 | variants="$(vagrant__valid_variants | tr '\n' ',' | sed -e 's/,$//' -e 's/,/, /g')" 13 | 14 | echo "${program}-vagrant-matrix $version 15 | 16 | Prints a list of distro/version/variant lines 17 | 18 | USAGE: 19 | $program vagrant matrix [FLAGS] [--] [ [ []]] 20 | 21 | FLAGS: 22 | -h, --help Prints help information 23 | 24 | ARGS: 25 | Name of Linux distribution 26 | [values: $distros] 27 | Variant of workstation setup 28 | [values: $variants] 29 | Release version of a Linux distribution 30 | 31 | AUTHOR: 32 | $author 33 | " | sed 's/^ \{1,4\}//g' 34 | } 35 | 36 | cli_vagrant_matrix__invoke() { 37 | # shellcheck source=support/lib/cmd_vagrant_matrix.sh 38 | . "$ROOT/lib/cmd_vagrant_matrix.sh" 39 | 40 | local program version author 41 | program="$1" 42 | shift 43 | version="$1" 44 | shift 45 | author="$1" 46 | shift 47 | 48 | OPTIND=1 49 | while getopts "h-:" arg; do 50 | case "$arg" in 51 | h) 52 | _print_usage_vagrant_matrix "$program" "$version" "$author" 53 | return 0 54 | ;; 55 | -) 56 | case "$OPTARG" in 57 | help) 58 | _print_usage_vagrant_matrix "$program" "$version" "$author" 59 | return 0 60 | ;; 61 | '') 62 | # "--" terminates argument processing 63 | break 64 | ;; 65 | *) 66 | _print_usage_vagrant_matrix "$program" "$version" "$author" >&2 67 | die "invalid argument --$OPTARG" 68 | ;; 69 | esac 70 | ;; 71 | \?) 72 | _print_usage_vagrant_matrix "$program" "$version" "$author" >&2 73 | die "invalid argument; arg=-$OPTARG" 74 | ;; 75 | esac 76 | done 77 | shift "$((OPTIND - 1))" 78 | 79 | case "${1:-}" in 80 | help) 81 | _print_usage_vagrant_matrix "$program" "$version" "$author" 82 | return 0 83 | ;; 84 | '') 85 | cmd_vagrant_matrix__for "_" "_" "_" 86 | return 0 87 | ;; 88 | *) 89 | if ! vagrant__valid_distros | grep -q "^$1$"; then 90 | _print_usage_vagrant_matrix "$program" "$version" "$author" >&2 91 | die "invalid distro value; distro=$1" 92 | fi 93 | 94 | local distro="$1" 95 | ;; 96 | esac 97 | shift 98 | 99 | case "${1:-}" in 100 | '') 101 | cmd_vagrant_matrix__for "$distro" "_" "_" 102 | return 0 103 | ;; 104 | *) 105 | if ! vagrant__valid_versions_for "$distro" | grep -q "^$1$"; then 106 | _print_usage_vagrant_matrix "$program" "$version" "$author" >&2 107 | die "invalid version value; version=$1" 108 | fi 109 | 110 | local distro_version="$1" 111 | ;; 112 | esac 113 | shift 114 | 115 | if [ -z "${1:-}" ]; then 116 | cmd_vagrant_matrix__for "$distro" "$distro_version" "_" 117 | return 0 118 | fi 119 | local variant="$1" 120 | shift 121 | 122 | cmd_vagrant_matrix__for "$distro" "$distro_version" "$variant" 123 | } 124 | -------------------------------------------------------------------------------- /support/lib/cmd_docker_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/docker.sh 5 | . "$ROOT/lib/docker.sh" 6 | 7 | cmd_docker_build__for() { 8 | local distro="$1" 9 | local version="$2" 10 | local variant="$3" 11 | local force="$4" 12 | 13 | if [ "_" = "$distro" ]; then 14 | for distro in $(docker__distros); do 15 | _process_distro "$distro" "$version" "$variant" "$force" 16 | done 17 | else 18 | _process_distro "$distro" "$version" "$variant" "$force" 19 | fi 20 | } 21 | 22 | _process_distro() { 23 | local distro="$1" 24 | local version="$2" 25 | local variant="$3" 26 | local force="$4" 27 | 28 | if [ "_" = "$version" ]; then 29 | for version in $(docker__versions_for "$distro"); do 30 | _process_version "$distro" "$version" "$variant" "$force" 31 | done 32 | else 33 | _process_version "$distro" "$version" "$variant" "$force" 34 | fi 35 | } 36 | 37 | _process_version() { 38 | local distro="$1" 39 | local version="$2" 40 | local variant="$3" 41 | local force="$4" 42 | 43 | if [ "_" = "$variant" ]; then 44 | for variant in $(docker__variants); do 45 | _build "$distro" "$version" "$variant" "$force" 46 | done 47 | else 48 | _build "$distro" "$version" "$variant" "$force" 49 | fi 50 | } 51 | 52 | _build() { 53 | local distro="$1" 54 | local version="$2" 55 | local variant="$3" 56 | local force="$4" 57 | 58 | if [ "pre" = "$variant" ]; then 59 | _build_pre "$distro" "$version" "$force" 60 | else 61 | _build_variant "$distro" "$version" "$variant" "$force" 62 | fi 63 | } 64 | 65 | _build_pre() { 66 | local distro="$1" 67 | local version="$2" 68 | local force="$3" 69 | 70 | local img 71 | img_pre="$(docker__img_pre_name "$distro" "$version")" 72 | 73 | if [ -z "$force" ] && docker__img_pre_exists "$distro" "$version"; then 74 | section "Pre image exists, skipping; img_pre=$img_pre" 75 | return 0 76 | fi 77 | 78 | local dockerfile="$ROOT/dockerfiles/Dockerfile.$distro" 79 | if [ ! -f "$dockerfile" ]; then 80 | warn "dockerfile not found; dockerfile=$dockerfile" 81 | return 1 82 | fi 83 | 84 | echo "--- Building pre image; img_pre=$img_pre" 85 | docker image build \ 86 | --pull \ 87 | --force-rm \ 88 | --build-arg "VERSION=$version" \ 89 | --tag "$img_pre" \ 90 | - \ 91 | <"$dockerfile" 92 | } 93 | 94 | _build_variant() { 95 | local distro="$1" 96 | local version="$2" 97 | local variant="$3" 98 | local force="$4" 99 | 100 | if ! docker__img_pre_exists "$distro" "$version"; then 101 | _build_pre "$distro" "$version" "$force" 102 | fi 103 | 104 | local img_pre img 105 | img_pre="$(docker__img_pre_name "$distro" "$version")" 106 | img="$(docker__img_variant_name "$distro" "$version" "$variant")" 107 | 108 | if [ -z "$force" ] && docker__img_variant_exists "$distro" "$version" "$variant"; then 109 | section "Image exists, skipping; img=$img" 110 | return 0 111 | fi 112 | 113 | section "Running workstation; variant=$variant, img_pre=$img_pre" 114 | cid="$(docker container run \ 115 | --detach \ 116 | --env TERM=screen-256color \ 117 | --volume="$(pwd)":/usr/src:ro \ 118 | "$img_pre" \ 119 | /usr/src/bin/prep "--profile=$variant")" 120 | docker container attach "$cid" 121 | 122 | section "Building image; img=$img" 123 | docker container commit --change='CMD ["/bin/bash", "-l"]' "$cid" "$img" 124 | docker container rm "$cid" 125 | } 126 | -------------------------------------------------------------------------------- /support/lib/cmd_docker_clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/docker.sh 5 | . "$ROOT/lib/docker.sh" 6 | 7 | cmd_docker_clean__for() { 8 | local distro="$1" 9 | local version="$2" 10 | local variant="$3" 11 | local force="$4" 12 | 13 | if [ "_" = "$distro" ]; then 14 | for distro in $(docker__distros); do 15 | _process_distro "$distro" "$version" "$variant" "$force" 16 | done 17 | else 18 | _process_distro "$distro" "$version" "$variant" "$force" 19 | fi 20 | } 21 | 22 | _process_distro() { 23 | local distro="$1" 24 | local version="$2" 25 | local variant="$3" 26 | local force="$4" 27 | 28 | if [ "_" = "$version" ]; then 29 | for version in $(docker__versions_for "$distro"); do 30 | _process_version "$distro" "$version" "$variant" "$force" 31 | done 32 | else 33 | _process_version "$distro" "$version" "$variant" "$force" 34 | fi 35 | } 36 | 37 | _process_version() { 38 | local distro="$1" 39 | local version="$2" 40 | local variant="$3" 41 | local force="$4" 42 | 43 | if [ "_" = "$variant" ]; then 44 | # Reverse list of variants so that all derivatives are removed before the 45 | # "pre" image 46 | for variant in $(docker__variants | _reverse_lines); do 47 | _clean "$distro" "$version" "$variant" "$force" 48 | done 49 | else 50 | _clean "$distro" "$version" "$variant" "$force" 51 | fi 52 | } 53 | 54 | _clean() { 55 | local distro="$1" 56 | local version="$2" 57 | local variant="$3" 58 | local force="$4" 59 | 60 | local img 61 | img="$(docker__img_variant_name "$distro" "$version" "$variant")" 62 | local containers images 63 | 64 | if [ -z "$force" ]; then 65 | _confirm "Are you sure you want to clean $img?" 66 | fi 67 | 68 | # Kill any currently running containers that are descendant from `$img` 69 | containers="$(docker container ls --all --quiet \ 70 | --filter "ancestor=$img" --filter status=running)" 71 | if [ -n "$containers" ]; then 72 | section "Killing running containers; img=$img" 73 | echo "$containers" | xargs docker container kill 74 | fi 75 | 76 | # Remove all containers that are descendant from `$img` 77 | containers="$(docker container ls --all --quiet --filter "ancestor=$img")" 78 | if [ -n "$containers" ]; then 79 | section "Removing containers; img=$img" 80 | echo "$containers" | xargs docker container rm 81 | fi 82 | 83 | # Remove all images related to `$img 84 | images="$(docker image ls --quiet "$img")" 85 | if [ -n "$images" ]; then 86 | section "Removing images; img=$img" 87 | echo "$images" | xargs docker image rm 88 | fi 89 | } 90 | 91 | _confirm() { 92 | local msg="$1" 93 | local answer 94 | 95 | while true; do 96 | printf -- "%s [%s/%s/%s] " "$msg" "Yes" "no" "quit" 97 | read -r answer 98 | 99 | case "$answer" in 100 | q | quit | Q | Quit | QUIT) 101 | echo "Quitting" 102 | exit 0 103 | ;; 104 | y | Y | yes | Yes | YES | '') 105 | break 106 | ;; 107 | esac 108 | done 109 | } 110 | 111 | # Reverse order of lines (emulating `tac`) 112 | # Thanks to "Handy One-Liners for Awk": http://tiny.cc/daxe6y 113 | _reverse_lines() { 114 | awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' 115 | } 116 | -------------------------------------------------------------------------------- /support/lib/cmd_docker_matrix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/docker.sh 5 | . "$ROOT/lib/docker.sh" 6 | 7 | cmd_docker_matrix__for() { 8 | local distro="$1" 9 | local version="$2" 10 | local variant="$3" 11 | 12 | if [ "_" = "$distro" ]; then 13 | for distro in $(docker__distros); do 14 | _process_distro "$distro" "$version" "$variant" 15 | done 16 | else 17 | _process_distro "$distro" "$version" "$variant" 18 | fi 19 | } 20 | 21 | _process_distro() { 22 | local distro="$1" 23 | local version="$2" 24 | local variant="$3" 25 | 26 | if [ "_" = "$version" ]; then 27 | for version in $(docker__versions_for "$distro"); do 28 | _process_version "$distro" "$version" "$variant" 29 | done 30 | else 31 | _process_version "$distro" "$version" "$variant" 32 | fi 33 | } 34 | 35 | _process_version() { 36 | local distro="$1" 37 | local version="$2" 38 | local variant="$3" 39 | 40 | if [ "_" = "$variant" ]; then 41 | for variant in $(docker__variants); do 42 | _print "$distro" "$version" "$variant" 43 | done 44 | else 45 | _print "$distro" "$version" "$variant" 46 | fi 47 | } 48 | 49 | _print() { 50 | local distro="$1" 51 | local version="$2" 52 | local variant="$3" 53 | 54 | printf -- "%s %s %s\n" "$distro" "$version" "$variant" 55 | } 56 | -------------------------------------------------------------------------------- /support/lib/cmd_docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/docker.sh 5 | . "$ROOT/lib/docker.sh" 6 | 7 | cmd_docker_run__exec() { 8 | local img="$1" 9 | shift 10 | local opts="${1:-}" 11 | shift 12 | 13 | if [ -n "$VERBOSE" ]; then 14 | set -x 15 | fi 16 | 17 | # shellcheck disable=SC2086 18 | exec docker run $opts "$img" "$@" 19 | } 20 | -------------------------------------------------------------------------------- /support/lib/cmd_vagrant_build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/vagrant.sh 5 | . "$ROOT/lib/vagrant.sh" 6 | 7 | cmd_vagrant_build__for() { 8 | local distro="$1" 9 | local version="$2" 10 | local variant="$3" 11 | local force="$4" 12 | 13 | if [ "_" = "$distro" ]; then 14 | for distro in $(vagrant__distros); do 15 | _process_distro "$distro" "$version" "$variant" "$force" 16 | done 17 | else 18 | _process_distro "$distro" "$version" "$variant" "$force" 19 | fi 20 | } 21 | 22 | _process_distro() { 23 | local distro="$1" 24 | local version="$2" 25 | local variant="$3" 26 | local force="$4" 27 | 28 | if [ "_" = "$version" ]; then 29 | for version in $(vagrant__versions_for "$distro"); do 30 | _process_version "$distro" "$version" "$variant" "$force" 31 | done 32 | else 33 | _process_version "$distro" "$version" "$variant" "$force" 34 | fi 35 | } 36 | 37 | _process_version() { 38 | local distro="$1" 39 | local version="$2" 40 | local variant="$3" 41 | local force="$4" 42 | 43 | if [ "_" = "$variant" ]; then 44 | for variant in $(vagrant__variants); do 45 | _build "$distro" "$version" "$variant" "$force" 46 | done 47 | else 48 | _build "$distro" "$version" "$variant" "$force" 49 | fi 50 | } 51 | 52 | _build() { 53 | local distro="$1" 54 | local version="$2" 55 | local variant="$3" 56 | local force="$4" 57 | 58 | local vm 59 | vm="$(vagrant__vm_variant_name "$distro" "$version" "$variant")" 60 | 61 | section "Building vm; vm=$vm" 62 | vagrant up "$vm" 63 | } 64 | -------------------------------------------------------------------------------- /support/lib/cmd_vagrant_clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/vagrant.sh 5 | . "$ROOT/lib/vagrant.sh" 6 | 7 | cmd_vagrant_clean__for() { 8 | local distro="$1" 9 | local version="$2" 10 | local variant="$3" 11 | local force="$4" 12 | 13 | if [ "_" = "$distro" ]; then 14 | for distro in $(vagrant__distros); do 15 | _process_distro "$distro" "$version" "$variant" "$force" 16 | done 17 | else 18 | _process_distro "$distro" "$version" "$variant" "$force" 19 | fi 20 | } 21 | 22 | _process_distro() { 23 | local distro="$1" 24 | local version="$2" 25 | local variant="$3" 26 | local force="$4" 27 | 28 | if [ "_" = "$version" ]; then 29 | for version in $(vagrant__versions_for "$distro"); do 30 | _process_version "$distro" "$version" "$variant" "$force" 31 | done 32 | else 33 | _process_version "$distro" "$version" "$variant" "$force" 34 | fi 35 | } 36 | 37 | _process_version() { 38 | local distro="$1" 39 | local version="$2" 40 | local variant="$3" 41 | local force="$4" 42 | 43 | if [ "_" = "$variant" ]; then 44 | # Reverse list of variants so that all derivatives are removed before the 45 | # "pre" image 46 | for variant in $(vagrant__variants | _reverse_lines); do 47 | _clean "$distro" "$version" "$variant" "$force" 48 | done 49 | else 50 | _clean "$distro" "$version" "$variant" "$force" 51 | fi 52 | } 53 | 54 | _clean() { 55 | local distro="$1" 56 | local version="$2" 57 | local variant="$3" 58 | local force="$4" 59 | 60 | local vm 61 | vm="$(vagrant__vm_variant_name "$distro" "$version" "$variant")" 62 | 63 | if [ -z "$force" ]; then 64 | _confirm "Are you sure you want to clean $vm?" 65 | fi 66 | 67 | section "Destroying vm; vm=$vm" 68 | vagrant destroy -f "$vm" 69 | } 70 | 71 | _confirm() { 72 | local msg="$1" 73 | local answer 74 | 75 | while true; do 76 | printf -- "%s [%s/%s/%s] " "$msg" "Yes" "no" "quit" 77 | read -r answer 78 | 79 | case "$answer" in 80 | q | quit | Q | Quit | QUIT) 81 | echo "Quitting" 82 | exit 0 83 | ;; 84 | y | Y | yes | Yes | YES | '') 85 | break 86 | ;; 87 | esac 88 | done 89 | } 90 | 91 | # Reverse order of lines (emulating `tac`) 92 | # Thanks to "Handy One-Liners for Awk": http://tiny.cc/daxe6y 93 | _reverse_lines() { 94 | awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' 95 | } 96 | -------------------------------------------------------------------------------- /support/lib/cmd_vagrant_console.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/vagrant.sh 5 | . "$ROOT/lib/vagrant.sh" 6 | 7 | cmd_vagrant_console__exec() { 8 | local vm="$1" 9 | shift 10 | 11 | if [ -n "$VERBOSE" ]; then 12 | set -x 13 | fi 14 | 15 | exec vagrant ssh "$vm" 16 | } 17 | -------------------------------------------------------------------------------- /support/lib/cmd_vagrant_matrix.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | # shellcheck source=support/lib/vagrant.sh 5 | . "$ROOT/lib/vagrant.sh" 6 | 7 | cmd_vagrant_matrix__for() { 8 | local distro="$1" 9 | local version="$2" 10 | local variant="$3" 11 | 12 | if [ "_" = "$distro" ]; then 13 | for distro in $(vagrant__distros); do 14 | _process_distro "$distro" "$version" "$variant" 15 | done 16 | else 17 | _process_distro "$distro" "$version" "$variant" 18 | fi 19 | } 20 | 21 | _process_distro() { 22 | local distro="$1" 23 | local version="$2" 24 | local variant="$3" 25 | 26 | if [ "_" = "$version" ]; then 27 | for version in $(vagrant__versions_for "$distro"); do 28 | _process_version "$distro" "$version" "$variant" 29 | done 30 | else 31 | _process_version "$distro" "$version" "$variant" 32 | fi 33 | } 34 | 35 | _process_version() { 36 | local distro="$1" 37 | local version="$2" 38 | local variant="$3" 39 | 40 | if [ "_" = "$variant" ]; then 41 | for variant in $(vagrant__variants); do 42 | _print "$distro" "$version" "$variant" 43 | done 44 | else 45 | _print "$distro" "$version" "$variant" 46 | fi 47 | } 48 | 49 | _print() { 50 | local distro="$1" 51 | local version="$2" 52 | local variant="$3" 53 | 54 | printf -- "%s %s %s\n" "$distro" "$version" "$variant" 55 | } 56 | -------------------------------------------------------------------------------- /support/lib/docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | docker__img_pre_name() { 5 | docker__img_variant_name "$1" "$2" "pre" 6 | } 7 | 8 | docker__img_variant_name() { 9 | local distro="$1" 10 | local version="$2" 11 | local variant="$3" 12 | 13 | echo "fnichol/workstation-${distro}-${version}-${variant}" 14 | } 15 | 16 | docker__img_pre_exists() { 17 | local distro="$1" 18 | local version="$2" 19 | local img_pre images 20 | 21 | img_pre="$(docker__img_pre_name "$distro" "$version")" 22 | 23 | images="$(docker image ls --quiet "$img_pre")" 24 | if [ -z "$images" ]; then 25 | return 1 26 | fi 27 | } 28 | 29 | docker__img_variant_exists() { 30 | local distro="$1" 31 | local version="$2" 32 | local variant="$3" 33 | local img images 34 | 35 | img="$(docker__img_variant_name "$distro" "$version" "$variant")" 36 | 37 | images="$(docker image ls --quiet "$img")" 38 | if [ -z "$images" ]; then 39 | return 1 40 | fi 41 | } 42 | 43 | docker__valid_distros() { 44 | echo "_" 45 | docker__distros 46 | } 47 | 48 | docker__distros() { 49 | find "$ROOT/dockerfiles" -type f -name 'Dockerfile.*' -exec basename {} \; \ 50 | | sed 's/^Dockerfile\.//' \ 51 | | sort 52 | } 53 | 54 | docker__valid_versions_for() { 55 | local distro="$1" 56 | 57 | echo "_" 58 | if [ "_" != "$distro" ]; then 59 | docker__versions_for "$distro" 60 | fi 61 | } 62 | 63 | docker__versions_for() { 64 | local distro="$1" 65 | 66 | cat "$ROOT/distros/docker/${distro}.txt" 67 | } 68 | 69 | docker__valid_variants() { 70 | echo "_" 71 | docker__variants 72 | } 73 | 74 | docker__variants() { 75 | echo "pre" 76 | echo "base" 77 | echo "headless" 78 | echo "graphical" 79 | } 80 | -------------------------------------------------------------------------------- /support/lib/vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # shellcheck disable=SC3043 3 | 4 | vagrant__vm_variant_name() { 5 | local distro="$1" 6 | local version="$2" 7 | local variant="$3" 8 | 9 | echo "workstation-${distro}-${version}-${variant}" 10 | } 11 | 12 | vagrant__valid_distros() { 13 | echo "_" 14 | vagrant__distros 15 | } 16 | 17 | vagrant__distros() { 18 | find "$ROOT/distros/vagrant" -type f -exec basename {} \; \ 19 | | sed 's/\.txt$//' \ 20 | | sort 21 | } 22 | 23 | vagrant__valid_versions_for() { 24 | local distro="$1" 25 | 26 | echo "_" 27 | if [ "_" != "$distro" ]; then 28 | vagrant__versions_for "$distro" 29 | fi 30 | } 31 | 32 | vagrant__versions_for() { 33 | local distro="$1" 34 | 35 | awk -F, '{ print $1 }' "$ROOT/distros/vagrant/${distro}.txt" 36 | } 37 | 38 | vagrant__valid_variants() { 39 | echo "_" 40 | vagrant__variants 41 | } 42 | 43 | vagrant__variants() { 44 | echo "base" 45 | echo "headless" 46 | echo "graphical" 47 | } 48 | -------------------------------------------------------------------------------- /vendor/mk/base.mk: -------------------------------------------------------------------------------- 1 | CHECK_TOOLS ?= 2 | TEST_TOOLS ?= 3 | 4 | all: clean build test check ## Runs clean, build, test, check 5 | .PHONY: all 6 | 7 | prepush: check test ## Runs all checks/test required before pushing 8 | @echo "--- $@" 9 | @echo "all prepush targets passed, okay to push." 10 | .PHONY: prepush 11 | 12 | checktools: ## Checks that required check tools are found on PATH 13 | @echo "--- $@" 14 | $(foreach tool, $(CHECK_TOOLS), $(if $(shell which $(tool)),, \ 15 | $(error "Required tool '$(tool)' not found on PATH"))) 16 | .PHONY: checktools 17 | 18 | testtools: ## Checks that required test tools are found on PATH 19 | @echo "--- $@" 20 | $(foreach tool, $(TEST_TOOLS), $(if $(shell which $(tool)),, \ 21 | $(error "Required tool '$(tool)' not found on PATH"))) 22 | .PHONY: testtools 23 | 24 | help: ## Prints help information 25 | @printf -- "\033[1;36;40mmake %s\033[0m\n" "$@" 26 | @echo 27 | @echo "USAGE:" 28 | @echo " make [TARGET]" 29 | @echo 30 | @echo "TARGETS:" 31 | @grep -hE '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk '\ 32 | BEGIN { FS = ":.*?## " }; \ 33 | { printf " \033[1;36;40m%-20s\033[0m %s\n", $$1, $$2 }' 34 | .PHONY: help 35 | -------------------------------------------------------------------------------- /vendor/mk/json.mk: -------------------------------------------------------------------------------- 1 | JSON_SOURCES ?= $(shell find . -type f -name '*.json' -not -path './tmp/*' -and -not -path './vendor/*') 2 | CHECK_TOOLS += jq 3 | 4 | check-json: checktools ## Checks JSON files are well formed 5 | @echo "--- $@" 6 | @for json in $(JSON_SOURCES); do echo " - $$json"; jq empty "$$json"; done 7 | .PHONY: check-json 8 | -------------------------------------------------------------------------------- /vendor/mk/libsh-vendor.mk: -------------------------------------------------------------------------------- 1 | vendor-libsh: ## Vendors updated version of libsh 2 | @echo "--- $@" 3 | curl --proto '=https' --tlsv1.2 -sSf \ 4 | https://fnichol.github.io/libsh/install.sh \ 5 | | sh -s -- --mode=vendor --release=latest 6 | .PHONY: vendor-libsh 7 | -------------------------------------------------------------------------------- /vendor/mk/shell.mk: -------------------------------------------------------------------------------- 1 | SH_SOURCES ?= $(shell find . -type f -name '*.sh' -not -path './tmp/*' -and -not -path './vendor/*') 2 | SHELL_BIN ?= $(SHELL) 3 | SHUNIT2_VERSION := 2.1.7 4 | CHECK_TOOLS += shellcheck shfmt 5 | 6 | test-shell: testtools dl-shunit2 ## Runs all shell code tests 7 | @echo "--- $@" 8 | for test in $(SH_TESTS); do \ 9 | echo; echo "Running: $$test"; $(SHELL_BIN) $$test; done 10 | .PHONY: test-shell 11 | 12 | check-shell: shellcheck shfmt ## Checks linting & styling rules for shell code 13 | .PHONY: check-shell 14 | 15 | shellcheck: checktools ## Checks shell code for linting rules 16 | @echo "--- $@" 17 | shellcheck --external-sources $(SH_SOURCES) 18 | .PHONY: shellcheck 19 | 20 | shfmt: checktools ## Checks shell code for consistent formatting 21 | @echo "--- $@" 22 | shfmt -i 2 -ci -bn -d -l $(SH_SOURCES) 23 | .PHONY: shfmt 24 | 25 | clean-shell: ## Cleans up shell project 26 | rm -rf tmp 27 | .PHONY: clean-shell 28 | 29 | dl-shunit2: tmp/shunit2 ## Downloads shUnit2 30 | .PHOHY: dl-shunit2 31 | 32 | tmp/shunit2: tmp/shunit2-$(SHUNIT2_VERSION) 33 | @echo "--- $@" 34 | ln -snf ./shunit2-$(SHUNIT2_VERSION) tmp/shunit2 35 | 36 | tmp/shunit2-$(SHUNIT2_VERSION): 37 | @echo "--- $@" 38 | mkdir -p $@ 39 | curl -sSfL https://github.com/kward/shunit2/archive/v$(SHUNIT2_VERSION).tar.gz \ 40 | | tar xzf - -C tmp/ 41 | --------------------------------------------------------------------------------