krz/keycask

Password manager: Swift core library, CLI for macOS/Linux/Windows, iOS/macOS app. cli password-manager swift

Commit fe4dc49efe

fe4dc49efea7625ca766de473681a8f4ccbc3c3d

parent: 000891bcaa

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-17 14:58 UTC

Add package scaffold and CI

Layout: unified · split

.gitbay/ci.yml added +25
@@ -0,0 +1,25 @@
1# Each step runs in its own `sh -c`; exports do not survive between steps.
2# swiftly installs into $HOME, which persists across builds.
3jobs:
4 build:
5 steps:
6 - |
7 set -eu
8 command -v curl >/dev/null || { echo "runner is missing: curl"; exit 1; }
9 if ! command -v "$HOME/.local/bin/swiftly" >/dev/null 2>&1; then
10 curl -fsSL "https://download.swift.org/swiftly/linux/swiftly-$(uname -m).tar.gz" | tar -xz -C /tmp
11 /tmp/swiftly init --assume-yes --skip-install --quiet-shell-followup
12 fi
13 . "$HOME/.local/share/swiftly/env.sh"
14 swiftly install --use 6.4
15 swift format lint --strict --recursive Sources Tests Package.swift
16 swift build
17 test:
18 steps:
19 - |
20 set -eu
21 . "$HOME/.local/share/swiftly/env.sh"
22 swiftly install --use 6.4
23 swift test
24 paths-ignore:
25 - docs/**
.swift-format added +12
@@ -0,0 +1,12 @@
1{
2 "version": 1,
3 "indentation": { "spaces": 4 },
4 "lineLength": 100,
5 "maximumBlankLines": 1,
6 "respectsExistingLineBreaks": true,
7 "rules": {
8 "AlwaysUseLowerCamelCase": true,
9 "NeverForceUnwrap": false,
10 "NeverUseImplicitlyUnwrappedOptionals": true
11 }
12}
Package.resolved added +33
@@ -0,0 +1,33 @@
1{
2 "originHash" : "f4830ec40bb4ed7681d28a845578098d2aaa25c4b7867710f01723e5ec0c9aa9",
3 "pins" : [
4 {
5 "identity" : "swift-argument-parser",
6 "kind" : "remoteSourceControl",
7 "location" : "https://github.com/apple/swift-argument-parser",
8 "state" : {
9 "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382",
10 "version" : "1.8.2"
11 }
12 },
13 {
14 "identity" : "swift-asn1",
15 "kind" : "remoteSourceControl",
16 "location" : "https://github.com/apple/swift-asn1.git",
17 "state" : {
18 "revision" : "d9a5b37470adc940d22c3bcd5ca6953a516b727f",
19 "version" : "1.7.2"
20 }
21 },
22 {
23 "identity" : "swift-crypto",
24 "kind" : "remoteSourceControl",
25 "location" : "https://github.com/apple/swift-crypto",
26 "state" : {
27 "revision" : "95ba0316a9b733e92bb6b071255ff46263bbe7dc",
28 "version" : "3.15.1"
29 }
30 }
31 ],
32 "version" : 3
33}
Package.swift added +33
@@ -0,0 +1,33 @@
1// swift-tools-version:6.4
2import PackageDescription
3
4let package = Package(
5 name: "keycask",
6 platforms: [.macOS(.v14), .iOS(.v17)],
7 products: [
8 .library(name: "KeycaskCore", targets: ["KeycaskCore"]),
9 .executable(name: "keycask", targets: ["keycask"]),
10 ],
11 dependencies: [
12 .package(url: "https://github.com/apple/swift-crypto", from: "3.15.0"),
13 .package(url: "https://github.com/apple/swift-argument-parser", from: "1.8.0"),
14 ],
15 targets: [
16 .target(
17 name: "KeycaskCore",
18 dependencies: [
19 .product(name: "Crypto", package: "swift-crypto"),
20 .product(name: "_CryptoExtras", package: "swift-crypto"),
21 ]
22 ),
23 .executableTarget(
24 name: "keycask",
25 dependencies: [
26 "KeycaskCore",
27 .product(name: "ArgumentParser", package: "swift-argument-parser"),
28 ]
29 ),
30 .testTarget(name: "KeycaskCoreTests", dependencies: ["KeycaskCore"]),
31 .testTarget(name: "KeycaskCLITests", dependencies: ["keycask"]),
32 ]
33)
Sources/KeycaskCore/KeycaskCore.swift added +3
@@ -0,0 +1,3 @@
1public enum KeycaskCore {
2 public static let name = "keycask"
3}
Sources/keycask/main.swift added +3
@@ -0,0 +1,3 @@
1import KeycaskCore
2
3print(KeycaskCore.name)
Tests/KeycaskCLITests/CLI.swift added +32
@@ -0,0 +1,32 @@
1import Foundation
2import Testing
3
4enum Binary {
5 static let url: URL = {
6 #if os(macOS)
7 if let index = CommandLine.arguments.firstIndex(of: "--test-bundle-path"),
8 CommandLine.arguments.count > index + 1
9 {
10 return URL(fileURLWithPath: CommandLine.arguments[index + 1])
11 .deletingLastPathComponent() // MacOS
12 .deletingLastPathComponent() // Contents
13 .deletingLastPathComponent() // *.xctest
14 .deletingLastPathComponent() // Products/Debug
15 .appendingPathComponent("keycask")
16 }
17 for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
18 return bundle.bundleURL.deletingLastPathComponent().appendingPathComponent(
19 "keycask")
20 }
21 fatalError("test bundle not found")
22 #elseif os(Windows)
23 return Bundle.main.bundleURL.appendingPathComponent("keycask.exe")
24 #else
25 return Bundle.main.bundleURL.appendingPathComponent("keycask")
26 #endif
27 }()
28}
29
30@Test func binaryIsBuilt() {
31 #expect(FileManager.default.isExecutableFile(atPath: Binary.url.path))
32}
Tests/KeycaskCoreTests/SmokeTests.swift added +7
@@ -0,0 +1,7 @@
1import Testing
2
3@testable import KeycaskCore
4
5@Test func packageBuilds() {
6 #expect(KeycaskCore.name == "keycask")
7}