mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-12-14 21:10:56 +00:00
make it build
This commit is contained in:
parent
c12d381280
commit
303c5fc2a1
12
.cursor/commands/deslop.md
Normal file
12
.cursor/commands/deslop.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Remove AI code slop
|
||||||
|
|
||||||
|
Check the diff against main, and remove all AI generated slop introduced in this branch.
|
||||||
|
|
||||||
|
This includes:
|
||||||
|
- Extra comments that a human wouldn't add or is inconsistent with the rest of the file
|
||||||
|
- Extra defensive checks or try/catch blocks that are abnormal for that area of the codebase (especially if called by trusted / validated codepaths)
|
||||||
|
- Casts to any to get around type issues
|
||||||
|
- Deeply nested code that should be refactored using early return guards to improve readability
|
||||||
|
- Any other style that is inconsistent with the file
|
||||||
|
|
||||||
|
Report at the end with only a 1-3 sentence summary of what you changed.
|
||||||
22
.github/scripts/signing.sh
vendored
22
.github/scripts/signing.sh
vendored
@ -1,22 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Import certificate and private key
|
|
||||||
echo $SIGNING_DATA | base64 -d -o Signing.p12
|
|
||||||
security create-keychain -p ci ci.keychain
|
|
||||||
security default-keychain -s ci.keychain
|
|
||||||
security list-keychains -s ci.keychain
|
|
||||||
security import ./Signing.p12 -k ci.keychain -P $SIGNING_PASSWORD -A
|
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k ci ci.keychain
|
|
||||||
|
|
||||||
# Import Profiles
|
|
||||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
|
||||||
echo $HOST_PROFILE_DATA | base64 -d -o Host.provisionprofile
|
|
||||||
HOST_UUID=`grep UUID -A1 -a Host.provisionprofile | grep -io "[-A-F0-9]\{36\}"`
|
|
||||||
cp Host.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$HOST_UUID.provisionprofile
|
|
||||||
echo $AGENT_PROFILE_DATA | base64 -d -o Agent.provisionprofile
|
|
||||||
AGENT_UUID=`grep UUID -A1 -a Agent.provisionprofile | grep -io "[-A-F0-9]\{36\}"`
|
|
||||||
cp Agent.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$AGENT_UUID.provisionprofile
|
|
||||||
|
|
||||||
# Create directories for ASC key
|
|
||||||
mkdir ~/.private_keys
|
|
||||||
echo -n "$APPLE_API_KEY_DATA" > ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8
|
|
||||||
93
.github/workflows/nightly.yml
vendored
93
.github/workflows/nightly.yml
vendored
@ -23,7 +23,22 @@ jobs:
|
|||||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||||
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: |
|
||||||
|
echo $SIGNING_DATA | base64 -d -o Signing.p12
|
||||||
|
security create-keychain -p ci ci.keychain
|
||||||
|
security default-keychain -s ci.keychain
|
||||||
|
security list-keychains -s ci.keychain
|
||||||
|
security import ./Signing.p12 -k ci.keychain -P $SIGNING_PASSWORD -A
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k ci ci.keychain
|
||||||
|
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||||
|
echo $HOST_PROFILE_DATA | base64 -d -o Host.provisionprofile
|
||||||
|
HOST_UUID=$(grep UUID -A1 -a Host.provisionprofile | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp Host.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$HOST_UUID.provisionprofile
|
||||||
|
echo $AGENT_PROFILE_DATA | base64 -d -o Agent.provisionprofile
|
||||||
|
AGENT_UUID=$(grep UUID -A1 -a Agent.provisionprofile | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp Agent.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$AGENT_UUID.provisionprofile
|
||||||
|
mkdir ~/.private_keys
|
||||||
|
echo -n "$APPLE_API_KEY_DATA" > ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.1.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.1.app
|
||||||
- name: Update Build Number
|
- name: Update Build Number
|
||||||
@ -34,32 +49,70 @@ jobs:
|
|||||||
sed -i '' -e "s/GITHUB_CI_VERSION/0.0.0_nightly-$DATE/g" Sources/Config/Config.xcconfig
|
sed -i '' -e "s/GITHUB_CI_VERSION/0.0.0_nightly-$DATE/g" Sources/Config/Config.xcconfig
|
||||||
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||||
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
|
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||||
- name: Build
|
- name: Build App
|
||||||
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: Move to Artifact Folder
|
- name: Build CLI
|
||||||
run: mkdir Artifact; cp -r Archive.xcarchive/Products/Applications/Secretive.app Artifact
|
run: swift build -c release --product SecretiveCLI --package-path Sources/Packages
|
||||||
- name: Upload App to Artifacts
|
- name: Codesign CLI
|
||||||
id: upload
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: Secretive
|
|
||||||
path: Artifact
|
|
||||||
- name: Download Zipped Artifact
|
|
||||||
id: download
|
|
||||||
env:
|
|
||||||
ZIP_ID: ${{ steps.upload.outputs.artifact-id }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
run: |
|
||||||
curl -L -H "Authorization: Bearer $GITHUB_TOKEN" -L \
|
CLI_BINARY="Sources/Packages/.build/release/SecretiveCLI"
|
||||||
https://api.github.com/repos/maxgoedjen/secretive/actions/artifacts/$ZIP_ID/zip > Secretive.zip
|
ENTITLEMENTS="Sources/Packages/Sources/SecretiveCLI/SecretiveCLI.entitlements"
|
||||||
|
IDENTITY=$(security find-identity -p codesigning -v 2>/dev/null | grep "Developer ID Application" | head -n1 | awk -F'"' '{print $2}')
|
||||||
|
codesign --force --options runtime --sign "$IDENTITY" --identifier "com.maxgoedjen.Secretive.Host" --entitlements "$ENTITLEMENTS" "$CLI_BINARY"
|
||||||
|
- name: Prepare Artifact Folder
|
||||||
|
run: |
|
||||||
|
mkdir -p Artifact/App
|
||||||
|
mkdir -p Artifact/CLI
|
||||||
|
cp -r Archive.xcarchive/Products/Applications/Secretive.app Artifact/App/
|
||||||
|
cp Sources/Packages/.build/release/SecretiveCLI Artifact/CLI/secretive
|
||||||
|
- name: Build Installer Package
|
||||||
|
run: |
|
||||||
|
pkgbuild --root Artifact/App --install-location /Applications --identifier com.maxgoedjen.Secretive.app --version 1.0 App.pkg
|
||||||
|
pkgbuild --root Artifact/CLI --install-location /usr/local/bin --identifier com.maxgoedjen.Secretive.cli --version 1.0 CLI.pkg
|
||||||
|
cat > distribution.xml << 'EOF'
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<installer-gui-script minSpecVersion="2">
|
||||||
|
<title>Secretive</title>
|
||||||
|
<organization>com.maxgoedjen</organization>
|
||||||
|
<domains enable_localSystem="true"/>
|
||||||
|
<options customize="never" require-scripts="false" rootVolumeOnly="true"/>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.app"/>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.cli"/>
|
||||||
|
<choices-outline>
|
||||||
|
<line choice="default">
|
||||||
|
<line choice="com.maxgoedjen.Secretive.app"/>
|
||||||
|
<line choice="com.maxgoedjen.Secretive.cli"/>
|
||||||
|
</line>
|
||||||
|
</choices-outline>
|
||||||
|
<choice id="default"/>
|
||||||
|
<choice id="com.maxgoedjen.Secretive.app" visible="false">
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.app"/>
|
||||||
|
</choice>
|
||||||
|
<choice id="com.maxgoedjen.Secretive.cli" visible="false">
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.cli"/>
|
||||||
|
</choice>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.app" version="1.0" onConclusion="none">App.pkg</pkg-ref>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.cli" version="1.0" onConclusion="none">CLI.pkg</pkg-ref>
|
||||||
|
</installer-gui-script>
|
||||||
|
EOF
|
||||||
|
productbuild --distribution distribution.xml --package-path . Secretive-unsigned.pkg
|
||||||
|
INSTALLER_IDENTITY=$(security find-identity -p basic -v 2>/dev/null | grep "Developer ID Installer" | head -n1 | awk -F'"' '{print $2}')
|
||||||
|
productsign --sign "$INSTALLER_IDENTITY" Secretive-unsigned.pkg Secretive.pkg
|
||||||
- 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 --wait Secretive.pkg
|
||||||
|
xcrun stapler staple Secretive.pkg
|
||||||
|
- name: Upload Installer to Artifacts
|
||||||
|
id: upload
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Secretive.pkg
|
||||||
|
path: Secretive.pkg
|
||||||
- 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.pkg"
|
||||||
subject-digest: sha256:${{ steps.upload.outputs.artifact-digest }}
|
|
||||||
|
|||||||
111
.github/workflows/release.yml
vendored
111
.github/workflows/release.yml
vendored
@ -20,7 +20,22 @@ jobs:
|
|||||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||||
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: |
|
||||||
|
echo $SIGNING_DATA | base64 -d -o Signing.p12
|
||||||
|
security create-keychain -p ci ci.keychain
|
||||||
|
security default-keychain -s ci.keychain
|
||||||
|
security list-keychains -s ci.keychain
|
||||||
|
security import ./Signing.p12 -k ci.keychain -P $SIGNING_PASSWORD -A
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k ci ci.keychain
|
||||||
|
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||||
|
echo $HOST_PROFILE_DATA | base64 -d -o Host.provisionprofile
|
||||||
|
HOST_UUID=$(grep UUID -A1 -a Host.provisionprofile | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp Host.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$HOST_UUID.provisionprofile
|
||||||
|
echo $AGENT_PROFILE_DATA | base64 -d -o Agent.provisionprofile
|
||||||
|
AGENT_UUID=$(grep UUID -A1 -a Agent.provisionprofile | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp Agent.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$AGENT_UUID.provisionprofile
|
||||||
|
mkdir ~/.private_keys
|
||||||
|
echo -n "$APPLE_API_KEY_DATA" > ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.1.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.1.app
|
||||||
- name: Test
|
- name: Test
|
||||||
@ -45,7 +60,22 @@ jobs:
|
|||||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||||
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||||
run: ./.github/scripts/signing.sh
|
run: |
|
||||||
|
echo $SIGNING_DATA | base64 -d -o Signing.p12
|
||||||
|
security create-keychain -p ci ci.keychain
|
||||||
|
security default-keychain -s ci.keychain
|
||||||
|
security list-keychains -s ci.keychain
|
||||||
|
security import ./Signing.p12 -k ci.keychain -P $SIGNING_PASSWORD -A
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k ci ci.keychain
|
||||||
|
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||||
|
echo $HOST_PROFILE_DATA | base64 -d -o Host.provisionprofile
|
||||||
|
HOST_UUID=$(grep UUID -A1 -a Host.provisionprofile | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp Host.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$HOST_UUID.provisionprofile
|
||||||
|
echo $AGENT_PROFILE_DATA | base64 -d -o Agent.provisionprofile
|
||||||
|
AGENT_UUID=$(grep UUID -A1 -a Agent.provisionprofile | grep -io "[-A-F0-9]\{36\}")
|
||||||
|
cp Agent.provisionprofile ~/Library/MobileDevice/Provisioning\ Profiles/$AGENT_UUID.provisionprofile
|
||||||
|
mkdir ~/.private_keys
|
||||||
|
echo -n "$APPLE_API_KEY_DATA" > ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8
|
||||||
- name: Set Environment
|
- name: Set Environment
|
||||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.1.app
|
run: sudo xcrun xcode-select -s /Applications/Xcode_26.1.app
|
||||||
- name: Update Build Number
|
- name: Update Build Number
|
||||||
@ -57,34 +87,73 @@ jobs:
|
|||||||
sed -i '' -e "s/GITHUB_CI_VERSION/$CLEAN_TAG/g" Sources/Config/Config.xcconfig
|
sed -i '' -e "s/GITHUB_CI_VERSION/$CLEAN_TAG/g" Sources/Config/Config.xcconfig
|
||||||
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||||
sed -i '' -e "s/GITHUB_BUILD_URL/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
|
sed -i '' -e "s/GITHUB_BUILD_URL/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||||
- name: Build
|
- name: Build App
|
||||||
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: Move to Artifact Folder
|
- name: Build CLI
|
||||||
run: mkdir Artifact; cp -r Archive.xcarchive/Products/Applications/Secretive.app Artifact
|
run: swift build -c release --product SecretiveCLI --package-path Sources/Packages
|
||||||
- name: Upload App to Artifacts
|
- name: Codesign CLI
|
||||||
id: upload
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: Secretive.zip
|
|
||||||
path: Artifact
|
|
||||||
- name: Download Zipped Artifact
|
|
||||||
id: download
|
|
||||||
env:
|
|
||||||
ZIP_ID: ${{ steps.upload.outputs.artifact-id }}
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
run: |
|
||||||
curl -L -H "Authorization: Bearer $GITHUB_TOKEN" -L \
|
CLI_BINARY="Sources/Packages/.build/release/SecretiveCLI"
|
||||||
https://api.github.com/repos/maxgoedjen/secretive/actions/artifacts/$ZIP_ID/zip > Secretive.zip
|
ENTITLEMENTS="Sources/Packages/Sources/SecretiveCLI/SecretiveCLI.entitlements"
|
||||||
|
IDENTITY=$(security find-identity -p codesigning -v 2>/dev/null | grep "Developer ID Application" | head -n1 | awk -F'"' '{print $2}')
|
||||||
|
codesign --force --options runtime --sign "$IDENTITY" --identifier "com.maxgoedjen.Secretive.Host" --entitlements "$ENTITLEMENTS" "$CLI_BINARY"
|
||||||
|
- name: Prepare Artifact Folder
|
||||||
|
run: |
|
||||||
|
mkdir -p Artifact/App
|
||||||
|
mkdir -p Artifact/CLI
|
||||||
|
cp -r Archive.xcarchive/Products/Applications/Secretive.app Artifact/App/
|
||||||
|
cp Sources/Packages/.build/release/SecretiveCLI Artifact/CLI/secretive
|
||||||
|
- name: Build Installer Package
|
||||||
|
run: |
|
||||||
|
pkgbuild --root Artifact/App --install-location /Applications --identifier com.maxgoedjen.Secretive.app --version 1.0 App.pkg
|
||||||
|
pkgbuild --root Artifact/CLI --install-location /usr/local/bin --identifier com.maxgoedjen.Secretive.cli --version 1.0 CLI.pkg
|
||||||
|
cat > distribution.xml << 'EOF'
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<installer-gui-script minSpecVersion="2">
|
||||||
|
<title>Secretive</title>
|
||||||
|
<organization>com.maxgoedjen</organization>
|
||||||
|
<domains enable_localSystem="true"/>
|
||||||
|
<options customize="never" require-scripts="false" rootVolumeOnly="true"/>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.app"/>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.cli"/>
|
||||||
|
<choices-outline>
|
||||||
|
<line choice="default">
|
||||||
|
<line choice="com.maxgoedjen.Secretive.app"/>
|
||||||
|
<line choice="com.maxgoedjen.Secretive.cli"/>
|
||||||
|
</line>
|
||||||
|
</choices-outline>
|
||||||
|
<choice id="default"/>
|
||||||
|
<choice id="com.maxgoedjen.Secretive.app" visible="false">
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.app"/>
|
||||||
|
</choice>
|
||||||
|
<choice id="com.maxgoedjen.Secretive.cli" visible="false">
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.cli"/>
|
||||||
|
</choice>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.app" version="1.0" onConclusion="none">App.pkg</pkg-ref>
|
||||||
|
<pkg-ref id="com.maxgoedjen.Secretive.cli" version="1.0" onConclusion="none">CLI.pkg</pkg-ref>
|
||||||
|
</installer-gui-script>
|
||||||
|
EOF
|
||||||
|
productbuild --distribution distribution.xml --package-path . Secretive-unsigned.pkg
|
||||||
|
INSTALLER_IDENTITY=$(security find-identity -p basic -v 2>/dev/null | grep "Developer ID Installer" | head -n1 | awk -F'"' '{print $2}')
|
||||||
|
productsign --sign "$INSTALLER_IDENTITY" Secretive-unsigned.pkg Secretive.pkg
|
||||||
- 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 --wait Secretive.pkg
|
||||||
|
xcrun stapler staple Secretive.pkg
|
||||||
|
- name: Upload Installer to Artifacts
|
||||||
|
id: upload
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: Secretive.pkg
|
||||||
|
path: Secretive.pkg
|
||||||
- name: Attest
|
- name: Attest
|
||||||
id: attest
|
id: attest
|
||||||
uses: actions/attest-build-provenance@v2
|
uses: actions/attest-build-provenance@v2
|
||||||
with:
|
with:
|
||||||
subject-path: "Secretive.zip"
|
subject-path: "Secretive.pkg"
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -95,4 +164,4 @@ jobs:
|
|||||||
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 $TAG_NAME Secretive.zip
|
gh release upload $TAG_NAME Secretive.pkg
|
||||||
|
|||||||
89
Makefile
Normal file
89
Makefile
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# Creates a dev package containing the Secretive app and CLI
|
||||||
|
# Usage: make
|
||||||
|
|
||||||
|
PROJECT_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
|
||||||
|
BUILD_DIR := $(PROJECT_DIR)/build
|
||||||
|
|
||||||
|
ARCHIVE := $(BUILD_DIR)/Archive.xcarchive
|
||||||
|
APP_BUNDLE := $(BUILD_DIR)/Secretive.app
|
||||||
|
APP_ROOT := $(BUILD_DIR)/AppPayload
|
||||||
|
APP_PKG := $(BUILD_DIR)/App.pkg
|
||||||
|
CLI_BIN := $(BUILD_DIR)/SecretiveCLI
|
||||||
|
CLI_ROOT := $(BUILD_DIR)/CLIPayload
|
||||||
|
CLI_PKG := $(BUILD_DIR)/CLI.pkg
|
||||||
|
DIST := $(BUILD_DIR)/distribution.xml
|
||||||
|
FINAL_PKG := $(BUILD_DIR)/Secretive-dev-unsigned.pkg
|
||||||
|
|
||||||
|
XCODEBUILD := xcodebuild -project $(PROJECT_DIR)/Sources/Secretive.xcodeproj
|
||||||
|
|
||||||
|
.PHONY: all clean
|
||||||
|
|
||||||
|
all: $(FINAL_PKG)
|
||||||
|
@echo "Built: $(FINAL_PKG)"
|
||||||
|
|
||||||
|
$(ARCHIVE):
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
$(XCODEBUILD) -scheme Secretive -configuration Release CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" -archivePath $(ARCHIVE) archive
|
||||||
|
|
||||||
|
$(APP_BUNDLE): $(ARCHIVE)
|
||||||
|
@rm -rf $(APP_BUNDLE)
|
||||||
|
cp -R $(ARCHIVE)/Products/Applications/Secretive.app $(APP_BUNDLE)
|
||||||
|
|
||||||
|
$(CLI_BIN):
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
cd $(PROJECT_DIR)/Sources/Packages && xcodebuild -scheme SecretiveCLI -configuration Release \
|
||||||
|
-destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY="" \
|
||||||
|
SYMROOT=$(BUILD_DIR)/xcode-cli build
|
||||||
|
cp $(BUILD_DIR)/xcode-cli/Release/SecretiveCLI $(CLI_BIN)
|
||||||
|
cp -R $(BUILD_DIR)/xcode-cli/Release/*.bundle $(BUILD_DIR)/ 2>/dev/null || true
|
||||||
|
|
||||||
|
$(APP_ROOT): $(APP_BUNDLE)
|
||||||
|
@rm -rf $(APP_ROOT)
|
||||||
|
@mkdir -p $(APP_ROOT)
|
||||||
|
cp -R $(APP_BUNDLE) $(APP_ROOT)/
|
||||||
|
|
||||||
|
$(CLI_ROOT): $(CLI_BIN)
|
||||||
|
@rm -rf $(CLI_ROOT)
|
||||||
|
@mkdir -p $(CLI_ROOT)
|
||||||
|
cp $(CLI_BIN) $(CLI_ROOT)/secretive
|
||||||
|
|
||||||
|
$(APP_PKG): $(APP_ROOT)
|
||||||
|
pkgbuild --root $(APP_ROOT) --install-location /Applications --identifier com.maxgoedjen.Secretive.app --version 0.0.0-dev $(APP_PKG)
|
||||||
|
|
||||||
|
$(CLI_PKG): $(CLI_ROOT)
|
||||||
|
pkgbuild --root $(CLI_ROOT) --install-location /usr/local/bin --identifier com.maxgoedjen.Secretive.cli --version 0.0.0-dev $(CLI_PKG)
|
||||||
|
|
||||||
|
$(DIST):
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
@printf '%s\n' \
|
||||||
|
'<?xml version="1.0" encoding="utf-8"?>' \
|
||||||
|
'<installer-gui-script minSpecVersion="2">' \
|
||||||
|
' <title>Secretive (Dev)</title>' \
|
||||||
|
' <organization>com.maxgoedjen</organization>' \
|
||||||
|
' <domains enable_localSystem="true"/>' \
|
||||||
|
' <options customize="never" require-scripts="false" rootVolumeOnly="true"/>' \
|
||||||
|
' <pkg-ref id="com.maxgoedjen.Secretive.app"/>' \
|
||||||
|
' <pkg-ref id="com.maxgoedjen.Secretive.cli"/>' \
|
||||||
|
' <choices-outline>' \
|
||||||
|
' <line choice="default">' \
|
||||||
|
' <line choice="com.maxgoedjen.Secretive.app"/>' \
|
||||||
|
' <line choice="com.maxgoedjen.Secretive.cli"/>' \
|
||||||
|
' </line>' \
|
||||||
|
' </choices-outline>' \
|
||||||
|
' <choice id="default"/>' \
|
||||||
|
' <choice id="com.maxgoedjen.Secretive.app" visible="false">' \
|
||||||
|
' <pkg-ref id="com.maxgoedjen.Secretive.app"/>' \
|
||||||
|
' </choice>' \
|
||||||
|
' <choice id="com.maxgoedjen.Secretive.cli" visible="false">' \
|
||||||
|
' <pkg-ref id="com.maxgoedjen.Secretive.cli"/>' \
|
||||||
|
' </choice>' \
|
||||||
|
' <pkg-ref id="com.maxgoedjen.Secretive.app" version="0.0.0-dev" onConclusion="none">App.pkg</pkg-ref>' \
|
||||||
|
' <pkg-ref id="com.maxgoedjen.Secretive.cli" version="0.0.0-dev" onConclusion="none">CLI.pkg</pkg-ref>' \
|
||||||
|
'</installer-gui-script>' \
|
||||||
|
> $(DIST)
|
||||||
|
|
||||||
|
$(FINAL_PKG): $(APP_PKG) $(CLI_PKG) $(DIST)
|
||||||
|
productbuild --distribution $(DIST) --package-path $(BUILD_DIR) $(FINAL_PKG)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
@ -46,15 +46,21 @@ let package = Package(
|
|||||||
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
|
.target(
|
||||||
|
name: "Localizations",
|
||||||
|
dependencies: [],
|
||||||
|
path: "Sources/SecretiveCLI/Generated",
|
||||||
|
swiftSettings: swiftSettings,
|
||||||
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "SecureEnclaveSecretKit",
|
name: "SecureEnclaveSecretKit",
|
||||||
dependencies: ["SecretKit"],
|
dependencies: ["SecretKit", "Localizations"],
|
||||||
resources: [localization],
|
resources: [localization],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
.target(
|
.target(
|
||||||
name: "SmartCardSecretKit",
|
name: "SmartCardSecretKit",
|
||||||
dependencies: ["SecretKit"],
|
dependencies: ["SecretKit", "Localizations"],
|
||||||
resources: [localization],
|
resources: [localization],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
@ -93,9 +99,12 @@ let package = Package(
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
"SecretAgentKit",
|
"SecretAgentKit",
|
||||||
"SecureEnclaveSecretKit",
|
"SecureEnclaveSecretKit",
|
||||||
|
"SmartCardSecretKit",
|
||||||
"SecretKit",
|
"SecretKit",
|
||||||
"Common",
|
"Common",
|
||||||
|
"Localizations",
|
||||||
],
|
],
|
||||||
|
exclude: ["Generated"],
|
||||||
swiftSettings: swiftSettings,
|
swiftSettings: swiftSettings,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright Anysphere Inc.
|
||||||
|
// Localization extensions for String.LocalizationValue
|
||||||
|
// These are normally generated by Xcode from String Catalogs, but SPM doesn't do this automatically.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public extension String.LocalizationValue {
|
||||||
|
|
||||||
|
// MARK: - Store Names
|
||||||
|
|
||||||
|
static var secureEnclave: String.LocalizationValue {
|
||||||
|
"secure_enclave"
|
||||||
|
}
|
||||||
|
|
||||||
|
static var smartCard: String.LocalizationValue {
|
||||||
|
"smart_card"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Secret Names
|
||||||
|
|
||||||
|
static var unnamedSecret: String.LocalizationValue {
|
||||||
|
"unnamed_secret"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Authentication Context
|
||||||
|
|
||||||
|
static var authContextRequestDenyButton: String.LocalizationValue {
|
||||||
|
"auth_context_request_deny_button"
|
||||||
|
}
|
||||||
|
|
||||||
|
static func authContextRequestSignatureDescription(appName: String, secretName: String) -> String.LocalizationValue {
|
||||||
|
"auth_context_request_signature_description \(appName) \(secretName)"
|
||||||
|
}
|
||||||
|
|
||||||
|
static func authContextPersistForDuration(secretName: String, duration: String) -> String.LocalizationValue {
|
||||||
|
"auth_context_persist_for_duration \(secretName) \(duration)"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,23 +2,31 @@
|
|||||||
|
|
||||||
A command-line interface for Secretive that provides full key management and SSH agent functionality, sharing the same keychain and socket path as the GUI application.
|
A command-line interface for Secretive that provides full key management and SSH agent functionality, sharing the same keychain and socket path as the GUI application.
|
||||||
|
|
||||||
## Building
|
## Installation
|
||||||
|
|
||||||
Build the CLI using Swift Package Manager:
|
The CLI is distributed as part of the Secretive installer package (`.pkg`). When you install Secretive via the pkg installer, the CLI binary is automatically installed to `/usr/local/bin/secretive`.
|
||||||
|
|
||||||
|
Download the latest release from [GitHub Releases](https://github.com/maxgoedjen/secretive/releases).
|
||||||
|
|
||||||
|
## Building (Development)
|
||||||
|
|
||||||
|
For local development, build the CLI using Swift Package Manager:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
swift build -c release --product SecretiveCLI
|
swift build -c release --product SecretiveCLI --package-path Sources/Packages
|
||||||
```
|
```
|
||||||
|
|
||||||
The binary will be located at `.build/release/SecretiveCLI`.
|
The binary will be located at `Sources/Packages/.build/release/SecretiveCLI`.
|
||||||
|
|
||||||
## Code Signing
|
**Note:** Production builds and code signing are handled automatically by GitHub Actions. See `.github/workflows/release.yml` and `.github/workflows/nightly.yml` for details.
|
||||||
|
|
||||||
To use the CLI with the same keychain access as the GUI app, you must sign it with the same bundle identifier and entitlements.
|
## Code Signing (Development)
|
||||||
|
|
||||||
|
For local development with keychain access, you must sign the CLI with the same bundle identifier and entitlements as the GUI app.
|
||||||
|
|
||||||
### Required Entitlements
|
### Required Entitlements
|
||||||
|
|
||||||
The CLI must be signed with entitlements that include the same keychain access group as the GUI app. Create an entitlements file (e.g., `SecretiveCLI.entitlements`) with:
|
The CLI uses the entitlements file at `SecretiveCLI.entitlements`:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
@ -44,8 +52,8 @@ codesign --force \
|
|||||||
--sign "Developer ID Application: YOUR_TEAM_NAME" \
|
--sign "Developer ID Application: YOUR_TEAM_NAME" \
|
||||||
--options runtime \
|
--options runtime \
|
||||||
--identifier com.maxgoedjen.Secretive.Host \
|
--identifier com.maxgoedjen.Secretive.Host \
|
||||||
--entitlements SecretiveCLI.entitlements \
|
--entitlements Sources/Packages/Sources/SecretiveCLI/SecretiveCLI.entitlements \
|
||||||
.build/release/SecretiveCLI
|
Sources/Packages/.build/release/SecretiveCLI
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `YOUR_TEAM_NAME` with your actual Developer ID or use your team's signing identity.
|
Replace `YOUR_TEAM_NAME` with your actual Developer ID or use your team's signing identity.
|
||||||
@ -60,22 +68,22 @@ Install and manage the SSH agent as a launchd service:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install the agent as a launchd service
|
# Install the agent as a launchd service
|
||||||
secretive-cli agent install
|
secretive agent install
|
||||||
|
|
||||||
# Start the agent
|
# Start the agent
|
||||||
secretive-cli agent start
|
secretive agent start
|
||||||
|
|
||||||
# Check agent status
|
# Check agent status
|
||||||
secretive-cli agent status
|
secretive agent status
|
||||||
|
|
||||||
# Stop the agent
|
# Stop the agent
|
||||||
secretive-cli agent stop
|
secretive agent stop
|
||||||
|
|
||||||
# Uninstall the agent
|
# Uninstall the agent
|
||||||
secretive-cli agent uninstall
|
secretive agent uninstall
|
||||||
|
|
||||||
# Run agent in foreground (for testing)
|
# Run agent in foreground (for testing)
|
||||||
secretive-cli agent run
|
secretive agent run
|
||||||
```
|
```
|
||||||
|
|
||||||
### Key Management
|
### Key Management
|
||||||
@ -84,19 +92,19 @@ Manage SSH keys stored in the Secure Enclave:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate a new key
|
# Generate a new key
|
||||||
secretive-cli key generate "My Key Name"
|
secretive key generate "My Key Name"
|
||||||
|
|
||||||
# List all keys
|
# List all keys
|
||||||
secretive-cli key list
|
secretive key list
|
||||||
|
|
||||||
# Show public key for a specific key
|
# Show public key for a specific key
|
||||||
secretive-cli key show "My Key Name"
|
secretive key show "My Key Name"
|
||||||
|
|
||||||
# Delete a key
|
# Delete a key
|
||||||
secretive-cli key delete "My Key Name"
|
secretive key delete "My Key Name"
|
||||||
|
|
||||||
# Update key (note: attributes cannot be changed after creation)
|
# Update key (note: attributes cannot be changed after creation)
|
||||||
secretive-cli key update "My Key Name"
|
secretive key update "My Key Name"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Socket Path
|
## Socket Path
|
||||||
@ -126,4 +134,3 @@ This is achieved by signing the CLI with the same bundle identifier (`com.maxgoe
|
|||||||
- Keys created via CLI will appear in the GUI app and vice versa
|
- Keys created via CLI will appear in the GUI app and vice versa
|
||||||
- The agent can be run either via launchd (recommended) or in foreground mode for testing
|
- The agent can be run either via launchd (recommended) or in foreground mode for testing
|
||||||
- All key operations require appropriate authentication (Touch ID, Apple Watch, or password) as configured per key
|
- All key operations require appropriate authentication (Touch ID, Apple Watch, or password) as configured per key
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.smartcard</key>
|
||||||
|
<true/>
|
||||||
|
<key>keychain-access-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(AppIdentifierPrefix)com.maxgoedjen.Secretive</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
||||||
@ -231,23 +231,25 @@ extension SecretiveCLI {
|
|||||||
logger.info("SSH agent listening on \(socketPath)")
|
logger.info("SSH agent listening on \(socketPath)")
|
||||||
print("SSH agent running on \(socketPath)")
|
print("SSH agent running on \(socketPath)")
|
||||||
print("Set SSH_AUTH_SOCK=\(socketPath) to use this agent")
|
print("Set SSH_AUTH_SOCK=\(socketPath) to use this agent")
|
||||||
|
|
||||||
|
func handleSession(_ session: SocketController.Session) async {
|
||||||
|
do {
|
||||||
|
for await message in session.messages {
|
||||||
|
let request = try parser.parse(data: message)
|
||||||
|
let response = await agent.handle(request: request, provenance: session.provenance)
|
||||||
|
try await MainActor.run {
|
||||||
|
try session.write(response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logger.error("Session error: \(error.localizedDescription)")
|
||||||
|
try? session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle sessions
|
// Handle sessions
|
||||||
for await session in socket.sessions {
|
for await session in socket.sessions {
|
||||||
Task {
|
await handleSession(session)
|
||||||
do {
|
|
||||||
for await message in session.messages {
|
|
||||||
let request = try parser.parse(data: message)
|
|
||||||
let response = await agent.handle(request: request, provenance: session.provenance)
|
|
||||||
try await MainActor.run {
|
|
||||||
try session.write(response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
logger.error("Session error: \(error.localizedDescription)")
|
|
||||||
try? session.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user