Compare commits

..
Author SHA1 Message Date
Max Goedjen 581e59f2e3 Pop two 2025-09-03 01:18:52 -07:00
Max Goedjen 6ede76a86f Abs 2025-09-03 01:15:20 -07:00
Max Goedjen 58f8680eea Tring 2025-09-03 01:03:32 -07:00
Max Goedjen 4bbf99f6fe Test 2025-09-03 01:00:28 -07:00
Max Goedjen 3d74ef39b9 No path 2025-09-03 00:55:23 -07:00
Max Goedjen 325606612e Try setting home. 2025-09-03 00:52:36 -07:00
Max Goedjen b8f349d1f0 Update path 2025-09-03 00:36:47 -07:00
Max Goedjen d3e1852b63 Merge branch 'main' into crowdin_config 2025-09-03 00:33:07 -07:00
Max Goedjen 612c7e1d71 Add Crowdin configuration file 2025-09-02 21:50:50 -07:00
12 changed files with 133 additions and 81 deletions
+13
View File
@@ -0,0 +1,13 @@
"project_id_env": "CROWDIN_PROJECT_ID"
"api_token_env": "CROWDIN_PERSONAL_TOKEN"
"base_path": "../../"
"preserve_hierarchy": true
"files": [
{
"source": "Sources/Packages/Resources/Localizable.xcstrings",
"translation": "Sources/Packages/Resources/Localizable.xcstrings",
"multilingual": true
}
]
+8 -15
View File
@@ -3,16 +3,10 @@ name: Nightly
on: on:
schedule: schedule:
- cron: "0 8 * * *" - cron: "0 8 * * *"
workflow_dispatch:
jobs: jobs:
build: build:
# runs-on: macOS-latest # runs-on: macOS-latest
runs-on: macos-15 runs-on: macos-15
permissions:
id-token: write
contents: write
attestations: write
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@@ -36,23 +30,22 @@ jobs:
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
- name: Build - name: Build
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
- name: Create ZIP - name: Create ZIPs
run: | run: |
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Archive.zip
- name: Notarize - name: Notarize
env: env:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
- name: Upload App to Artifacts
id: upload
uses: actions/upload-artifact@v4
with:
name: Secretive.zip
path: Secretive.zip
- name: Attest - name: Attest
id: attest id: attest
uses: actions/attest-build-provenance@v2 uses: actions/attest-build-provenance@v2
with: with:
subject-name: "Secretive.zip" subject-path: 'Secretive.zip'
subject-digest: sha256:${{ steps.upload.outputs.artifact-digest }} - name: Upload App to Artifacts
uses: actions/upload-artifact@v4
with:
name: Secretive.zip
path: Secretive.zip
+14 -9
View File
@@ -56,34 +56,39 @@ jobs:
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
- name: Build - name: Build
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
- name: Create ZIP - name: Create ZIPs
run: | run: |
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Xcode_Archive.zip
- name: Notarize - name: Notarize
env: env:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
- name: Upload App to Artifacts
id: upload
uses: actions/upload-artifact@v4
with:
name: Secretive.zip
path: Secretive.zip
- name: Attest - name: Attest
id: attest id: attest
uses: actions/attest-build-provenance@v2 uses: actions/attest-build-provenance@v2
with: with:
subject-name: "Secretive.zip" subject-path: 'Secretive.zip, Xcode_Archive.zip'
subject-digest: ${{ steps.upload.outputs.artifact-digest }}
- name: Create Release - name: Create Release
run: | run: |
sed -i.tmp "s/RUN_ID/$RUN_ID/g" .github/templates/release.md sed -i.tmp "s/RUN_ID/$RUN_ID/g" .github/templates/release.md
sed -i.tmp "s/ATTESTATION_ID/$ATTESTATION_ID/g" .github/templates/release.md sed -i.tmp "s/ATTESTATION_ID/$ATTESTATION_ID/g" .github/templates/release.md
gh release create $TAG_NAME -d -F .github/templates/release.md gh release create $TAG_NAME -d -F .github/templates/release.md
gh release upload Secretive.zip gh release upload Secretive.zip
gh release upload Xcode_Archive.zip
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.ref }} TAG_NAME: ${{ github.ref }}
RUN_ID: ${{ github.run_id }} RUN_ID: ${{ github.run_id }}
ATTESTATION_ID: ${{ steps.attest.outputs.attestation-id }} ATTESTATION_ID: ${{ steps.attest.outputs.attestation-id }}
- name: Upload App to Artifacts
uses: actions/upload-artifact@v4
with:
name: Secretive.zip
path: Secretive.zip
- name: Upload Archive to Artifacts
uses: actions/upload-artifact@v4
with:
name: Xcode_Archive.zip
path: Xcode_Archive.zip
@@ -3403,6 +3403,28 @@
} }
} }
}, },
"integrations_public_key_path_placeholder" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "YOUR_PUBLIC_KEY_PATH"
}
}
}
},
"integrations_public_key_placeholder" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "YOUR_PUBLIC_KEY"
}
}
}
},
"integrations_shell_section_title" : { "integrations_shell_section_title" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
@@ -30,7 +30,7 @@ extension SecureEnclave {
SecItemCopyMatching(privateAttributes, &privateUntyped) SecItemCopyMatching(privateAttributes, &privateUntyped)
guard let privateTyped = privateUntyped as? [[CFString: Any]] else { return } guard let privateTyped = privateUntyped as? [[CFString: Any]] else { return }
let migratedPublicKeys = Set(store.secrets.map(\.publicKey)) let migratedPublicKeys = Set(store.secrets.map(\.publicKey))
var migratedAny = false var migrated = false
for key in privateTyped { for key in privateTyped {
let name = key[kSecAttrLabel] as? String ?? String(localized: .unnamedSecret) let name = key[kSecAttrLabel] as? String ?? String(localized: .unnamedSecret)
let id = key[kSecAttrApplicationLabel] as! Data let id = key[kSecAttrApplicationLabel] as! Data
@@ -45,7 +45,6 @@ extension SecureEnclave {
// Best guess. // Best guess.
let auth: AuthenticationRequirement = String(describing: accessControl) let auth: AuthenticationRequirement = String(describing: accessControl)
.contains("DeviceOwnerAuthentication") ? .presenceRequired : .unknown .contains("DeviceOwnerAuthentication") ? .presenceRequired : .unknown
do {
let parsed = try CryptoKit.SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: tokenObjectID) let parsed = try CryptoKit.SecureEnclave.P256.Signing.PrivateKey(dataRepresentation: tokenObjectID)
let secret = Secret(id: UUID().uuidString, name: name, publicKey: parsed.publicKey.x963Representation, attributes: Attributes(keyType: .init(algorithm: .ecdsa, size: 256), authentication: auth)) let secret = Secret(id: UUID().uuidString, name: name, publicKey: parsed.publicKey.x963Representation, attributes: Attributes(keyType: .init(algorithm: .ecdsa, size: 256), authentication: auth))
guard !migratedPublicKeys.contains(parsed.publicKey.x963Representation) else { guard !migratedPublicKeys.contains(parsed.publicKey.x963Representation) else {
@@ -57,12 +56,9 @@ extension SecureEnclave {
try store.saveKey(tokenObjectID, name: name, attributes: secret.attributes) try store.saveKey(tokenObjectID, name: name, attributes: secret.attributes)
logger.log("Migrated \(name).") logger.log("Migrated \(name).")
try markMigrated(secret: secret, oldID: id) try markMigrated(secret: secret, oldID: id)
migratedAny = true migrated = true
} catch {
logger.error("Failed to migrate \(name): \(error).")
} }
} if migrated {
if migratedAny {
store.reloadSecrets() store.reloadSecrets()
} }
} }
@@ -146,6 +146,6 @@ struct CreateSecretView<StoreType: SecretStoreModifiable>: View {
} }
//#Preview { #Preview {
// CreateSecretView(store: Preview.StoreModifiable()) { _ in } CreateSecretView(store: Preview.StoreModifiable()) { _ in }
//} }
@@ -57,10 +57,15 @@ struct EmptyStoreModifiableView: View {
} }
} }
#if DEBUG
#Preview { struct EmptyStoreModifiableView_Previews: PreviewProvider {
static var previews: some View {
Group {
EmptyStoreImmutableView() EmptyStoreImmutableView()
}
#Preview {
EmptyStoreModifiableView() EmptyStoreModifiableView()
} }
}
}
#endif
@@ -13,7 +13,12 @@ struct NoStoresView: View {
} }
#Preview { #if DEBUG
struct NoStoresView_Previews: PreviewProvider {
static var previews: some View {
NoStoresView() NoStoresView()
} }
}
#endif
@@ -37,6 +37,6 @@ struct SecretDetailView<SecretType: Secret>: View {
} }
//#Preview { #Preview {
// SecretDetailView(secret: Preview.Secret(name: "Demonstration Secret")) SecretDetailView(secret: Preview.Secret(name: "Demonstration Secret"))
//} }
@@ -143,11 +143,11 @@ struct AgentNotRunningView: View {
} }
//#Preview { #Preview {
// AgentStatusView() AgentStatusView()
// .environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: false)) .environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: false))
//} }
//#Preview { #Preview {
// AgentStatusView() AgentStatusView()
// .environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: true, process: .current)) .environment(\.agentStatusChecker, PreviewAgentStatusChecker(running: true, process: .current))
//} }
+21 -13
View File
@@ -198,17 +198,25 @@ extension ContentView {
} }
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
// Empty on modifiable and nonmodifiable
ContentView(showingCreation: .constant(false), runningSetup: .constant(false), hasRunSetup: .constant(true))
.environment(Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]))
.environment(PreviewUpdater())
// 5 items on modifiable and nonmodifiable
ContentView(showingCreation: .constant(false), runningSetup: .constant(false), hasRunSetup: .constant(true))
.environment(Preview.storeList(stores: [Preview.Store()], modifiableStores: [Preview.StoreModifiable()]))
.environment(PreviewUpdater())
}
}
}
#endif
//#Preview {
// // Empty on modifiable and nonmodifiable
// ContentView(showingCreation: .constant(false), runningSetup: .constant(false), hasRunSetup: .constant(true))
// .environment(Preview.storeList(stores: [Preview.Store(numberOfRandomSecrets: 0)], modifiableStores: [Preview.StoreModifiable(numberOfRandomSecrets: 0)]))
// .environment(PreviewUpdater())
//}
//
//#Preview {
// // 5 items on modifiable and nonmodifiable
// ContentView(showingCreation: .constant(false), runningSetup: .constant(false), hasRunSetup: .constant(true))
// .environment(Preview.storeList(stores: [Preview.Store()], modifiableStores: [Preview.StoreModifiable()]))
// .environment(PreviewUpdater())
//}
@@ -163,12 +163,17 @@ fileprivate struct BackgroundViewModifier: ViewModifier {
} }
#Preview { #if DEBUG
struct CopyableView_Previews: PreviewProvider {
static var previews: some View {
Group {
CopyableView(title: .secretDetailSha256FingerprintLabel, image: Image(systemName: "figure.wave"), text: "Hello world.") CopyableView(title: .secretDetailSha256FingerprintLabel, image: Image(systemName: "figure.wave"), text: "Hello world.")
.padding() .padding()
}
#Preview {
CopyableView(title: .secretDetailSha256FingerprintLabel, image: Image(systemName: "figure.wave"), text: "Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. ") CopyableView(title: .secretDetailSha256FingerprintLabel, image: Image(systemName: "figure.wave"), text: "Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. Long text. ")
.padding() .padding()
} }
}
}
#endif