Skip to content

Commit

Permalink
Add searchPaths config option
Browse files Browse the repository at this point in the history
Closes #8
  • Loading branch information
samuelmeuli committed Apr 29, 2020
1 parent 4031eb1 commit f36c416
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 58 deletions.
48 changes: 14 additions & 34 deletions README.md
Expand Up @@ -38,40 +38,20 @@ Alternatively, if you don't want to build the binary yourself, you can download

You can configure the behavior of `tmignore` by creating a configuration file at `~/.config/tmignore/config.json`:

- **`"whitelist"`:** If there are certain files ignored by Git which you _do_ want to back up (e.g. configuration or password files), you can add these files to the whitelist:

```js
{
// Default: []
"whitelist": [
"*/application.yml",
"*/config.json",
"*/.env.*"
]
}
```

- **`"ignoredPaths"`:** You can also prevent `tmignore` from scanning certain folders for Git repositories:

```js
{
/*
Default: [
"~/.Trash",
"~/Applications",
"~/Downloads",
"~/Library",
"~/Music/iTunes",
"~/Music/Music",
"~/Pictures/Photos\\ Library.photoslibrary"
]
*/
"ignoredPaths": [
"~/.Trash",
"~/Documents/"
]
}
```
- **`"searchPaths"`:** Directories which should be scanned for Git repositories. Default: `["~"]` (home directory)

- **`"ignoredPaths"`:** Directories which should be excluded from the Git repository search. Default: `["~/.Trash", "~/Applications", "~/Downloads", "~/Library", "~/Music/iTunes", "~/Music/Music", "~/Pictures/Photos\\ Library.photoslibrary"]`

- **`"whitelist"`:** Files/directories which should be included in backups, even if they are matched by a `.gitignore` file. Useful e.g. for configuration or password files. Default: `[]`

**Configuration example:**

```json
{
"searchPaths": ["~", "/path/to/another/drive"],
"whitelist": ["*/application.yml", "*/config.json", "*/.env.*"]
}
```

## Contributing

Expand Down
49 changes: 33 additions & 16 deletions tmignore/Config.swift
Expand Up @@ -5,14 +5,25 @@ enum ConfigError: Error {
case parseFailed
}

/// Responsible for parsing and storing values of the script's configuration file
/// Responsible for parsing and storing values of the script's configuration file.
class Config {
let configPath = NSString(string: "~/.config/tmignore/config.json").expandingTildeInPath
var configJson = JSON()
let fileManager = FileManager.default

// Default values
// Directories which should be scanned for Git repositories.
let searchPathsKey = "searchPaths"
let searchPathsDefault = ["~"]
var searchPaths: [String] {
let searchPathsStrings = configJson[searchPathsKey] == JSON.null
? searchPathsDefault
: configJson[searchPathsKey].arrayValue.map { $0.stringValue }
return searchPathsStrings.map { NSString(string: $0).expandingTildeInPath }
}

// Paths which aren't scanned for Git repositories
var ignoredPaths = [
// Directories which should be excluded from the Git repository search.
let ignoredPathsKey = "ignoredPaths"
let ignoredPathsDefault = [
"~/.Trash",
"~/Applications",
"~/Downloads",
Expand All @@ -21,23 +32,29 @@ class Config {
"~/Music/Music",
"~/Pictures/Photos\\ Library.photoslibrary",
]
var ignoredPaths: [String] {
let ignoredPathsStrings = configJson[ignoredPathsKey] == JSON.null
? ignoredPathsDefault
: configJson[ignoredPathsKey].arrayValue.map { $0.stringValue }
return ignoredPathsStrings.map { NSString(string: $0).expandingTildeInPath }
}

// Files which will be included in backups, even if they are matched by a `.gitignore` file
var whitelist = [String]()
// Files/directories which should be included in backups, even if they are matched by a
// `.gitignore` file. Useful e.g. for configuration or password files.
let whitelistKey = "whitelist"
let whitelistDefault = [String]()
var whitelist: [String] {
let whitelistPathsStrings = configJson[whitelistKey] == JSON.null
? whitelistDefault
: configJson[whitelistKey].arrayValue.map { $0.stringValue }
return whitelistPathsStrings.map { NSString(string: $0).expandingTildeInPath }
}

/// Parses the cache file (if it exists) and saves the contained values into instance variables
/// so they can be accessed later on
/// Parses the cache file (if it exists) and saves its contents.
init() throws {
if let jsonData = NSData(contentsOfFile: configPath) {
do {
let json = try JSON(data: jsonData as Data)
logger.debug("Found config file at \(configPath)")
if json["ignoredPaths"] != JSON.null {
ignoredPaths = json["ignoredPaths"].arrayValue.map { $0.stringValue }
}
if json["whitelist"] != JSON.null {
whitelist = json["whitelist"].arrayValue.map { $0.stringValue }
}
configJson = try JSON(data: jsonData as Data)
} catch {
logger.error("Could not parse config file: \(error.localizedDescription)")
throw ConfigError.parseFailed
Expand Down
8 changes: 4 additions & 4 deletions tmignore/Git.swift
Expand Up @@ -38,12 +38,12 @@ class Git {

/// Searches the home directory for Git repositories and returns their paths. Folders specified in
/// `ignoredPaths` aren't traversed
static func findRepos(ignoredPaths: [String]) -> [String] {
static func findRepos(searchPath: String, ignoredPaths: [String]) -> [String] {
var repoPaths = [String]()
logger.info("Searching for Git repositories…")
logger.info("Searching for Git repositories in \(searchPath)")

// Start building array of arguments for the `find` command
var command = "find $HOME"
var command = "find \"\(searchPath)\""

// Tell `find` to skip the ignored paths
for ignoredPath in ignoredPaths {
Expand Down Expand Up @@ -77,7 +77,7 @@ class Git {
// Build list of repositories (e.g. ["/path/to/repo"])
repoPaths = gitDirs.map { String($0.dropLast(5)) }

logger.info("Found \(repoPaths.count) Git repositories")
logger.info("Found \(repoPaths.count) Git repositories in \(searchPath)")
return repoPaths
}
}
15 changes: 11 additions & 4 deletions tmignore/main.swift
Expand Up @@ -11,8 +11,8 @@ let cli = CLI(

class RunCommand: Command {
let name = "run"
let shortDescription = "Searches the disk for files/directories ignored by Git and excludes " +
"them from future Time Machine backups"
let shortDescription =
"Searches the disk for files/directories ignored by Git and excludes them from future Time Machine backups"

func execute() throws {
let cache = Cache()
Expand All @@ -21,12 +21,19 @@ class RunCommand: Command {
let config = try Config()

// Search file system for Git repositories
let repoPaths = Git.findRepos(ignoredPaths: config.ignoredPaths)
var repoSet = Set<String>()
for searchPath in config.searchPaths {
let repoList = Git.findRepos(searchPath: searchPath, ignoredPaths: config.ignoredPaths)
for repoPath in repoList {
repoSet.insert(repoPath)
}
}
logger.info("Found \(repoSet.count) Git repositories in total")

// Build list of files/directories which should be excluded from Time Machine backups
logger.info("Building list of files to exclude from backups…")
var exclusions = [String]()
for repoPath in repoPaths {
for repoPath in repoSet {
for path in Git.getIgnoredFiles(repoPath: repoPath) {
// Only exclude path from backups if it is not whitelisted
if config.whitelist.allSatisfy({ !pathMatchesGlob(glob: $0, path: path) }) {
Expand Down

0 comments on commit f36c416

Please sign in to comment.