Skip to content

Commit 4fb5116

Browse files
Merge pull request #57 from stevegrunwell/release/v0.3.0
Version 0.3.0
2 parents c1dbe48 + 730c6fc commit 4fb5116

File tree

15 files changed

+2453
-53
lines changed

15 files changed

+2453
-53
lines changed

.editorconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# https://editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.{json,php}]
13+
indent_size = 4
14+
15+
[*.yml]
16+
indent_style = space
17+
indent_size = 2

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
.DS_Store
1+
vendor
2+
.DS_Store
3+
.phpunit.result.cache

.travis.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
os: osx
2+
language: shell
3+
4+
matrix:
5+
include:
6+
- name: macOS 10.15 (Catalina)
7+
osx_image: xcode11.5
8+
- name: macOS 10.14 (Mojave)
9+
osx_image: xcode11.3
10+
- name: macOS 10.13 (High Sierra)
11+
osx_image: xcode10.1
12+
fast_finish: true
13+
14+
install:
15+
- brew install composer shellcheck
16+
- |
17+
if [[ $(php -r 'echo phpversion() . PHP_EOL;') < 7.3.1 ]]; then
18+
composer update --prefer-lowest --prefer-dist --no-suggest --no-progress --no-ansi
19+
else
20+
composer install --prefer-dist --no-suggest --no-progress --no-ansi
21+
fi
22+
23+
script:
24+
- composer test

CHANGELOG.md

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,76 @@ All notable changes to this project will be documented in this file.
44

55
This project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [Version 0.3.0] — 2020-06-16
78

