Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project overview

ACKLocalization is a macOS command-line tool (Swift Package, swift-tools 6.0, Swift 5 language mode, macOS 13+) that downloads translations from a Google Spreadsheet and generates `.strings` / `.stringsdict` / `InfoPlist.strings` files for Apple apps. It authenticates via Google service account, API key, or application default credentials (using the `google-auth-swift` dependency).

## Commands

- Build: `swift build`
- Run all tests: `swift test`
- Run a single test: `swift test --filter <TestSuiteOrTestName>` (e.g. `swift test --filter ConfigurationTests`)
- Release build (what CI archives on tag push): `swift build --configuration release`

Tests use **Swift Testing** (`@Test`/`#expect`), not XCTest.

## Architecture

Two targets with a thin executable wrapper:

- `Sources/ACKLocalization/main.swift` — executable; just instantiates `ACKLocalization` and calls `run()`.
- `Sources/ACKLocalizationCore` — all logic, exposed as a library so it is testable.

The pipeline lives in `ACKLocalizationCore/ACKLocalization.swift` and runs: load `localization.json` (`Model/Configuration.swift`, with fallback decoding of the legacy `ConfigurationV1` format) → resolve credentials (config values take priority over the `ACKLOCALIZATION_SERVICE_ACCOUNT_PATH` / `ACKLOCALIZATION_API_KEY` env vars, service account over API key, ADC as last resort) → fetch spreadsheet via `SheetsAPIService` → `transformValues` maps sheet columns to languages via `languageMapping` → `saveMappedValues` groups rows per output file (keys prefixed `plist.<FileName>.` go to `<FileName>.strings`, plural keys `key##{rule}` go to `.stringsdict`) and writes via `FileSystem`.

Dependency seams for testing: `SheetsAPIService` and `FileSystem` protocols (`Services/`), mocked in `Tests/ACKLocalizationCoreTests/Mocks/`. Concurrency is async/await throughout (no Combine).

Conventions for services and code style are in `.claude/rules/services.md` and `.claude/rules/style.md` — follow them (protocol + `Impl` in separate files, public factory functions, typed throws, no file header comments).

## CI and releases

- Every PR must touch `CHANGELOG.md` (enforced by the Checks workflow).
- Tests run on macOS via `swift test` for every push/PR.
- Releases: pushing a tag builds a release binary and attaches a zip to the GitHub release. Version bumps are commits like "🔖 Bump version to X.Y.Z". Distribution to users is via Mint.
- Commit messages follow the Ackee style guide: https://github.com/AckeeCZ/styleguide/blob/master/git/guides/commit-message.md, using the Ackee conventional gitmoji set (♻️, 🔥, ✅, 📝, 🔖, …): https://github.com/AckeeCZ/conventional-gitmoji
6 changes: 6 additions & 0 deletions .claude/rules/services.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Service naming and structure

- Protocols use the plain name (e.g. `SheetsAPIService`), implementations use the `Impl` suffix (e.g. `SheetsAPIServiceImpl`).
- Keep protocols and implementations in separate files.
- Implementations are `internal`; public services are exposed through a public factory function (e.g. `createSheetsAPIService(...)`) that mirrors the implementation's initializer.
- Prefer typed throws in service interfaces when a single error type applies (e.g. `async throws(RequestError)`).
3 changes: 3 additions & 0 deletions .claude/rules/style.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Code style

- No file header comments (no `// FileName.swift / Created by ...` blocks) — files start directly with imports.
104 changes: 104 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Read",
"Bash(swift build*)",
"Bash(swift test*)",
"Bash(swift run*)",
"Bash(swift package*)",
"Bash(git add *)",
"Bash(git checkout *)",
"Bash(git commit *)",
"Bash(git rev-parse *)",
"Bash(git stash *)",
"Bash(git status*)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(git show*)",
"Bash(git branch*)",
"Bash(git blame*)",
"Bash(git push*)",
"mcp__xcode__BuildProject",
"mcp__xcode__GetBuildLog",
"mcp__xcode__GetTestList",
"mcp__xcode__RunAllTests",
"mcp__xcode__RunSomeTests",
"mcp__xcode__DocumentationSearch",
"mcp__xcode__XcodeRead",
"mcp__xcode__XcodeGrep",
"mcp__xcode__XcodeGlob",
"mcp__xcode__XcodeLS",
"mcp__xcode__XcodeListNavigatorIssues",
"mcp__xcode__XcodeRefreshCodeIssuesInFile",
"WebFetch(domain:anthropic.com)"
],
"deny": [
"Bash(git push --force*)",
"Bash(git push -f*)",
"Bash(git push * --force*)",
"Bash(git push * -f)",
"Bash(git push * -f *)",
"Bash(git push --force-with-lease*)",
"Bash(git push * --force-with-lease*)",
"Bash(git push origin --delete*)",
"Bash(git push * --delete*)",
"Bash(git branch -d*)",
"Bash(git branch -D*)",
"Bash(git branch * -d*)",
"Bash(git branch * -D*)"
],
"ask": [
"Edit(.claude/settings.json)"
]
},
"enabledPlugins": {
"swift-concurrency@swift-concurrency-agent-skill": true,
"swift-lsp@claude-plugins-official": true
},
"extraKnownMarketplaces": {
"swift-concurrency-agent-skill": {
"source": {
"source": "github",
"repo": "AvdLee/Swift-Concurrency-Agent-Skill"
}
}
},
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"network": {
"allowedDomains": [
"github.com",
"*.github.com",
"*.githubusercontent.com",
"*.swift.org",
"*.apple.com"
]
},
"excludedCommands": [
"git *",
"swift *",
"xcodebuild *",
"xcrun *"
],
"filesystem": {
"allowWrite": [
"~/Library/Caches/org.swift.swiftpm",
"~/Library/org.swift.swiftpm",
"~/Library/Developer/Xcode/DerivedData"
],
"denyRead": [
"~/.bash_history",
"~/.bash_profile",
"~/.bashrc",
"~/.profile",
"~/.ssh",
"~/.zprofile",
"~/.zsh_history",
"~/.zshrc"
]
}
},
"plansDirectory": "./.claude/plans"
}
12 changes: 12 additions & 0 deletions .mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mcpServers": {
"xcode": {
"type": "stdio",
"command": "xcrun",
"args": [
"mcpbridge"
],
"env": {}
}
}
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

## main

### Changed
- Replace internal Combine usage with async/await ([#47](https://github.com/AckeeCZ/ACKLocalization/pull/47), kudos to @olejnjak)
- Migrate tests to Swift Testing and extend test coverage ([#47](https://github.com/AckeeCZ/ACKLocalization/pull/47), kudos to @olejnjak)

## 1.7.0

### Added
Expand Down
10 changes: 1 addition & 9 deletions Sources/ACKLocalization/main.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
//
// main.swift
//
//
// Created by Jakub Olejník on 11/12/2019.
//

import ACKLocalizationCore
import Foundation

let localization = ACKLocalization()

localization.run()
await localization.run()
Comment thread
olejnjak marked this conversation as resolved.
Loading
Loading