├── .gitignore ├── laptop ├── Brewfile ├── seekret-rules ├── slack.rule ├── newrelic.rule ├── aws.rule └── mandrill.rule ├── Brewfile.local ├── ISSUE_TEMPLATE.md ├── .laptop.local ├── .circleci └── config.yml ├── test ├── README.md ├── seekrets.bats └── test_helper.bash ├── CONTRIBUTING.md ├── LICENSE.md ├── .about.yml ├── seekrets-install ├── mac └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | releases 4 | -------------------------------------------------------------------------------- /laptop: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd ~ 4 | curl --remote-name https://raw.githubusercontent.com/18F/laptop/master/mac 5 | curl --remote-name https://raw.githubusercontent.com/18F/laptop/master/Brewfile 6 | /usr/bin/env bash mac 2>&1 | tee ~/laptop.log 7 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | cask_args appdir: '/Applications' 2 | 3 | brew 'git' 4 | brew 'libgit2' 5 | 6 | brew 'the_silver_searcher' 7 | 8 | tap 'homebrew/services' 9 | 10 | brew 'hub' 11 | 12 | tap 'cloudfoundry/homebrew-tap' 13 | brew 'cf-cli' 14 | 15 | cask 'chromedriver' 16 | cask 'docker' 17 | cask 'slack' 18 | 19 | -------------------------------------------------------------------------------- /seekret-rules/slack.rule: -------------------------------------------------------------------------------- 1 | api_token: 2 | match: (\"|')?(SLACK|Slack|slack)?_?(API|Api|api)?_?(TOKEN|Token|token)?_?(\"|')?\s*(:|=>|=)?\s*(\"|')?xox[a-z]-\d{11}-\d{12}-\d{12}-[a-z\d]{32}(\"|')? 3 | 4 | webhook_url: 5 | match: (\"|')?(SLACK|Slack|slack)?_?(WEBHOOK|Webhook|webhook)?_?(URL|Url|url)?_?(\"|')?\s*(:|=>|=)?\s*(\"|')?xox[a-z]-\d{11}-\d{12}-\d{12}-[a-z\d]{32}(\"|')? 6 | -------------------------------------------------------------------------------- /Brewfile.local: -------------------------------------------------------------------------------- 1 | cask 'atom' 2 | cask 'firefox' 3 | cask 'iterm2' 4 | cask 'spectacle' 5 | cask 'sublime-text' 6 | cask 'visual-studio-code' 7 | 8 | brew 'vim' 9 | brew 'ctags' 10 | brew 'tmux' 11 | brew 'reattach-to-user-namespace' 12 | 13 | brew 'go' 14 | 15 | # Install GNU coreutils and have them mask the macOS versions. See 16 | # https://github.com/18F/laptop/issues/36 for more info. 17 | # brew install coreutils --default-names -------------------------------------------------------------------------------- /seekret-rules/newrelic.rule: -------------------------------------------------------------------------------- 1 | license_key: 2 | match: (\"|')?(NEW|New|new)?_?(RELIC|Relic|relic)_?(LICENSE|License|license)?_?(KEY|Key|key)?\s*(:|=>|=)?\s*(\"|')?[0-9A-Fa-f]{40}(\"|')? 3 | unmatch: 4 | - (\"|')?github.com.*/#[A-Za-z0-9]{40}(\"|')? 5 | - (\"|')?sha(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9]{40}(\"|')? 6 | - (\"|')?revision(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9]{40}(\"|')? 7 | - (\"|')?version(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9]{40}(\"|')? 8 | - (\"|')?hash(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9]{40}(\"|')? -------------------------------------------------------------------------------- /seekret-rules/aws.rule: -------------------------------------------------------------------------------- 1 | secret_access_key: 2 | match: (\"|')?(AWS|aws|Aws)_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9/\+=]{40}(\"|')? 3 | unmatch: 4 | - .+(RELIC|Relic|relic).+ 5 | 6 | access_key_id: 7 | match: (\"|')?(AWS|aws|Aws)_?(ACCESS|access|Access)?_?(KEY|key|Key)_?(ID|id|Id)(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9]{20}(\"|')? 8 | unmatch: 9 | - .+(RELIC|Relic|relic).+ 10 | 11 | account_id: 12 | match: (\"|')?(AWS|aws|Aws)_?(ACCOUNT|account|Account)_?(ID|id|Id)?(\"|')?\s*(:|=>|=)\s*(\"|')?[0-9]{4}\-?[0-9]{4}\-?[0-9]{4}(\"|')? 13 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Hi! Thanks for using the laptop script. 2 | 3 | If you are reporting an issue with the script, please fill out the details 4 | below to help us troubleshoot the error. 5 | 6 | ### OS X version you are using 7 | 8 | ### Did you recently upgrade from a previous OS X version? 9 | 10 | ### Are you running the script on a fresh OS X installation? 11 | 12 | ### Did you have any of the following tools installed before running the script? 13 | 14 | - Xcode 15 | - Apple's command line tools 16 | - Anything the script installs (see "What it sets up" in the README) 17 | 18 | ### Copy and paste the entire contents of laptop.log, or attach it 19 | -------------------------------------------------------------------------------- /.laptop.local: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | fancy_echo "Running your customizations from ~/.laptop.local ..." 4 | 5 | # Install oh-my-zsh, a bunch of configurations for zsh that make it 6 | # a bunch more useful 7 | # sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 8 | 9 | if [ -f "$HOME/Brewfile.local" ]; then 10 | if brew bundle --file="$HOME/Brewfile.local"; then 11 | fancy_echo "All items in Brewfile.local were installed successfully." 12 | else 13 | fancy_echo "Some items in Brewfile.local were not installed successfully." 14 | fi 15 | fi 16 | 17 | append_to_file "$HOME/.zshrc" 'export PATH="$PATH:/usr/local/opt/go/libexec/bin"' 18 | -------------------------------------------------------------------------------- /seekret-rules/mandrill.rule: -------------------------------------------------------------------------------- 1 | username: 2 | match: (\"|')?(MANDRILL|mandrill)_?(\"|')?(USERNAME|username)?(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9_\-\.]+\@[A-Za-z0-9_\-\.]+(\"|')? 3 | 4 | password: 5 | match: (\"|')?(MANDRILL|mandrill)_(\"|')?(PASSWORD|password)(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9\+\!\^\%\*\-_]{8,}(\"|')? 6 | unmatch: 7 | - (\"|')?(MANDRILL|mandrill)_?(PASSWORD|password)(\"|')?\s*(:|=>|=)\s*(\"|')?\s*ENV\[\'MANDRILL_PASSWORD\'\] 8 | 9 | api_key: 10 | match: (\"|')?(MANDRILL|mandrill)_?(API|api)?_?(KEY|key|Key)(\"|')?\s*(:|=>|=)\s*(\"|')?[A-Za-z0-9\-]{20,}(\"|')? 11 | unmatch: 12 | - (\"|')?(MANDRILL|mandrill)?_?(API|api)?_?(KEY|key|Key)(\"|')?\s*(:|=>|=)\s*(\"|')?ENV\[\'MANDRILL_API_KEY\'\](\"|')? 13 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | lint: 4 | docker: 5 | - image: koalaman/shellcheck-alpine 6 | steps: 7 | - checkout 8 | - run: shellcheck mac 9 | - run: shellcheck seekrets-install 10 | 11 | test-seekrets-ubuntu: 12 | machine: yes 13 | steps: 14 | - checkout 15 | - run: | 16 | sudo apt-get update 17 | sudo apt-get install athena-jot 18 | - run: cat seekrets-install | bash - 19 | - run: bats test/seekrets.bats 20 | 21 | test-seekrets-macos: 22 | macos: 23 | xcode: "10.1.0" 24 | steps: 25 | - checkout 26 | - run: 27 | name: install bats 28 | command: brew install bats-core 29 | - run: 30 | name: install seekrets 31 | command: cat seekrets-install | bash - 32 | - run: bats test/seekrets.bats 33 | 34 | test-laptop: 35 | macos: 36 | xcode: "10.1.0" 37 | steps: 38 | - checkout 39 | - run: 40 | name: install bats 41 | command: | 42 | git clone https://github.com/bats-core/bats-core.git /tmp/bats 43 | cd /tmp/bats 44 | sudo ./install.sh /usr/local 45 | - run: bash ./mac 46 | 47 | 48 | workflows: 49 | version: 2 50 | commit: 51 | jobs: 52 | - lint 53 | - test-seekrets-ubuntu 54 | - test-seekrets-macos 55 | - test-laptop 56 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## Local BATS documentation 2 | 3 | Testing for the `git-seekrets` is performed via a testing framework called 4 | [BATS](https://github.com/bats-core/bats-core). To create an associated test 5 | for new seekrets rulesets, it is necessary to install BATS. More 6 | information on installation is located in the `seekrets.bats` script. 7 | 8 | Below is an example invocation for running the seekrets test with 9 | BATS: 10 | 11 | bats --tap seekrets.bat 12 | 13 | More information on the TAP format is available at the following 14 | address http://testanything.org/ 15 | 16 | ## Adding new tests 17 | 18 | The tests for `git-seekret` are located in `seekrets.bats`. The following would describe a test case for a matching a new secret: 19 | 20 | ```shell 21 | @test "git-seekrets finds a new type of secret" 22 | run addFileWithNewTypeOfSecret 23 | [ $status -gt 0 ] 24 | } 25 | ``` 26 | 27 | Helper and test function definitons are located in `test_helper.bash` in the test directory of this repository. An associated test function for the above example test could be structured like: 28 | 29 | ```shell 30 | addFileWithNewTypeOfSecret() { 31 | local secrets_file="${REPO_PATH}/newsecretfile.md" 32 | 33 | touch "${secrets_file}" 34 | echo "new secret: SOME_SECRET_ACCESS_INFO" >> "${secrets_file}" 35 | (cd "${REPO_PATH}" && git add "${secrets_file}") 36 | (cd ${REPO_PATH} && git commit -m 'test commit') 37 | } 38 | ``` 39 | 40 | A test case for a false positive match (with associated test function in `test_helper.bash`) would be structured as: 41 | 42 | ```shell 43 | @test "git-seekrets does not find false positives for a new type of secret" 44 | run addFileWithFalseNewTypeOfSecret 45 | [ $status -eq 0 ] 46 | } 47 | ``` 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Welcome! 2 | 3 | We're so glad you're thinking about contributing to an 18F open source project! 4 | If you're unsure or afraid of anything, just ask or submit the issue or pull 5 | request anyways. The worst that can happen is that you'll be politely asked to 6 | change something. We appreciate any sort of contribution, and don't want a 7 | wall of rules to get in the way of that. 8 | 9 | Before contributing, we encourage you to read our CONTRIBUTING policy 10 | (you are here), our LICENSE, and our README, all of which should be in this 11 | repository. If you have any questions, or want to read more about our 12 | underlying policies, you can consult the 18F Open Source Policy GitHub 13 | repository at https://github.com/18f/open-source-policy, or just shoot us an 14 | email/official government letterhead note to [18f@gsa.gov](mailto:18f@gsa.gov). 15 | 16 | ## Contributing to the laptop script 17 | 18 | Edit the `mac` file. 19 | If you added or removed any tools, document your changes in the `README.md` 20 | file. 21 | Follow shell style guidelines by using [ShellCheck]. 22 | 23 | ```sh 24 | # Install shellcheck 25 | brew install shellcheck 26 | 27 | # Run shellcheck from the root of the repo 28 | shellcheck mac 29 | ``` 30 | 31 | [ShellCheck]: http://www.shellcheck.net/about.html 32 | 33 | ## Public domain 34 | 35 | This project is in the public domain within the United States, and 36 | copyright and related rights in the work worldwide are waived through 37 | the [CC0 1.0 Universal public domain dedication][CC0]. 38 | 39 | [CC0]: https://creativecommons.org/publicdomain/zero/1.0/ 40 | 41 | All contributions to this project will be released under the CC0 42 | dedication. By submitting a pull request, you are agreeing to comply 43 | with this waiver of copyright interest. 44 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This 18F project is originally based on [thoughtbot's laptop project](https://github.com/thoughtbot/laptop). 2 | 3 | 4 | ## thoughtbot's work 5 | 6 | [thoughtbot's laptop project](https://github.com/thoughtbot/laptop), and code we retain here from it, is released under the MIT License: 7 | 8 | ``` 9 | Copyright (c) 2011-2014 thoughtbot, inc. 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | ``` 29 | 30 | ## Public domain 31 | 32 | 18F's contributions to this project are covered by the following: 33 | 34 | As a work of the United States Government, this project is in the 35 | public domain within the United States. 36 | 37 | Additionally, we waive copyright and related rights in the work 38 | worldwide through the CC0 1.0 Universal public domain dedication. 39 | 40 | ## CC0 1.0 Universal Summary 41 | 42 | This is a human-readable summary of the 43 | [Legal Code (read the full text)][code]. 44 | 45 | [code]: https://creativecommons.org/publicdomain/zero/1.0/legalcode 46 | 47 | ### No Copyright 48 | 49 | The person who associated a work with this deed has dedicated the work to 50 | the public domain by waiving all of his or her rights to the work worldwide 51 | under copyright law, including all related and neighboring rights, to the 52 | extent allowed by law. 53 | 54 | You can copy, modify, distribute and perform the work, even for commercial 55 | purposes, all without asking permission. 56 | 57 | ### Other Information 58 | 59 | In no way are the patent or trademark rights of any person affected by CC0, 60 | nor are the rights that other persons may have in the work or in how the 61 | work is used, such as publicity or privacy rights. 62 | 63 | Unless expressly stated otherwise, the person who associated a work with 64 | this deed makes no warranties about the work, and disclaims liability for 65 | all uses of the work, to the fullest extent permitted by applicable law. 66 | When using or citing the work, you should not imply endorsement by the 67 | author or the affirmer. 68 | -------------------------------------------------------------------------------- /.about.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # .about.yml project metadata 3 | # 4 | # Short name that acts as the project identifier (required) 5 | name: laptop 6 | 7 | # Full proper name of the project (required) 8 | full_name: Laptop Script 9 | 10 | # The type of content in the repo 11 | # values: app, docs, policy 12 | type: app 13 | 14 | # Describes whether a project team, working group/guild, etc. owns the repo (required) 15 | # values: guild, working-group, project 16 | owner_type: project 17 | 18 | # Name of the main project repo if this is a sub-repo; name of the working group/guild repo if this is a working group/guild subproject 19 | # parent: 20 | 21 | # Maturity stage of the project (required) 22 | # values: discovery, alpha, beta, live, sunset, transfer, end 23 | stage: live 24 | 25 | # Description of the project 26 | description: > 27 | A shell script that turns your Mac into an awesome web development 28 | machine. 29 | 30 | # Tags that describe the project or aspects of the project 31 | tags: 32 | - automation 33 | 34 | # Should be 'true' if the project has a continuous build (required) 35 | # values: true, false 36 | testable: true 37 | 38 | # Team members contributing to the project (required) 39 | # Items: 40 | # - github: GitHub user name 41 | # id: Internal team identifier/user name 42 | # role: Team member's role; leads should be designated as 'lead' 43 | team: 44 | - github: monfresh 45 | id: Moncef Belyamani 46 | role: lead 47 | - github: afeld 48 | id: Aidan Feldman 49 | role: developer 50 | 51 | # Partners for whom the project is developed 52 | # partners: 53 | # - 54 | 55 | # Organizations or individuals who have adopted the project for their own use 56 | # Items: 57 | # - id: The name of the organization or individual 58 | # url: A URL to the user's version of the project 59 | # users: 60 | # - 61 | 62 | # Brief descriptions of significant project developments 63 | # milestones: 64 | # - 65 | 66 | # Technologies used to build the project 67 | stack: 68 | - Bash 69 | 70 | # Brief description of the project's outcomes 71 | impact: > 72 | It allows new and current employees to easily set up their laptop with 73 | all the tools needed to be productive on day one. 74 | 75 | # Services used to supply project status information 76 | # Items: 77 | # - name: Name of the service 78 | # category: Type of the service 79 | # url: URL for detailed information 80 | # badge: URL for the status badge 81 | services: 82 | - name: Circle CI 83 | category: Continous integration 84 | url: https://circleci.com/gh/18F/laptop 85 | badge: https://circleci.com/gh/18F/laptop.svg 86 | 87 | # Licenses that apply to the project and/or its components (required) 88 | # Items by property name pattern: 89 | # .*: 90 | # name: Name of the license from the Software Package Data Exchange (SPDX): https://spdx.org/licenses/ 91 | # url: URL for the text of the license 92 | licenses: 93 | thoughtbot-laptop: 94 | name: MIT 95 | url: https://github.com/thoughtbot/laptop/blob/master/LICENSE 96 | 18F-laptop: 97 | name: CC0 98 | url: https://github.com/18F/laptop/blob/master/LICENSE.md 99 | 100 | # Blogs or websites associated with project development 101 | # blog: 102 | # - 103 | 104 | # Links to project artifacts 105 | # Items: 106 | # - url: URL for the link 107 | # text: Anchor text for the link 108 | # links: 109 | # - 110 | 111 | # URIs for points-of-contact 112 | # Items: 113 | # - url: URI for the link to 114 | # text: Anchor text for the link 115 | # contact: 116 | # - 117 | -------------------------------------------------------------------------------- /test/seekrets.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | # 3 | # bats test file for testing git seekrets and 4 | # seekrets rulesets 5 | # 6 | # Prerequisites: 7 | # * git seekrets is installed (cf. seekrets-install) 8 | # 9 | # Installation: 10 | # * Use the laptop script via ~/.laptop.local 11 | # 12 | # echo 'bats' >> ~/.laptop.local 13 | # 14 | # * homebrew method 15 | # 16 | # brew install bats-core 17 | # 18 | # Running Tests: 19 | # 20 | # bats seekrets.bat 21 | 22 | load test_helper 23 | 24 | @test "no args gives usage instructions" { 25 | run git-seekret 26 | [ $status -eq 0 ] 27 | [ $(expr "${lines[2]}" : "USAGE:") -ne 0 ] 28 | } 29 | 30 | @test "option --version prints version number" { 31 | run git-seekret --version 32 | [ $status -eq 0 ] 33 | [ $(expr "$output" : "git-seekret version [0-9][0-9.]*") -ne 0 ] 34 | } 35 | 36 | @test "config command with no options shows config" { 37 | run git-seekret rules 38 | [ $status -eq 0 ] 39 | [ $(expr "${lines[0]}" : "List of rules:") -ne 0 ] 40 | } 41 | 42 | @test "rules command with no options gives a listing of rules" { 43 | run git-seekret rules 44 | [ $status -eq 0 ] 45 | [ $(expr "${lines[0]}" : "List of rules:") -ne 0 ] 46 | } 47 | 48 | @test "git-seekrets does find aws secrets in test repo" { 49 | run addFileWithAwsSecrets 50 | [ $(echo "$output" | grep -c 'Found Secrets: 2') -eq 1 ] 51 | } 52 | 53 | @test "git-seekrets does find aws accounts in test repo" { 54 | run addFileWithAwsAccounts 55 | [ $(echo "$output" | grep -c 'Found Secrets: 1') -eq 1 ] 56 | } 57 | 58 | @test "git-seekrets does find aws access keys in test repo" { 59 | run addFileWithAwsAccessKey 60 | [ $(echo "$output" | grep -c 'Found Secrets: 2') -eq 1 ] 61 | } 62 | 63 | @test "git-seekrets does not find newrelic keys as aws keys in test repo" { 64 | enableOnlyRuleFile "aws" 65 | run addFileWithNewrelicSecrets 66 | [ $(echo "$output" | grep -c 'Found Secrets: 0') -eq 1 ] 67 | } 68 | 69 | @test "git-seekrets does find newrelic secrets in test repo" { 70 | run addFileWithNewrelicSecrets 71 | [ $(echo "$output" | grep -c 'Found Secrets: 1') -eq 1 ] 72 | } 73 | 74 | @test "git-seekrets does not find newrelic false positives in test repo" { 75 | run addFileWithFalseNewrelicSecrets 76 | [ $(echo "$output" | grep -c 'Found Secrets: 0') -eq 1 ] 77 | } 78 | 79 | @test "git-seekrets only matches newrelic secrets in test repo" { 80 | run addFileWithSomeNewrelicSecrets 81 | [ $(echo "$output" | grep -c 'Found Secrets: 1') -eq 1 ] 82 | } 83 | 84 | @test "git-seekrets does find mandrill keys in test repo" { 85 | run addFileWithMandrillKey 86 | [ $(echo "$output" | grep -c 'Found Secrets: 1') -eq 1 ] 87 | } 88 | 89 | @test "git-seekrets does not find mandrill key false positives in test repo" { 90 | run addFileWithFalseMandrillKey 91 | [ $(echo "$output" | grep -c 'Found Secrets: 0') -eq 1 ] 92 | } 93 | 94 | @test "git-seekrets does find mandrill passwords in test repo" { 95 | run addFileWithMandrillPassword 96 | [ $(echo "$output" | grep -c 'Found Secrets: 2') -eq 1 ] 97 | } 98 | 99 | @test "git-seekrets does not find mandrill password false positives in test repo" { 100 | run addFileWithFalseMandrillPassword 101 | [ $(echo "$output" | grep -c 'Found Secrets: 0') -eq 1 ] 102 | } 103 | 104 | @test "git-seekrets does find mandrill usernames in test repo" { 105 | run addFileWithMandrillUsername 106 | [ $(echo "$output" | grep -c 'Found Secrets: 1') -eq 1 ] 107 | } 108 | 109 | @test "git-seekrets does find slack api token in test repo" { 110 | run addFileWithSlackAPIToken 111 | [ $(echo "$output" | grep -c 'Found Secrets: 1') -eq 1 ] 112 | } 113 | 114 | @test "git-seekrets does not find secrets in test repo" { 115 | run addFileWithNoSecrets 116 | [ $(echo "$output" | grep -c 'Found Secrets: 0') -eq 1 ] 117 | } 118 | 119 | @test "git-seekrets can disable all rulesets" { 120 | run git-seekret rules --disable-all 121 | [ $status -eq 0 ] 122 | [ $(echo "$output" | grep -c '\[x\]') -eq 0 ] 123 | } 124 | 125 | @test "git-seekrets can enable all rulesets" { 126 | run git-seekret rules --enable-all 127 | [ $status -eq 0 ] 128 | [ $(echo "$output" | grep -c '\[x\]') -gt 0 ] 129 | } 130 | -------------------------------------------------------------------------------- /seekrets-install: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTPATH=$( cd "$(dirname "$0")" ; pwd -P ) 4 | 5 | fancy_echo() { 6 | # shellcheck disable=SC2039 7 | local fmt="$1"; shift 8 | 9 | # shellcheck disable=SC2059 10 | printf "\n$fmt\n" "$@" 11 | } 12 | 13 | toolminversion() { 14 | # shellcheck disable=SC2039 15 | local prog="$1" value="$2" version 16 | 17 | version=$($prog --version | awk '{print $NF; exit}') 18 | 19 | awk -v v1="$version" -v v2="$value" 'BEGIN { 20 | split(v1, a, /\./); split(v2, b, /\./); 21 | 22 | if (a[1] < b[1]) { 23 | # major version is lower 24 | exit 1; 25 | } 26 | if (a[1] > b[1]) { 27 | # major version is higher 28 | exit 0; 29 | } 30 | 31 | # major versions match 32 | 33 | if (a[2] < b[2]) { 34 | # minor version is lower 35 | exit 2; 36 | } 37 | if (a[2] > b[2]) { 38 | # minor version is higher 39 | exit 0; 40 | } 41 | 42 | # minor versions match 43 | 44 | if(a[3] < b[3]) { 45 | #patch version is lower 46 | exit 3; 47 | } 48 | 49 | # patch version is higher or matches 50 | exit 0; 51 | }' 52 | } 53 | 54 | get_platform() { 55 | case $OSTYPE in 56 | linux*) 57 | PLATFORM=linux 58 | ;; 59 | darwin*) 60 | PLATFORM=osx 61 | ;; 62 | *) 63 | echo "unknown platform: $OSTYPE" 64 | exit 1 65 | ;; 66 | esac 67 | } 68 | 69 | set -e 70 | 71 | if toolminversion git 2.9.1; then 72 | fancy_echo "Installing Seekret" 73 | WORK_DIR=$( mktemp -d -t "$( basename "$(pwd)XXXXXX" )" ) 74 | pushd "${WORK_DIR}" > /dev/null 75 | git init > /dev/null 76 | get_platform 77 | LATEST_RELEASE_URL=$(curl -s https://github.com/18F/git-seekret/releases | grep "18F/git-seekret/releases/download" | grep ${PLATFORM} | head -n 1 | cut -d '"' -f 2) 78 | BIN_LOCATION="/usr/local/bin" 79 | SUDO_REQUIRED=true 80 | if [[ -d "$HOME/bin" && ":$PATH:" == *":$HOME/bin:"* ]]; then 81 | BIN_LOCATION="$HOME/bin" 82 | SUDO_REQUIRED=false 83 | fi 84 | INSTALL_CMD="curl -L -# \"https://github.com${LATEST_RELEASE_URL}\" -o \"${BIN_LOCATION}/git-seekret\" > /dev/null && chmod a+x \"${BIN_LOCATION}/git-seekret\"" 85 | echo "Installing in $BIN_LOCATION" 86 | if [ "$SUDO_REQUIRED" = false ]; then 87 | eval "$INSTALL_CMD" 88 | else 89 | sudo sh -c "$INSTALL_CMD" 90 | fi 91 | 92 | fancy_echo "Downloading Seekret rules" 93 | export SEEKRET_RULES_PATH="${HOME}/.git-support/seekret-rules" 94 | mkdir -p "${SEEKRET_RULES_PATH}" 95 | # Download rules 96 | SEEKRET_RULES_URL="https://raw.githubusercontent.com/18F/laptop/master/seekret-rules" 97 | # get list of rules from Github API; pull out just download_url; then get just the filename 98 | SEEKRET_RULES=$(curl -s https://api.github.com/repos/18F/laptop/contents/seekret-rules | grep download_url | sed -e 's/.*: "\([^"]*\)".*/\1/' | sed -e 's/.*\/seekret-rules\/\(.*\)/\1/') 99 | 100 | # if rules are local, use those instead 101 | if [ -d "${SCRIPTPATH}/seekret-rules" ]; then 102 | SEEKRET_RULES=$( ls "${SCRIPTPATH}/seekret-rules" ) 103 | fi 104 | 105 | for rule in ${SEEKRET_RULES} 106 | do 107 | printf "\t%s: " "$rule" 108 | if [ -d "${SCRIPTPATH}/seekret-rules" ]; then 109 | cp "${SCRIPTPATH}/seekret-rules/${rule}" "${SEEKRET_RULES_PATH}" 110 | else 111 | (cd "${SEEKRET_RULES_PATH}" \ 112 | && curl -# -o "${rule}" "${SEEKRET_RULES_URL}/${rule}" > /dev/null) 113 | fi 114 | printf "done\n" 115 | done 116 | 117 | fancy_echo "Configuring Seekret" 118 | mkdir -p "${HOME}/.git-support/hooks" 119 | git config --global core.hooksPath "${HOME}/.git-support/hooks" 120 | git seekret --global config --init 121 | # Activate all installed rules 122 | git seekret --global rules --enable-all 123 | git seekret --global hook --enable-all 124 | 125 | popd > /dev/null 126 | rm -rf "${WORK_DIR}" 127 | fancy_echo "Finished installing Seekret" 128 | else 129 | echo 130 | echo "Looks like you need to install git 2.9.1 or better." 131 | echo 132 | echo "If you are using git from the command line, this should" 133 | echo "be as easy as upgrading with homebrew:" 134 | echo " $ brew update && brew upgrade git" 135 | echo 136 | echo "If you are using GitHub Desktop, please upgrade to version" 137 | echo "221 or better for OS X. Download: https://desktop.github.com/" 138 | echo 139 | exit 1; 140 | fi 141 | -------------------------------------------------------------------------------- /test/test_helper.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | REPO_PATH=$(mktemp -d "${BATS_TMPDIR}/gittest.XXXXXX") 4 | LOCAL_SEEKRETS="$(dirname $BATS_TEST_DIRNAME)/seekret-rules" 5 | 6 | if ! command -v jot > /dev/null; then 7 | # jot should be available already on macos. For ubuntu, no. 8 | ( 9 | echo "jot is not installed. Try installing it." >&2 10 | echo "" 11 | echo " $ apt-get install athena-jot" >&2 12 | echo "" 13 | ) >&2 14 | 15 | # Not sure if exit is right here, but we definitely want to fail the build if 16 | # the command is not installed. Otherwise tests will fail without any hints 17 | # as to what the issue is. 18 | exit 1 19 | fi 20 | 21 | setupGitRepo() { 22 | git init "${REPO_PATH}" 23 | git config --local gitseekret.rulespath "$LOCAL_SEEKRETS" 24 | git config --global gitseekret.rulespath "$LOCAL_SEEKRETS" 25 | (cd "${REPO_PATH}" && \ 26 | git config user.email "example@example.com" && \ 27 | git config user.name "Example User" && \ 28 | git-seekret rules --enable-all) 29 | } 30 | 31 | cleanGitRepo() { 32 | (cd "${REPO_PATH}" && git reset --hard) 33 | rm -fr "${REPO_PATH}" 34 | } 35 | 36 | enableOnlyRuleFile() { 37 | local rule_file="$1" 38 | local rule_file_rules=$(cd "${REPO_PATH}" && git-seekret rules | grep "$1\." | awk ' { print $2 } ') 39 | 40 | (cd "${REPO_PATH}" && git-seekret rules --disable-all) 41 | for rf in $rule_file_rules; do 42 | (cd "${REPO_PATH}" && git-seekret rules --enable "${rf}") 43 | done 44 | } 45 | 46 | addFalseMandrillKey() { 47 | local secrets_file="$1" 48 | 49 | touch "${secrets_file}" 50 | echo "Fake keys in this file" >> "${secrets_file}" 51 | echo "MANDRILL_API_KEY => ENV['MANDRILL_API_KEY']" >> "${secrets_file}" 52 | echo "mandrill_api_key: ENV['MANDRILL_API_KEY']" >> "${secrets_file}" 53 | } 54 | 55 | addFalseMandrillPassword() { 56 | local secrets_file="$1" 57 | 58 | touch "${secrets_file}" 59 | echo "Fake passwords in this file" >> "${secrets_file}" 60 | echo "MANDRILL_PASSWORD: ENV['MANDRILL_PASSWORD']" >> "${secrets_file}" 61 | echo "MANDRILL_PASSWORD: ''" >> "${secrets_file}" 62 | echo "address\":smtp.mandrillaapp.compassword:1234" >> "${secrets_file}" 63 | } 64 | 65 | addFalseNewrelicSecrets() { 66 | local secrets_file="$1" 67 | 68 | touch "${secrets_file}" 69 | echo "False secrets in this file" >> "${secrets_file}" 70 | echo "github.com/18f/#$(seq -s '' 2 1 25)" >> "${secrets_file}" 71 | echo "sha = $(seq -s '' 4 1 26)" >> "${secrets_file}" 72 | echo "revision: $(seq -s '' 6 1 27)" >> "${secrets_file}" 73 | } 74 | 75 | addMandrillKey() { 76 | local secrets_file="$1" 77 | 78 | touch "${secrets_file}" 79 | echo "Keys in this file" >> "${secrets_file}" 80 | echo "mandrill_api_key: p$(jot -s '' -w p%c 11 c)" >> "${secrets_file}" 81 | } 82 | 83 | addMandrillPassword() { 84 | local secrets_file="$1" 85 | 86 | touch "${secrets_file}" 87 | echo "Secrets in this file" >> "${secrets_file}" 88 | echo "MANDRILL_PASSWORD: pa+$(jot -s '' -w K%c 5 c)-$(jot -s '' -w K%c 5 c)%" >> "${secrets_file}" 89 | printf "export MANDRILL_PASSWORD" >> "${secrets_file}" 90 | echo "='TNT42__H1_18fZbbT+c41A'" >> "${secrets_file}" 91 | } 92 | 93 | addMandrillUsername() { 94 | local secrets_file="$1" 95 | 96 | touch "${secrets_file}" 97 | echo "Secrets in this file" >> "${secrets_file}" 98 | printf "MANDRILL_USERNAME" >> "${secrets_file}" 99 | echo ": notauser@example.com" >> "${secrets_file}" 100 | } 101 | 102 | addNewrelicSecrets() { 103 | local secrets_file="$1" 104 | 105 | touch "${secrets_file}" 106 | echo "Secrets in this file" >> "${secrets_file}" 107 | printf "NEW_RELIC_LICENSE_KEY" >> "${secrets_file}" 108 | echo ": 6333476647d1758c8fe524655342a3c561ff45ac" >> "${secrets_file}" 109 | } 110 | 111 | addFileWithNoSecrets() { 112 | local filename="${REPO_PATH}/plainfile.md" 113 | 114 | touch "${filename}" 115 | echo "Just a plain old file" >> "${filename}" 116 | (cd "${REPO_PATH}" && git add "${filename}") 117 | (cd ${REPO_PATH} && git commit -m 'test commit') 118 | } 119 | 120 | addFileWithAwsSecrets() { 121 | local secrets_file="${REPO_PATH}/secretsfile.md" 122 | 123 | touch "${secrets_file}" 124 | echo "SHHHH... Secrets in this file" >> "${secrets_file}" 125 | echo "aws_secret_key: $(seq -s '' 8 1 28)" >> "${secrets_file}" 126 | printf "AWS_SECRET_ACCESS_KEY" >> "${secrets_file}" 127 | echo ": fhhlE5DalovdNsSVY3Zu0D/oi9RZF5gvEt6h7ofU" >> "${secrets_file}" 128 | printf "secretKey" >> "${secrets_file}" 129 | echo ": fqhl4EDajovdNsSVV3Zu0F/oi9RZF5qvEt6h7bfU" >> "${secrets_file}" 130 | printf "key_id" >> "${secrets_file}" 131 | echo ": AKIALLVCLLYFEWP5MWXA" >> "${secrets_file}" 132 | printf "secret_key" >> "${secrets_file}" 133 | echo ": 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" >> "${secrets_file}" 134 | (cd "${REPO_PATH}" && git add "${secrets_file}") 135 | (cd ${REPO_PATH} && git commit -m 'test commit') 136 | } 137 | 138 | addFileWithAwsAccounts() { 139 | local secrets_file="${REPO_PATH}/awsaccountsfile.md" 140 | 141 | touch "${secrets_file}" 142 | echo "SHHHH... Secrets in this file" >> "${secrets_file}" 143 | echo "aws_account_id=$(jot -s '-' 4 1024 5096)" >> "${secrets_file}" 144 | (cd "${REPO_PATH}" && git add "${secrets_file}") 145 | (cd ${REPO_PATH} && git commit -m 'test commit') 146 | } 147 | 148 | addFileWithAwsAccessKey() { 149 | local secrets_file="${REPO_PATH}/secretsfile.md" 150 | 151 | touch "${secrets_file}" 152 | echo "SHHHH... Secrets in this file" >> "${secrets_file}" 153 | echo "aws_key: $(seq -s '' 8 1 28)" >> "${secrets_file}" 154 | printf "AWS_ACCESS_KEY_ID" >> "${secrets_file}" 155 | echo ": AKIAJLLCKKYFEWP5MWXA " >> "${secrets_file}" 156 | (cd "${REPO_PATH}" && git add "${secrets_file}") 157 | (cd ${REPO_PATH} && git commit -m 'test commit') 158 | } 159 | 160 | addFileWithNewrelicSecrets() { 161 | local secrets_file="${REPO_PATH}/secretsfile.md" 162 | 163 | touch "${secrets_file}" 164 | addNewrelicSecrets "${secrets_file}" 165 | (cd "${REPO_PATH}" && git add "${secrets_file}") 166 | (cd ${REPO_PATH} && git commit -m 'test commit') 167 | } 168 | 169 | addFileWithSomeNewrelicSecrets() { 170 | local secrets_file="${REPO_PATH}/somesecretsfile.md" 171 | 172 | touch "${secrets_file}" 173 | addFalseNewrelicSecrets "${secrets_file}" 174 | addNewrelicSecrets "${secrets_file}" 175 | (cd "${REPO_PATH}" && git add "${secrets_file}") 176 | (cd ${REPO_PATH} && git commit -m 'test commit') 177 | } 178 | 179 | addFileWithFalseNewrelicSecrets() { 180 | local secrets_file="${REPO_PATH}/secretsfile.md" 181 | 182 | touch "${secrets_file}" 183 | addFalseNewrelicSecrets "${secrets_file}" 184 | (cd "${REPO_PATH}" && git add "${secrets_file}") 185 | (cd ${REPO_PATH} && git commit -m 'test commit') 186 | } 187 | 188 | addFileWithFalseMandrillKey() { 189 | local secrets_file="${REPO_PATH}/secretsfile.md" 190 | 191 | touch "${secrets_file}" 192 | addFalseMandrillKey "${secrets_file}" 193 | (cd "${REPO_PATH}" && git add "${secrets_file}") 194 | (cd ${REPO_PATH} && git commit -m 'test commit') 195 | } 196 | 197 | addFileWithFalseMandrillPassword() { 198 | local secrets_file="${REPO_PATH}/secretsfile.md" 199 | 200 | touch "${secrets_file}" 201 | addFalseMandrillPassword "${secrets_file}" 202 | (cd "${REPO_PATH}" && git add "${secrets_file}") 203 | (cd ${REPO_PATH} && git commit -m 'test commit') 204 | } 205 | 206 | addFileWithMandrillKey() { 207 | local secrets_file="${REPO_PATH}/secretsfile.md" 208 | 209 | touch "${secrets_file}" 210 | addMandrillKey "${secrets_file}" 211 | (cd "${REPO_PATH}" && git add "${secrets_file}") 212 | (cd ${REPO_PATH} && git commit -m 'test commit') 213 | } 214 | 215 | addFileWithMandrillPassword() { 216 | local secrets_file="${REPO_PATH}/secretsfile.md" 217 | 218 | touch "${secrets_file}" 219 | addMandrillPassword "${secrets_file}" 220 | (cd "${REPO_PATH}" && git add "${secrets_file}") 221 | (cd ${REPO_PATH} && git commit -m 'test commit') 222 | } 223 | 224 | addFileWithMandrillUsername() { 225 | local secrets_file="${REPO_PATH}/secretsfile.md" 226 | 227 | touch "${secrets_file}" 228 | addMandrillUsername "${secrets_file}" 229 | (cd "${REPO_PATH}" && git add "${secrets_file}") 230 | (cd ${REPO_PATH} && git commit -m 'test commit') 231 | } 232 | 233 | addFileWithSlackAPIToken() { 234 | local secrets_file="${REPO_PATH}/slacktokenfile.md" 235 | 236 | touch "${secrets_file}" 237 | echo "SHHHH... Secrets in this file" >> "${secrets_file}" 238 | echo "slack_api_token=xoxo-11111111111-222222222222-333333333333-0123456789abcdef0123456789abcdef" >> "${secrets_file}" 239 | (cd "${REPO_PATH}" && git add "${secrets_file}") 240 | (cd ${REPO_PATH} && git commit -m 'test commit') 241 | } 242 | 243 | setup() { 244 | setupGitRepo 245 | } 246 | 247 | teardown() { 248 | cleanGitRepo 249 | } 250 | -------------------------------------------------------------------------------- /mac: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Welcome to the 18F laptop script! 4 | # Be prepared to turn your laptop (or desktop) 5 | # into an awesome development machine. 6 | 7 | fancy_echo() { 8 | # shellcheck disable=SC2039 9 | local fmt="$1"; shift 10 | 11 | # shellcheck disable=SC2059 12 | printf "\n$fmt\n" "$@" 13 | } 14 | 15 | append_to_file() { 16 | # shellcheck disable=SC2039 17 | local file="$1" 18 | # shellcheck disable=SC2039 19 | local text="$2" 20 | 21 | if [ "$file" = "$HOME/.zshrc" ]; then 22 | if [ -w "$HOME/.zshrc.local" ]; then 23 | file="$HOME/.zshrc.local" 24 | else 25 | file="$HOME/.zshrc" 26 | fi 27 | fi 28 | 29 | if ! grep -qs "^$text$" "$file"; then 30 | printf "\n%s\n" "$text" >> "$file" 31 | fi 32 | } 33 | 34 | append_to_shell_file() { 35 | append_to_file "$shell_file" "$1" 36 | } 37 | 38 | create_and_set_shell_file() { 39 | shell_file="$1" 40 | if [ ! -f "$shell_file" ]; then 41 | touch "$shell_file" 42 | fi 43 | } 44 | 45 | create_zshrc_and_set_it_as_shell_file() { 46 | create_and_set_shell_file "$HOME/.zshrc" 47 | } 48 | 49 | create_fishconfig_and_set_it_as_shell_file() { 50 | create_and_set_shell_file "$HOME/.config/fish/config.fish" 51 | } 52 | 53 | create_bash_profile_and_set_it_as_shell_file() { 54 | create_and_set_shell_file "$HOME/.bash_profile" 55 | } 56 | 57 | # shellcheck disable=SC2154 58 | trap 'ret=$?; test $ret -ne 0 && printf "failed\n\n" >&2; exit $ret' EXIT 59 | 60 | set -e 61 | 62 | if [ ! -d "$HOME/.bin/" ]; then 63 | mkdir "$HOME/.bin" 64 | fi 65 | 66 | case "$SHELL" in 67 | */fish) : 68 | create_fishconfig_and_set_it_as_shell_file 69 | ;; 70 | */zsh) : 71 | create_zshrc_and_set_it_as_shell_file 72 | ;; 73 | *) 74 | create_bash_profile_and_set_it_as_shell_file 75 | if [ -z "$CI" ]; then 76 | bold=$(tput bold) 77 | normal=$(tput sgr0) 78 | echo "Want to switch your shell from the default ${bold}bash${normal} to ${bold}zsh${normal}?" 79 | echo "Both work fine for development, and ${bold}zsh${normal} has some extra " 80 | echo "features for customization and tab completion." 81 | echo "If you aren't sure or don't care, we recommend ${bold}zsh${normal}." 82 | echo "Note that you can always switch back to ${bold}bash${normal} if you change your mind." 83 | echo "Please see the README for instructions." 84 | echo -n "Press ${bold}y${normal} to switch to zsh, ${bold}n${normal} to keep bash: " 85 | read -r -n 1 response 86 | if [ "$response" = "y" ]; then 87 | create_zshrc_and_set_it_as_shell_file 88 | if grep "$(command -v zsh)" > /dev/null 2>&1 < /etc/shells; then 89 | fancy_echo "=== Getting ready to change your shell to zsh. Please enter your password to continue. ===" 90 | echo "=== Note that there won't be visual feedback when you type your password. Type it slowly and press return. ===" 91 | echo "=== Press control-c to cancel ===" 92 | chsh -s "$(command -v zsh)" 93 | else 94 | printf "\n\n" 95 | echo "Can't switch shells automatically in this case. The path to zsh isn't in" 96 | echo "the list of allowed shells. To manually switch to zsh, enter the following" 97 | echo "two lines into your terminal (in another tab, or when this script is done):" 98 | echo "" 99 | echo "sudo echo \"\$(command -v zh)\" >> /etc/shells" 100 | echo "chsh -s \"\$(command -v zs)\"" 101 | sleep 3 102 | fi 103 | else 104 | fancy_echo "Shell will not be changed." 105 | fi 106 | else 107 | fancy_echo "CI System detected, will not change shells" 108 | fi 109 | ;; 110 | esac 111 | 112 | brew_is_installed() { 113 | brew list -1 | grep -Fqx "$1" 114 | } 115 | 116 | tap_is_installed() { 117 | brew tap | grep -Fqx "$1" 118 | } 119 | 120 | gem_install_or_update() { 121 | if gem list "$1" | grep "^$1 ("; then 122 | fancy_echo "Updating %s ..." "$1" 123 | gem update "$@" 124 | else 125 | fancy_echo "Installing %s ..." "$1" 126 | gem install "$@" 127 | fi 128 | } 129 | 130 | latest_installed_ruby() { 131 | find "$HOME/.rubies" -maxdepth 1 -name 'ruby-*' | tail -n1 | grep -E -o '\d+\.\d+\.\d+' 132 | } 133 | 134 | switch_to_latest_ruby() { 135 | # shellcheck disable=SC1091 136 | . /usr/local/share/chruby/chruby.sh 137 | chruby "ruby-$(latest_installed_ruby)" 138 | } 139 | 140 | 141 | laptop_script_url="https://raw.githubusercontent.com/18F/laptop/master/laptop" 142 | case "$SHELL" in 143 | */fish) : 144 | append_to_shell_file "alias laptop='bash (curl -s $laptop_script_url|psub)'" 145 | # shellcheck disable=SC2016 146 | append_to_shell_file 'set -xg PATH $HOME/.bin $PATH' 147 | ;; 148 | *) : 149 | append_to_shell_file "alias laptop='bash <(curl -s $laptop_script_url)'" 150 | # shellcheck disable=SC2016 151 | append_to_shell_file 'export PATH="$HOME/.bin:$PATH"' 152 | ;; 153 | esac 154 | 155 | if ! command -v brew >/dev/null; then 156 | fancy_echo "Installing Homebrew ..." 157 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" 158 | 159 | case "$SHELL" in 160 | */fish) : 161 | # noop, fish ships with /usr/local/bin in a good spot in the path 162 | ;; 163 | *) : 164 | # shellcheck disable=SC2016 165 | append_to_shell_file 'export PATH="/usr/local/bin:$PATH"' 166 | ;; 167 | esac 168 | else 169 | fancy_echo "Homebrew already installed. Skipping ..." 170 | fi 171 | 172 | # Remove brew-cask since it is now installed as part of brew tap caskroom/cask. 173 | # See https://github.com/caskroom/homebrew-cask/releases/tag/v0.60.0 174 | if brew_is_installed 'brew-cask'; then 175 | brew uninstall --force 'brew-cask' 176 | fi 177 | 178 | if tap_is_installed 'caskroom/versions'; then 179 | brew untap caskroom/versions 180 | fi 181 | 182 | fancy_echo "Updating Homebrew..." 183 | brew update 184 | 185 | fancy_echo "Verifying the Homebrew installation..." 186 | if brew doctor; then 187 | fancy_echo "Your Homebrew installation is good to go." 188 | else 189 | fancy_echo "Your Homebrew installation reported some errors or warnings." 190 | echo "If the warnings are related to Python, you can ignore them." 191 | echo "Otherwise, review the Homebrew messages to see if any action is needed." 192 | fi 193 | 194 | if brew_is_installed 'cloudfoundry-cli'; then 195 | brew uninstall --force cloudfoundry-cli 196 | fi 197 | 198 | fancy_echo "Installing formulas and casks from the Brewfile ..." 199 | if brew bundle --file="$HOME/Brewfile"; then 200 | fancy_echo "All formulas and casks were installed successfully." 201 | else 202 | fancy_echo "Some formulas or casks failed to install." 203 | echo "This is usually due to one of the Mac apps being already installed," 204 | echo "in which case, you can ignore these errors." 205 | fi 206 | 207 | case "$SHELL" in 208 | */fish) : 209 | mkdir -p "$HOME/.config/fish/functions" 210 | hub alias fish > "$HOME/.config/fish/functions/git.fish" 211 | ;; 212 | *) : 213 | # shellcheck disable=SC2016 214 | append_to_shell_file 'eval "$(hub alias -s)"' 215 | ;; 216 | esac 217 | 218 | fancy_echo 'Checking on Node.js installation...' 219 | 220 | case "$SHELL" in 221 | */fish) : 222 | fancy_echo "We recommend following the steps at https://github.com/FabioAntunes/fish-nvm to install nvm in fish" 223 | ;; 224 | esac 225 | if ! brew_is_installed "node"; then 226 | if command -v n > /dev/null; then 227 | fancy_echo "We recommend using \`nvm\` and not \`n\`." 228 | fancy_echo "See https://pages.18f.gov/frontend/#install-npm" 229 | elif ! command -v nvm > /dev/null; then 230 | fancy_echo 'Installing nvm and lts Node.js and npm...' 231 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash 232 | export NVM_DIR="$HOME/.nvm" 233 | # shellcheck source=/dev/null 234 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 235 | # nvm is a bash script itself, some commands of which may fail WITHOUT 236 | # causing the whole operation to fail. To accomdate that, disable exit on 237 | # any nonzero exit code while nvm runs. 238 | set +e 239 | 240 | nvm install --lts 241 | 242 | # Turn it back on when nvm is done, since the rest of this script may have 243 | # been written assuming this behavior. 244 | set -e 245 | else 246 | fancy_echo 'version manager detected. Skipping...' 247 | fi 248 | else 249 | brew bundle --file=- < /dev/null; then eval "$(pyenv init -)"; fi' 273 | # shellcheck disable=SC2016 274 | append_to_shell_file 'if which pyenv-virtualenv-init > /dev/null; then eval "$(pyenv virtualenv-init -)"; fi' 275 | esac 276 | 277 | # pyenv currently doesn't have a convenience version to use, e.g., "latest", 278 | # so we check for the latest version against Homebrew instead. 279 | latest_python_3="$(brew info python3 | grep -E -o "3\.\d+\.\d+" | head -1)" 280 | 281 | if ! pyenv versions | ag "$latest_python_3" > /dev/null; then 282 | # Starting with macOS 10.14 (Mojave), the header files for system libraries 283 | # have been moved. Rather than hack the header paths based on OS version, 284 | # just install zlib with brew and build against that directly for now. 285 | brew install zlib 286 | export LDFLAGS="-L/usr/local/opt/zlib/lib" 287 | export CPPFLAGS="-I/usr/local/opt/zlib/include" 288 | pyenv install "$latest_python_3" 289 | pyenv global "$latest_python_3" 290 | pyenv rehash 291 | fi 292 | else 293 | brew bundle --file=- < /dev/null; then 300 | case "$SHELL" in 301 | */fish) : 302 | fancy_echo "virtualenvwrapper is not compatible with fish" 303 | ;; 304 | *) : 305 | fancy_echo 'Installing virtualenvwrapper...' 306 | pip3 install virtualenvwrapper 307 | append_to_shell_file 'export VIRTUALENVWRAPPER_PYTHON=/usr/local/bin/python3' 308 | append_to_shell_file 'export VIRTUALENVWRAPPER_VIRTUALENV=/usr/local/bin/virtualenv' 309 | append_to_shell_file 'source /usr/local/bin/virtualenvwrapper.sh' 310 | ;; 311 | esac 312 | fi 313 | fi 314 | 315 | fancy_echo '...Finished Python installation checks.' 316 | 317 | 318 | fancy_echo 'Checking on Ruby installation...' 319 | 320 | append_to_file "$HOME/.gemrc" 'gem: --no-document' 321 | 322 | case "$SHELL" in 323 | */fish) : 324 | fancy_echo "We recommend following the steps at https://github.com/JeanMertz/chruby-fish to install chruby in fish" 325 | ;; 326 | 327 | # For all other shells, install chruby as long as rbenv is not installed 328 | *) : 329 | if command -v rbenv >/dev/null || command -v rvm >/dev/null; then 330 | fancy_echo 'We recommend chruby and ruby-install over RVM or rbenv' 331 | else 332 | if ! brew_is_installed "chruby"; then 333 | fancy_echo 'Installing chruby, ruby-install, and the latest Ruby...' 334 | 335 | brew bundle --file=- < /dev/null 357 | latest_stable_ruby="$(cat < "$HOME/.cache/ruby-install/ruby/stable.txt" | tail -n1)" 358 | 359 | if ! [ "$latest_stable_ruby" = "$(latest_installed_ruby)" ]; then 360 | fancy_echo "Installing latest stable Ruby version: $latest_stable_ruby" 361 | ruby-install ruby 362 | else 363 | fancy_echo 'You have the latest version of Ruby' 364 | fi 365 | fi 366 | fi 367 | esac 368 | 369 | fancy_echo 'Updating Rubygems...' 370 | gem update --system 371 | 372 | gem_install_or_update 'bundler' 373 | 374 | fancy_echo "Configuring Bundler ..." 375 | number_of_cores=$(sysctl -n hw.ncpu) 376 | bundle config --global jobs $((number_of_cores - 1)) 377 | 378 | fancy_echo '...Finished Ruby installation checks.' 379 | 380 | curl -s https://raw.githubusercontent.com/18F/laptop/master/seekrets-install | sh - 381 | 382 | if [ -f "$HOME/.laptop.local" ]; then 383 | # shellcheck source=/dev/null 384 | . "$HOME/.laptop.local" 385 | fi 386 | 387 | fancy_echo 'All done!' 388 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Laptop 2 | ====== 3 | [![Build Status](https://circleci.com/gh/18F/laptop.svg)](https://circleci.com/gh/18F/laptop) 4 | 5 | Laptop is a script to set up an OS X computer for web development, and to keep 6 | it up to date. 7 | 8 | It can be run multiple times on the same machine safely. 9 | It installs, upgrades, or skips packages 10 | based on what is already installed on the machine. 11 | 12 | Requirements 13 | ------------ 14 | 15 | We support: 16 | 17 | * [macOS Sierra (10.12)](https://www.apple.com/osx/) 18 | * OS X El Capitan (10.11) 19 | * OS X Yosemite (10.10) 20 | * OS X Mavericks (10.9) 21 | 22 | Older versions may work but aren't regularly tested. Bug reports for older 23 | versions are welcome. 24 | 25 | Install 26 | ------- 27 | 28 | Begin by opening the Terminal application on your Mac. The easiest way to open 29 | an application in OS X is to search for it via [Spotlight]. The default 30 | keyboard shortcut for invoking Spotlight is `command-Space`. Once Spotlight 31 | is up, just start typing the first few letters of the app you are looking for, 32 | and once it appears, press `return` to launch it. 33 | 34 | In your Terminal window, copy and paste the command below, then press `return`. 35 | 36 | ```sh 37 | bash <(curl -s https://raw.githubusercontent.com/18F/laptop/master/laptop) 38 | ``` 39 | The [script](https://github.com/18F/laptop/blob/master/mac) itself is 40 | available in this repo for you to review if you want to see what it does 41 | and how it works. 42 | 43 | Note that the script will ask you to enter your OS X password at various 44 | points. This is the same password that you use to log in to your Mac. 45 | If you don't already have it installed, GitHub for Mac will launch 46 | automatically at the end of the script so you can set up everything you'll 47 | need to push code to GitHub. 48 | 49 | **Once the script is done, make sure to quit and relaunch Terminal.** 50 | 51 | More [detailed instructions with a video][video] are available in the Wiki. 52 | 53 | It is highly recommended to run the script regularly to keep your computer 54 | up to date. Once the script has been installed, you'll be able to run it 55 | at your convenience by typing `laptop` and hitting `return` in your Terminal. 56 | 57 | [Spotlight]: https://support.apple.com/en-us/HT204014 58 | [video]: https://github.com/18F/laptop/wiki/Detailed-installation-instructions-with-video 59 | 60 | ### Want to install just git-seekret? 61 | In your terminal window, copy and paste the following line, and press `return`: 62 | ```sh 63 | curl -s https://raw.githubusercontent.com/18F/laptop/master/seekrets-install | bash - 64 | ``` 65 | Note that the script may ask you to enter your password. This is the same password that you use to log in to your computer. 66 | 67 | > git-seekret requires git 2.9.1 or higher. Some versions of Ubuntu ship with an older version. To update your git before installing git-seekret: 68 | > 69 | > ``` 70 | > sudo add-apt-repository ppa:git-core/ppa 71 | > sudo apt-get update 72 | > ``` 73 | 74 | **git-seekret will install global git hooks into ~/.git-support/hooks. To restore pre-existing git hooks, it is recommended to save pre-existing hooks into a separate directory and to copy those hooks into ~/.git-support/hooks after git-seekret is installed.** 75 | 76 | Development 77 | ----------- 78 | 79 | ### Git Seekret 80 | 81 | This section covers contributing and developing new rulesets for `git-seekrets`. 82 | 83 | The rules installed by the `seekret-install` script are located in the `seekret-rules` directory at the root of this repository. Inside each rule file is a list of rules. The rule file can be considered a tree with the rules as the leaves of the tree. 84 | 85 | An example rule file is below: 86 | 87 | ```yaml 88 | thing_to_match: 89 | match: r[egx]{2,}p? 90 | unmatch: 91 | - some_prefix\s*r[egx]{2,}p? 92 | - r[egx]{2,}p?\s*some_suffix 93 | ``` 94 | 95 | Using the example above, let's break down each stanza: 96 | 97 | - `thing_to_match` : The name of the rule we'd like to match / unmatch. This can be anything that makes sense for the `.rule` file being created / edited. 98 | - `match` : A single regular expression which will be used to match any rules for the name above. 99 | - `unmatch` : A list of regular expressions which will be used to unmatch anything that the `match` rule matches. 100 | 101 | Feel free to submit an issue/create a pull request in order to submit a new ruleset or to apply a modifification to an existing ruleset. 102 | 103 | #### Testing Git Seekrets 104 | 105 | You can test secret rulesets using BATS for automated testing and manually using the installation script. 106 | 107 | ##### Let's talk about BATS 108 | 109 | Please read the [local BATS documentation](./test). 110 | 111 | ##### Let's talk about local manual testing 112 | 113 | To install the `*.rule` files located in the repo, just run the installation script locally. This will update your local `~/.git-support/seekret-rules` directory with the changes in this repository. 114 | 115 | ```shell 116 | ./seekrets-install 117 | ``` 118 | 119 | You should now be able to run the check within any repository on your machine. 120 | 121 | ```shell 122 | git seekret check -c 0 # check for secrets within commit history 123 | ``` 124 | 125 | ```shell 126 | git seekret check -s # check for secrets within staged files 127 | ``` 128 | 129 | Debugging 130 | --------- 131 | 132 | Your last Laptop run will be saved to `~/laptop.log`. Read through it to see if 133 | you can debug the issue yourself. If not, copy and paste the entire log into a 134 | [new GitHub Issue](https://github.com/18F/laptop/issues/new) for us. 135 | 136 | #### Git Seekrets False Positives 137 | 138 | Sometimes the `git-seekrets` rules may indicate a false positive and match 139 | things that aren't actually secrets. This can happen if the regular 140 | expressions used to `match` and `unmatch` are too strict. 141 | 142 | Make sure you have [the latest rulesets from this repository by running the 143 | git-seekrets installation script](#want-to-install-just-git-seekret). 144 | 145 | If the ruleset is still triggering a false positive, please open an issue 146 | (or a pull request if you know how to fix the regular expression), and 147 | include the text that is being treated as a false positive, along with the 148 | rules installed on your computer. Please run this command to output 149 | your current rules, then copy and paste them into the GitHub issue: 150 | 151 | ```shell 152 | cat ~/.git-support/seekret-rules/*.rule 153 | ``` 154 | 155 | What it sets up 156 | --------------- 157 | 158 | * [ChromeDriver] for headless website testing 159 | * [chruby] for managing [Ruby] versions 160 | * [Cloud Foundry CLI] for command line access to 18F's Cloud Foundry-based application platform 161 | * [Docker] for all your containerization needs 162 | * [git-seekret] for preventing you from committing passwords and other sensitive information to a git repository 163 | * [GitHub Desktop] for setting up your SSH keys automatically 164 | * [Homebrew] for managing operating system libraries 165 | * [Homebrew Cask] for quickly installing Mac apps from the command line 166 | * [Homebrew Services] so you can easily stop, start, and restart services 167 | * [hub] for interacting with the GitHub API 168 | * [nvm] for managing Node.js versions if you do not have [Node.js] already installed (Includes latest [Node.js] and [NPM], for running apps and installing JavaScript packages) 169 | * [pyenv] for managing Python versions if you do not have [Python] already installed (includes the latest 3.x [Python]) 170 | * [ruby-install] for installing different versions of Ruby 171 | * [Slack] for communicating with your team 172 | * [The Silver Searcher] for finding things in files 173 | * [Virtualenv] for creating isolated Python environments (via [pyenv] if it is installed) 174 | * [Virtualenvwrapper] for extending Virtualenv (via [pyenv] if it is installed) 175 | * [Zsh] as your shell 176 | 177 | [Bundler]: http://bundler.io/ 178 | [ChromeDriver]: http://chromedriver.chromium.org/ 179 | [chruby]: https://github.com/postmodern/chruby 180 | [Cloud Foundry CLI]: https://github.com/cloudfoundry/cli 181 | [Docker]: https://www.docker.com/ 182 | [git-seekret]: https://github.com/18F/git-seekret 183 | [Homebrew]: http://brew.sh/ 184 | [Homebrew Cask]: https://github.com/Homebrew/homebrew-cask 185 | [Homebrew Services]: https://github.com/Homebrew/homebrew-services 186 | [hub]: https://github.com/github/hub 187 | [n]: https://github.com/tj/n 188 | [Node.js]: http://nodejs.org/ 189 | [NPM]: https://www.npmjs.org/ 190 | [Python]: https://www.python.org/ 191 | [pyenv]: https://github.com/yyuu/pyenv/ 192 | [Ruby]: https://www.ruby-lang.org/en/ 193 | [ruby-install]: https://github.com/postmodern/ruby-install 194 | [Slack]: https://slack.com/ 195 | [The Silver Searcher]: https://github.com/ggreer/the_silver_searcher 196 | [Virtualenv]: https://virtualenv.pypa.io/en/latest/ 197 | [Virtualenvwrapper]: http://virtualenvwrapper.readthedocs.org/en/latest/# 198 | [Zsh]: http://www.zsh.org/ 199 | It should take less than 15 minutes to install (depends on your machine and 200 | internet connection). 201 | 202 | Customize in `~/.laptop.local` and `~/Brewfile.local` 203 | ----------------------------------------------------- 204 | 205 | Your `~/.laptop.local` is run at the end of the `mac` script. 206 | Put your customizations there. If you want to install additional 207 | tools or Mac apps with Homebrew, add them to your `~/Brewfile.local`. 208 | This repo already contains a [.laptop.local] and [Brewfile.local] 209 | you can use to get started. 210 | 211 | ```sh 212 | # Go to your OS X user's root directory 213 | cd ~ 214 | 215 | # Download the sample files to your computer 216 | curl --remote-name https://raw.githubusercontent.com/18F/laptop/master/.laptop.local 217 | curl --remote-name https://raw.githubusercontent.com/18F/laptop/master/Brewfile.local 218 | ``` 219 | 220 | It lets you install the following tools and apps: 221 | 222 | * [VSCode] - Microsoft's open source text editor 223 | * [Atom] - GitHub's open source text editor 224 | * [Sublime Text 3] for coding all the things 225 | * [Vim] for those who prefer the command line 226 | * [Exuberant Ctags] for indexing files for vim tab completion 227 | * [Firefox] for testing your website on a browser other than Chrome 228 | * [iTerm2] - an awesome replacement for the OS X Terminal 229 | * [reattach-to-user-namespace] to allow copy and paste from Tmux 230 | * [Tmux] for saving project state and switching between projects 231 | * [Spectacle] - automatic window manipulation 232 | 233 | [.laptop.local]: https://github.com/18F/laptop/blob/master/.laptop.local 234 | [Brewfile.local]: https://github.com/18F/laptop/blob/master/Brewfile.local 235 | [VSCode]: https://code.visualstudio.com/ 236 | [Atom]: https://atom.io/ 237 | [Sublime Text 3]: http://www.sublimetext.com/3 238 | [Exuberant Ctags]: http://ctags.sourceforge.net/ 239 | [Firefox]: https://www.mozilla.org/en-US/firefox/new/ 240 | [iTerm2]: http://iterm2.com/ 241 | [reattach-to-user-namespace]: https://github.com/ChrisJohnsen/tmux-MacOSX-pasteboard 242 | [Tmux]: https://tmux.github.io/ 243 | [Vim]: http://www.vim.org/ 244 | [Spectacle]: https://www.spectacleapp.com/ 245 | 246 | Write your customizations such that they can be run safely more than once. 247 | See the `mac` script for examples. 248 | 249 | Laptop functions such as `fancy_echo` and `gem_install_or_update` can be used 250 | in your `~/.laptop.local`. 251 | 252 | What about background services (Redis, Postgres, MySQL, etc.)? 253 | ---------------------------------------------------------- 254 | The script does not automatically install services like databases because you 255 | may not need any particular one, or you may need specific versions, not just 256 | the latest. For services, we recommend using [Docker]. For example, you can 257 | use the [`docker run`] command to start a service in a container and make the 258 | container's port available to your local machine: 259 | 260 | ```shell 261 | docker run -p 5432 postgres:10.6 262 | ``` 263 | 264 | You can also use [Docker Compose] to define your app's containers and their 265 | relationships, cutting out the need to manually setup each service. 266 | 267 | [Docker Compose]: https://docs.docker.com/compose/ 268 | [docker run]: https://docs.docker.com/engine/reference/run/#expose-incoming-ports 269 | 270 | How to switch your shell back to bash from zsh (or vice versa) 271 | -------------------------------------------------------------- 272 | 1. Find out which shell you're currently running: `echo $SHELL` 273 | 2. Find out the location of the shell you want to switch to. For example, if 274 | you want to switch to `bash`, run `which bash`. 275 | 3. Verify if the shell location is included in `/etc/shells`. 276 | Run `cat /etc/shells` to see the contents of the file. 277 | 4. If the location of the shell is included, run 278 | `chsh -s [the location of the shell]`. 279 | For example, if `which bash` returned `/bin/bash`, you would run 280 | `chsh -s /bin/bash`. 281 | 282 | If the location of the shell is not in `/etc/shells`, add it, then run the 283 | `chsh` command. 284 | If you have Sublime Text, you can open the file by running 285 | `subl /etc/shells`. 286 | 5. Quit and restart Terminal (or iTerm2), or open a new tab for the new shell 287 | to take effect. 288 | 289 | Whether you're using bash or zsh, we recommend installing the latest versions 290 | with Homebrew because the versions that came with your Mac are really old. 291 | ``` 292 | brew install bash 293 | ``` 294 | or 295 | ``` 296 | brew install zsh 297 | ``` 298 | 299 | Credits 300 | ------- 301 | 302 | The 18F laptop script is based on and inspired by 303 | [thoughtbot's laptop](https://github.com/thoughtbot/laptop) script. 304 | 305 | ### Public domain 306 | 307 | thoughtbot's original work remains covered under an [MIT License](https://github.com/thoughtbot/laptop/blob/c997c4fb5a986b22d6c53214d8f219600a4561ee/LICENSE). 308 | 309 | 18F's work on this project is in the worldwide [public domain](LICENSE.md), as are contributions to our project. As stated in [CONTRIBUTING](CONTRIBUTING.md): 310 | 311 | > This project is in the public domain within the United States, and copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/). 312 | > 313 | > All contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest. 314 | --------------------------------------------------------------------------------