8-
## [0.2.0]
9+
### Added
10+
11+
* Added Homebrew support 🙌 ([#34], props @Dids)
12+
* Exclude Bower dependencies ([#22], props @moezzie)
13+
* Exclude Maven builds ([#30], props @bertschneider)
14+
* Exclude Stack dependencies ([#32], props @alex-kononovich)
15+
* Exclude Carthage dependencies ([#37], props @qvacua)
16+
* Exclude CocoaPods dependencies and Swift builds ([#43], props @slashmo)
17+
* Exclude Bundler, Cargo, and Dart dependencies ([#56])
18+
* Define a [Travis CI pipeline for Asimov](https://travis-ci.com/github/stevegrunwell/asimov) ([#20])
19+
* Add an automated test suite using PHPUnit ([#31])
20+
21+
### Fixed
22+
23+
* Removed an extraneous `read -r path`, which was causing the first match to be skipped ([#15], props @rowanbeentje)
24+
* Use the full system path when running `chmod` in `install.sh` ([#33], props @ko-dever)
25+
26+
### Changed
27+
28+
* The size of the excluded directories are now included in the Asimov output ([#16], props @rowanbeentje)
29+
* Switch to using find's -prune switch to exclude match subdirectories for speed, and exclude ~/Library folder from searches ([#17], props @rowanbeentje)
30+
* Rework the `find` command and path variables so that `find` is only run once however many FILEPATHS are set ([#18], @props @rowanbeentje, yet again 😉)
31+
Fix incorrect directory pruning, simplify path handling ([#36], props @rwe)
32+
* Recommend cloning via HTTPS rather than SSH for manual installations ([#52], props @Artoria2e5)
33+
* Don't look for matches in `~/.Trash` ([#55])
34+
35+
36+
## [Version 0.2.0] — 2017-11-25
37+
38+
### Added
939

1040
* Bundle the script with `com.stevegrunwell.asimov.plist`, enabling Asimov to be scheduled to run daily. Users can set this up in a single step by running the new `install.sh` script.
11-
* Fixed pathing issue when resolving the script directory for `install.sh`. Props @morganestes. (#7)
12-
* Change the scope of Asimov to find matching directories within the current user's home directory, not just `~/Sites`. Props to @vitch for catching this! (#10).
13-
* Added a formal change log to the repository. (#5)
41+
Added a formal change log to the repository. ([#5])
42+
43+
### Fixed
44+
45+
* Fixed pathing issue when resolving the script directory for `install.sh`. Props @morganestes. ([#7])
1446

47+
### Changed
48+
* Change the scope of Asimov to find matching directories within the current user's home directory, not just `~/Sites`. Props to @vitch for catching this! ([#10]).
1549

16-
## [0.1.0]
50+
51+
## [Version 0.1.0] — 2017-10-17
1752

1853
Initial public release.
1954

2055

2156
[Unreleased]: https://github.com/stevegrunwell/asimov/compare/master...develop
22-
[0.2.0]: https://github.com/stevegrunwell/asimov/releases/tag/v0.2.0
23-
[0.1.0]: https://github.com/stevegrunwell/asimov/releases/tag/v0.1.0
24-
[#10]: https://github.com/stevegrunwell/asimov/issues/10
57+
[Version 0.1.0]: https://github.com/stevegrunwell/asimov/releases/tag/v0.1.0
58+
[Version 0.2.0]: https://github.com/stevegrunwell/asimov/releases/tag/v0.2.0
59+
[Version 0.3.0]: https://github.com/stevegrunwell/asimov/releases/tag/v0.3.0
60+
[#5]: https://github.com/stevegrunwell/asimov/issues/5
2561
[#7]: https://github.com/stevegrunwell/asimov/issues/7
26-
[#5]: https://github.com/stevegrunwell/asimov/issues/5
62+
[#10]: https://github.com/stevegrunwell/asimov/issues/10
63+
[#15]: https://github.com/stevegrunwell/asimov/pull/15
64+
[#16]: https://github.com/stevegrunwell/asimov/pull/16
65+
[#17]: https://github.com/stevegrunwell/asimov/pull/17
66+
[#18]: https://github.com/stevegrunwell/asimov/pull/18
67+
[#20]: https://github.com/stevegrunwell/asimov/pull/20
68+
[#22]: https://github.com/stevegrunwell/asimov/pull/22
69+
[#30]: https://github.com/stevegrunwell/asimov/pull/30
70+
[#31]: https://github.com/stevegrunwell/asimov/pull/31
71+
[#32]: https://github.com/stevegrunwell/asimov/pull/32
72+
[#33]: https://github.com/stevegrunwell/asimov/pull/33
73+
[#34]: https://github.com/stevegrunwell/asimov/pull/34
74+
[#36]: https://github.com/stevegrunwell/asimov/pull/36
75+
[#37]: https://github.com/stevegrunwell/asimov/pull/37
76+
[#43]: https://github.com/stevegrunwell/asimov/pull/43
77+
[#52]: https://github.com/stevegrunwell/asimov/pull/52
78+
[#55]: https://github.com/stevegrunwell/asimov/pull/55
79+
[#56]: https://github.com/stevegrunwell/asimov/pull/56

README.md

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Asimov
22

3+
[![Build Status](https://travis-ci.com/stevegrunwell/asimov.svg?branch=develop)](https://travis-ci.com/stevegrunwell/asimov)
4+
![Requires macOS 10.13 (High Sierra) or newer](https://img.shields.io/badge/macOS-10.13%20or%20higher-blue)
5+
[![MIT license](https://img.shields.io/badge/license-MIT-green)](LICENSE.txt)
6+
37
> Those people who think they know everything are a great annoyance to those of us who do.<br>— Issac Asimov
48
59
For macOS users, [Time Machine](https://support.apple.com/en-us/HT201250) is a no-frills, set-it-and-forget-it solution for on-site backups. Plug in an external hard drive (or configure a network storage drive), and your Mac's files are backed up.
@@ -8,19 +12,51 @@ For the average consumer, Time Machine is an excellent choice, especially consid
812

913
Asimov aims to solve that problem, scanning your filesystem for known dependency directories (e.g. `node_modules/` living adjacent to a `package.json` file) and excluding them from Time Machine backups. After all, why eat up space on your backup drive for something you could easily restore via `npm install`?
1014

15+
1116
## Installation
1217

13-
To get started with Asimov, clone the repository or download and extract an archive anywhere you'd like on your Mac:
18+
Asimov may be installed in a few different ways:
19+
20+
### Installation via Homebrew
21+
22+
The easiest way to install Asimov is through [Homebrew](https://brew.sh):
23+
24+
```sh
25+
$ brew install asimov
26+
```
27+
28+
If you would prefer to use the latest development release, you may append the `--head` flag:
29+
30+
```sh
31+
$ brew install asimov --head
32+
```
33+
34+
Once installed, you may instruct Homebrew to automatically load the scheduled job, ensuring Asimov is being run automatically every day:
1435

1536
```sh
16-
$ git clone git@github.com:stevegrunwell/asimov.git
37+
$ sudo brew services start asimov
38+
```
39+
40+
If you don't need or want the scheduled job, you may run Asimov on-demand:
41+
42+
```sh
43+
$ asimov
44+
```
45+
46+
### Manual installation
47+
48+
If you would prefer to install Asimov manually, you can do so by cloning the repository (or downloading and extracting an archive of the source) anywhere on your Mac:
49+
50+
```sh
51+
$ git clone https://github.com/stevegrunwell/asimov.git --depth 1
1752
```
1853

1954
After you've cloned the repository, run the `install.sh` script to automatically:
2055
* Symlink Asimov to `/usr/local/bin`, making it readily available from anywhere.
2156
* Schedule Asimov to run once a day, ensuring new projects' dependencies are quickly excluded from Time Machine backups.
2257
* Run Asimov for the first time, finding all current project dependencies adding them to Time Machine's exclusion list.
2358

59+
2460
## How it works
2561

2662
At its essence, Asimov is a simple wrapper around Apple's `tmutil` program, which provides more granular control over Time Machine.
@@ -41,4 +77,4 @@ If a directory has been excluded from backups in error, you can remove the exclu
4177

4278
```bash
4379
$ tmutil removeexclusion /path/to/directory
44-
```
80+
```

asimov

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env bash
2+
set -Eeu -o pipefail
23

34
# Look through the local filesystem and exclude development dependencies
45
# from Apple Time Machine backups.
@@ -12,60 +13,89 @@
1213
#
1314
# For a full explanation, please see https://apple.stackexchange.com/a/25833/206772
1415
#
15-
# @version 0.2.0
16+
# @version 0.3.0
1617
# @author Steve Grunwell
1718
# @license MIT
1819

19-
readonly FILEPATHS=(
20-
"vendor ../composer.json"
21-
"node_modules ../package.json"
22-
".vagrant ../Vagrantfile"
20+
readonly ASIMOV_ROOT=~
21+
22+
# Paths to unconditionally skip over. This prevents Asimov from modifying the
23+
# Time Machine exclusions for these paths (and decendents). It has an important
24+
# side-effect of speeding up the search.
25+
readonly ASIMOV_SKIP_PATHS=(
26+
~/.Trash
27+
~/Library
2328
)
2429

25-
# Given a directory path, determine if the corresponding file (relative
26-
# to that directory) is available.
30+
# A list of "directory"/"sentinel" pairs.
2731
#
28-
# For example, when looking at a /vendor directory, we may choose to
29-
# ensure a composer.json file is available.
30-
dependency_file_exists() {
31-
filename=$1
32-
33-
read -r path;
34-
35-
while read -r path; do
36-
37-
# Return early if this is a nested dependency (e.g. node_modules
38-
# inside another node_modules directory.
39-
if [[ $(dirname "$path") == *"/$(basename "$path")/"* ]]; then
40-
continue;
41-
fi
42-
43-
if [ -f "${path}/${filename}" ]; then
44-
echo "$path"
45-
fi
46-
done
47-
}
32+
# Directories will only be excluded if the dependency ("sentinel") file exists.
33+
#
34+
# For example, 'node_modules package.json' means "exclude node_modules/ from the
35+
# Time Machine backups if there is a package.json file next to it."
36+
readonly ASIMOV_VENDOR_DIR_SENTINELS=(
37+
'.build Package.swift' # Swift
38+
'.packages pubspec.yaml' # Pub (Dart)
39+
'.stack-work stack.yaml' # Stack (Haskell)
40+
'.vagrant Vagrantfile' # Vagrant
41+
'Carthage Cartfile' # Carthage
42+
'Pods Podfile' # CocoaPods
43+
'bower_components bower.json' # Bower (JavaScript)
44+
'node_modules package.json' # npm, Yarn (NodeJS)
45+
'target Cargo.toml' # Cargo (Rust)
46+
'target pom.xml' # Maven
47+
'vendor composer.json' # Composer (PHP)
48+
'vendor Gemfile' # Bundler (Ruby)
49+
)
4850

49-
# Exclude the given path from Time Machine backups.
51+
# Exclude the given paths from Time Machine backups.
52+
# Reads the newline-separated list of paths from stdin.
5053
exclude_file() {
51-
while read -r path; do
52-
if tmutil isexcluded "$path" | grep -q '\[Excluded\]'; then
54+
local path
55+
while IFS=$'\n' read -r path; do
56+
if tmutil isexcluded "${path}" | grep -Fq '[Excluded]'; then
5357
echo "- ${path} is already excluded, skipping."
5458
continue
5559
fi
5660

57-
tmutil addexclusion "$path"
61+
tmutil addexclusion "${path}"
5862

59-
echo "- ${path} has been excluded from Time Machine backups."
60-
done
63+
sizeondisk=$(du -hs "${path}" | cut -f1)
64+
echo "- ${path} has been excluded from Time Machine backups (${sizeondisk})."
65+
done
6166
}
6267

63-
# Iterate over dependencies.
64-
for i in "${FILEPATHS[@]}"; do
65-
read -ra parts <<< "$i"
68+
# Iterate over the skip directories to construct the `find` expression.
69+
declare -a find_parameters_skip=()
70+
for d in "${ASIMOV_SKIP_PATHS[@]}"; do
71+
find_parameters_skip+=( -not \( -path "${d}" -prune \) )
72+
done
6673

67-
printf "\\n\\033[0;36mFinding %s/ directories with corresponding %s files...\\033[0m\\n" \
68-
"${parts[0]}" "${parts[1]}"
74+
# Iterate over the directory/sentinel pairs to construct the `find` expression.
75+
declare -a find_parameters_vendor=()
76+
for i in "${ASIMOV_VENDOR_DIR_SENTINELS[@]}"; do
77+
read -ra parts <<< "${i}"
6978

70-
find ~ -name "${parts[0]}" -type d | dependency_file_exists "${parts[1]}" | exclude_file
79+
# Add this folder to the `find` list, allowing a single `find` command to find all
80+
_exclude_name="${parts[0]}"
81+
_sibling_sentinel_name="${parts[1]}"
82+
83+
# Given a directory path, determine if the corresponding file (relative
84+
# to that directory) is available.
85+
#
86+
# For example, when looking at a /vendor directory, we may choose to
87+
# ensure a composer.json file is available.
88+
find_parameters_vendor+=( -or \( \
89+
-type d \
90+
-name "${_exclude_name}" \
91+
-execdir test -e "${_sibling_sentinel_name}" \; \
92+
-prune \
93+
-print \
94+
\) )
7195
done
96+
97+
printf '\n\033[0;36mFinding dependency directories with corresponding definition files…\033[0m\n'
98+
99+
find "${ASIMOV_ROOT}" \( "${find_parameters_skip[@]}" \) \( -false "${find_parameters_vendor[@]}" \) \
100+
| exclude_file \
101+
;

composer.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "stevegrunwell/asimov",
3+
"description": "Automatically exclude development dependencies from Apple Time Machine backups",
4+
"type": "project",
5+
"authors": [
6+
{
7+
"name": "Steve Grunwell",
8+
"homepage": "https://stevegrunwell.com"
9+
},
10+
{
11+
"name": "Sudar Muthu",
12+
"homepage": "https://sudarmuthu.com"
13+
}
14+
],
15+
"support": {
16+
"source": "https://github.com/stevegrunwell/asimov",
17+
"issues": "https://github.com/stevegrunwell/asimov/issues"
18+
},
19+
"license": "MIT",
20+
"require": {
21+
"php": ">=7.1"
22+
},
23+
"require-dev": {
24+
"phpunit/phpunit": "^7.5|^9.0"
25+
},
26+
"autoload-dev": {
27+
"psr-4": {
28+
"Tests\\": "tests/"
29+
}
30+
},
31+
"scripts": {
32+
"test": "sh tests/bin/run-tests.sh"
33+
},
34+
"scripts-descriptions": {
35+
"test": "Run the automated tests for Asimov."
36+
},
37+
"config": {
38+
"preferred-install": "dist",
39+
"sort-packages": true
40+
}
41+
}

0 commit comments

Comments
 (0)