Skip to content

Commit e1ec95e

Browse files
authored
Merge pull request #2 from yapstudios/xdg-config-paths
Move config to XDG Base Directory paths
2 parents 01a9af1 + 9712637 commit e1ec95e

9 files changed

Lines changed: 84 additions & 20 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ DerivedData/
66
.swiftpm/configuration/registries.json
77
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
88
.netrc
9-
.zeplin/
9+
.zeplin-cli/

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## 0.5.0
4+
5+
- Move global config from `~/.zeplin/` to `~/.config/zeplin-cli/` (XDG Base Directory)
6+
- Move update cache from `~/.zeplin/` to `~/.cache/zeplin-cli/`
7+
- Move project-local config from `.zeplin/` to `.zeplin-cli/`
8+
- Support `XDG_CONFIG_HOME` and `XDG_CACHE_HOME` environment variables
9+
310
## 0.4.3
411

512
- Fix `--limit` not returning more than 100 results (now paginates automatically)

README.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,11 @@ zeplin-cli user profile
221221
zeplin-cli auth init
222222
```
223223

224-
This prompts for your token, optionally a default organization ID, and saves credentials to `~/.zeplin/config.json` with restricted permissions (600). It also verifies the token works.
224+
This prompts for your token, optionally a default organization ID, and saves credentials to `~/.config/zeplin-cli/config.json` with restricted permissions (600). It also verifies the token works.
225225

226226
### Manual Config File
227227

228-
Create `~/.zeplin/config.json`:
228+
Create `~/.config/zeplin-cli/config.json`:
229229

230230
```json
231231
{
@@ -252,14 +252,24 @@ zeplin-cli projects list
252252
zeplin-cli projects list --token "your-personal-access-token"
253253
```
254254

255+
### Configuration Paths
256+
257+
zeplin-cli follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/):
258+
259+
| Purpose | Default Path | Override |
260+
|---------|-------------|----------|
261+
| Config | `~/.config/zeplin-cli/config.json` | `$XDG_CONFIG_HOME` |
262+
| Cache | `~/.cache/zeplin-cli/update-check.json` | `$XDG_CACHE_HOME` |
263+
| Project | `.zeplin-cli/config.json` ||
264+
255265
### Credential Resolution Order
256266

257267
Credentials are resolved in this order (first match wins):
258268

259269
1. `--token` command-line flag
260270
2. `ZEPLIN_TOKEN` environment variable
261-
3. Project-local config (`.zeplin/config.json`)
262-
4. Global config (`~/.zeplin/config.json`)
271+
3. Project-local config (`.zeplin-cli/config.json`)
272+
4. Global config (`~/.config/zeplin-cli/config.json`)
263273

264274
### Multiple Profiles
265275

@@ -588,7 +598,7 @@ zeplin-cli design-tokens get --project <project-id> --pretty > tokens.json
588598

589599
Run `zeplin-cli auth init` to set up credentials interactively. In interactive mode (`zeplin-cli` with no arguments), you'll be prompted to set up credentials automatically.
590600

591-
You can also check that your config file exists at `~/.zeplin/config.json`.
601+
You can also check that your config file exists at `~/.config/zeplin-cli/config.json`.
592602

593603
### "Unauthorized" errors
594604

Sources/ZeplinCLI/CLI.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ public struct Zeplin: ParsableCommand {
1616
Credentials can be provided via:
1717
1. Command-line flag (--token)
1818
2. Environment variable (ZEPLIN_TOKEN)
19-
3. Project-local config (.zeplin/config.json)
20-
4. Global config (~/.zeplin/config.json)
19+
3. Project-local config (.zeplin-cli/config.json)
20+
4. Global config (~/.config/zeplin-cli/config.json)
2121
2222
Run 'zeplin-cli auth init' to set up credentials interactively.
2323
Get a personal access token at https://app.zeplin.io/profile/developer
@@ -45,7 +45,7 @@ public struct Zeplin: ParsableCommand {
4545
DOCUMENTATION
4646
https://docs.zeplin.dev/reference/introduction
4747
""",
48-
version: "0.4.3",
48+
version: "0.5.0",
4949
subcommands: [
5050
InteractiveCommand.self,
5151
AuthCommand.self,

Sources/ZeplinCLI/Commands/Auth/AuthCommand.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ struct AuthCommand: ParsableCommand {
99
discussion: """
1010
Set up and manage Zeplin API credentials.
1111
12-
Credentials are stored in ~/.zeplin/config.json with restricted permissions (600).
12+
Credentials are stored in ~/.config/zeplin-cli/config.json with restricted permissions (600).
1313
You can configure multiple profiles for different accounts.
1414
1515
GETTING A TOKEN
@@ -155,7 +155,7 @@ struct AuthProfilesCommand: ParsableCommand {
155155
let resolver = CredentialResolver()
156156

157157
if let config = try resolver.loadConfig(from: CredentialResolver.globalConfigPath) {
158-
print("Global config (~/.zeplin/config.json):")
158+
print("Global config (\(CredentialResolver.globalConfigPath)):")
159159
print(" Default: \(config.defaultProfile ?? "(none)")")
160160
print(" Profiles:")
161161
for (name, profile) in config.profiles.sorted(by: { $0.key < $1.key }) {
@@ -166,11 +166,11 @@ struct AuthProfilesCommand: ParsableCommand {
166166
}
167167
}
168168
} else {
169-
print("No global config found at ~/.zeplin/config.json")
169+
print("No global config found at \(CredentialResolver.globalConfigPath)")
170170
}
171171

172172
if let config = try resolver.loadConfig(from: CredentialResolver.localConfigPath) {
173-
print("\nLocal config (.zeplin/config.json):")
173+
print("\nLocal config (\(CredentialResolver.localConfigPath)):")
174174
print(" Default: \(config.defaultProfile ?? "(none)")")
175175
print(" Profiles:")
176176
for (name, _) in config.profiles.sorted(by: { $0.key < $1.key }) {

Sources/ZeplinCLI/UpdateChecker.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
import ZeplinKit
23

34
public enum UpdateChecker {
45

@@ -9,9 +10,7 @@ public enum UpdateChecker {
910
private static let fetchTimeout: TimeInterval = 3
1011

1112
private static var cacheURL: URL {
12-
FileManager.default.homeDirectoryForCurrentUser
13-
.appendingPathComponent(".zeplin")
14-
.appendingPathComponent("update-check.json")
13+
Paths.updateCheckCacheFile
1514
}
1615

1716
public struct Cache: Codable {

Sources/ZeplinKit/Auth/CredentialResolver.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ public struct CredentialOptions: Sendable {
1313
}
1414

1515
public struct CredentialResolver: Sendable {
16-
public static let globalConfigPath = "~/.zeplin/config.json"
17-
public static let localConfigPath = ".zeplin/config.json"
16+
public static var globalConfigPath: String { Paths.globalConfigPath }
17+
public static var localConfigPath: String { Paths.localConfigPath }
1818

1919
public static let envToken = "ZEPLIN_TOKEN"
2020
public static let envOrganizationId = "ZEPLIN_ORGANIZATION_ID"
@@ -49,7 +49,7 @@ public struct CredentialResolver: Sendable {
4949
Credentials can also be provided via:
5050
- Command-line flag (--token)
5151
- Environment variable (ZEPLIN_TOKEN)
52-
- Config file (~/.zeplin/config.json)
52+
- Config file (\(Paths.globalConfigPath))
5353
5454
Get a personal access token at:
5555
https://app.zeplin.io/profile/developer

Sources/ZeplinKit/Paths.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Foundation
2+
3+
public enum Paths {
4+
5+
// MARK: - Config
6+
7+
public static var configDirectory: URL {
8+
let base: URL
9+
if let xdg = ProcessInfo.processInfo.environment["XDG_CONFIG_HOME"], !xdg.isEmpty {
10+
base = URL(fileURLWithPath: xdg)
11+
} else {
12+
base = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".config")
13+
}
14+
return base.appendingPathComponent("zeplin-cli")
15+
}
16+
17+
public static var globalConfigFile: URL {
18+
configDirectory.appendingPathComponent("config.json")
19+
}
20+
21+
public static var globalConfigPath: String {
22+
if let xdg = ProcessInfo.processInfo.environment["XDG_CONFIG_HOME"], !xdg.isEmpty {
23+
return URL(fileURLWithPath: xdg)
24+
.appendingPathComponent("zeplin-cli/config.json").path
25+
}
26+
return "~/.config/zeplin-cli/config.json"
27+
}
28+
29+
// MARK: - Cache
30+
31+
public static var cacheDirectory: URL {
32+
let base: URL
33+
if let xdg = ProcessInfo.processInfo.environment["XDG_CACHE_HOME"], !xdg.isEmpty {
34+
base = URL(fileURLWithPath: xdg)
35+
} else {
36+
base = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(".cache")
37+
}
38+
return base.appendingPathComponent("zeplin-cli")
39+
}
40+
41+
public static var updateCheckCacheFile: URL {
42+
cacheDirectory.appendingPathComponent("update-check.json")
43+
}
44+
45+
// MARK: - Local (project-scoped)
46+
47+
public static let localConfigPath = ".zeplin-cli/config.json"
48+
}

Tests/ZeplinKitTests/CredentialsTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ struct CredentialsTests {
132132

133133
@Test func missingProfileThrows() throws {
134134
let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
135-
let subDir = tempDir.appendingPathComponent(".zeplin")
135+
let subDir = tempDir.appendingPathComponent(".zeplin-cli")
136136
try FileManager.default.createDirectory(at: subDir, withIntermediateDirectories: true)
137137
defer { try? FileManager.default.removeItem(at: tempDir) }
138138

0 commit comments

Comments
 (0)