Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cdc40774c4 | |||
| 5847294a80 | |||
| 343481d053 | |||
| 13a3dc4890 | |||
| 526dedd653 | |||
| 9b3ac8c31c | |||
| cd2ba37db6 | |||
| 7be2ef27b9 | |||
| b7e4931e64 | |||
| a3bfd42759 | |||
| 1587db4edf | |||
| a516f0570d | |||
| 7cbdc3cdb7 | |||
| 7f27ee1b4e | |||
| 4f9c73e90f | |||
| e3359320fc | |||
| 9073a51787 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -32,6 +32,9 @@ playground.xcworkspace
|
|||||||
|
|
||||||
.build/
|
.build/
|
||||||
|
|
||||||
|
# Generated by xcodegen in CI — do not commit
|
||||||
|
*.xcodeproj/
|
||||||
|
|
||||||
# CocoaPods
|
# CocoaPods
|
||||||
#
|
#
|
||||||
# We recommend against adding the Pods directory to your .gitignore. However
|
# We recommend against adding the Pods directory to your .gitignore. However
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ final class AppEnvironment: ObservableObject {
|
|||||||
let apiClient: SilverAPIClient
|
let apiClient: SilverAPIClient
|
||||||
let mdmService: MDMEnrollmentService
|
let mdmService: MDMEnrollmentService
|
||||||
|
|
||||||
private var serverUrl: String {
|
var serverUrl: String {
|
||||||
UserDefaults.standard.string(forKey: "silverapple.serverUrl") ?? ""
|
UserDefaults.standard.string(forKey: "silverapple.serverUrl") ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ final class PassportAuthService: NSObject, ObservableObject, ASWebAuthentication
|
|||||||
|
|
||||||
let authUrl = buildAuthURL(challenge: challenge)
|
let authUrl = buildAuthURL(challenge: challenge)
|
||||||
|
|
||||||
let callbackUrl = try await withCheckedThrowingContinuation { continuation in
|
let callbackUrl: URL = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<URL, any Error>) in
|
||||||
let session = ASWebAuthenticationSession(url: authUrl, callbackURLScheme: "silverapple") { url, error in
|
let session = ASWebAuthenticationSession(url: authUrl, callbackURLScheme: "silverapple") { url, error in
|
||||||
if let error { continuation.resume(throwing: error); return }
|
if let error { continuation.resume(throwing: error); return }
|
||||||
guard let url else { continuation.resume(throwing: AuthError.noCallback); return }
|
guard let url else { continuation.resume(throwing: AuthError.noCallback); return }
|
||||||
|
|||||||
16
SilverApple/Hardening/Views/HardeningListView.swift
Normal file
16
SilverApple/Hardening/Views/HardeningListView.swift
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct HardeningListView: View {
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
List {
|
||||||
|
Section {
|
||||||
|
Label("Privacy hardening checks coming soon.", systemImage: "lock.shield")
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.font(.subheadline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Hardening")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ struct MDMEnrollmentView: View {
|
|||||||
VStack(spacing: 32) {
|
VStack(spacing: 32) {
|
||||||
Image(systemName: "iphone.badge.play")
|
Image(systemName: "iphone.badge.play")
|
||||||
.font(.system(size: 56))
|
.font(.system(size: 56))
|
||||||
.foregroundStyle(.accentColor)
|
.foregroundStyle(Color.accentColor)
|
||||||
|
|
||||||
VStack(spacing: 12) {
|
VStack(spacing: 12) {
|
||||||
Text("Device Management")
|
Text("Device Management")
|
||||||
|
|||||||
29
SilverApple/Settings/Views/ServerConfigView.swift
Normal file
29
SilverApple/Settings/Views/ServerConfigView.swift
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ServerConfigView: View {
|
||||||
|
@EnvironmentObject var env: AppEnvironment
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
List {
|
||||||
|
Section("Server") {
|
||||||
|
HStack {
|
||||||
|
Text("URL")
|
||||||
|
Spacer()
|
||||||
|
Text(env.serverUrl.isEmpty ? "Not configured" : env.serverUrl)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
.truncationMode(.middle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Button("Sign Out", role: .destructive) {
|
||||||
|
env.authService.signOut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Settings")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,13 +11,23 @@
|
|||||||
"name": "SilverApple",
|
"name": "SilverApple",
|
||||||
"bundleIdentifier": "uk.silverlabs.silverapple",
|
"bundleIdentifier": "uk.silverlabs.silverapple",
|
||||||
"developerName": "SilverLABS",
|
"developerName": "SilverLABS",
|
||||||
"downloadURL": "",
|
"downloadURL": "https://git.silverlabs.uk/SilverLABS/SilverApple/releases/download/v0.1.1/SilverApple.ipa",
|
||||||
"subtitle": "Privacy companion for iOS",
|
"subtitle": "Privacy companion for iOS",
|
||||||
"localizedDescription": "SilverApple integrates your device with the SilverLABS ecosystem — MDM enrollment, privacy hardening checks, and Passport authentication.",
|
"localizedDescription": "SilverApple integrates your device with the SilverLABS ecosystem — MDM enrollment, privacy hardening checks, and Passport authentication.",
|
||||||
"iconURL": "https://git.silverlabs.uk/SilverLABS/SilverApple/raw/branch/main/SilverApple/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png",
|
"iconURL": "https://git.silverlabs.uk/SilverLABS/SilverApple/raw/branch/main/SilverApple/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png",
|
||||||
"tintColor": "00BBFF",
|
"tintColor": "00BBFF",
|
||||||
"category": "utilities",
|
"category": "utilities",
|
||||||
"versions": []
|
"versions": [
|
||||||
|
{
|
||||||
|
"version": "0.1.1",
|
||||||
|
"buildVersion": "0.1.1",
|
||||||
|
"date": "2026-04-06T21:22:41Z",
|
||||||
|
"localizedDescription": "See release notes.",
|
||||||
|
"downloadURL": "https://git.silverlabs.uk/SilverLABS/SilverApple/releases/download/v0.1.1/SilverApple.ipa",
|
||||||
|
"sha256": "fa2004dee56567a11b134461bc3736417a2f84f015bf2944e6872fda020e0399",
|
||||||
|
"size": 91467
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"news": []
|
"news": []
|
||||||
|
|||||||
107
codemagic.yaml
107
codemagic.yaml
@@ -13,39 +13,85 @@ workflows:
|
|||||||
|
|
||||||
environment:
|
environment:
|
||||||
xcode: latest
|
xcode: latest
|
||||||
|
groups:
|
||||||
|
- SLABS
|
||||||
|
- APPLE_SIGNING
|
||||||
vars:
|
vars:
|
||||||
BUNDLE_ID: uk.silverlabs.silverapple
|
BUNDLE_ID: uk.silverlabs.silverapple
|
||||||
GITEA_REPO: SilverLABS/SilverApple
|
GITEA_REPO: SilverLABS/SilverApple
|
||||||
GITEA_API: https://git.silverlabs.uk/api/v1
|
GITEA_API: https://git.silverlabs.uk/api/v1
|
||||||
# GITEA_TOKEN — set this in Codemagic App Settings → Environment variables (mark as secret)
|
|
||||||
|
|
||||||
scripts:
|
scripts:
|
||||||
- name: Build archive (unsigned)
|
- name: Generate Xcode project
|
||||||
|
script: |
|
||||||
|
brew install xcodegen
|
||||||
|
xcodegen generate --spec project.yml
|
||||||
|
|
||||||
|
- name: Initialize keychain
|
||||||
|
script: keychain initialize
|
||||||
|
|
||||||
|
- name: Install signing certificate
|
||||||
|
script: |
|
||||||
|
echo $CM_CERTIFICATE | base64 --decode > /tmp/cert.p12
|
||||||
|
keychain add-certificates \
|
||||||
|
--certificate /tmp/cert.p12 \
|
||||||
|
--certificate-password $CM_CERTIFICATE_PASSWORD
|
||||||
|
|
||||||
|
- name: Install provisioning profile
|
||||||
|
script: |
|
||||||
|
echo $CM_PROVISIONING_PROFILE | base64 --decode > /tmp/profile.mobileprovision
|
||||||
|
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||||
|
cp /tmp/profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
|
||||||
|
|
||||||
|
- name: Configure Xcode signing
|
||||||
|
script: xcode-project use-profiles
|
||||||
|
|
||||||
|
- name: Build (signed)
|
||||||
script: |
|
script: |
|
||||||
xcodebuild archive \
|
xcodebuild archive \
|
||||||
|
-project SilverApple.xcodeproj \
|
||||||
-scheme SilverApple \
|
-scheme SilverApple \
|
||||||
-destination "generic/platform=iOS" \
|
-destination "generic/platform=iOS" \
|
||||||
|
-configuration Release \
|
||||||
-archivePath "$CM_BUILD_DIR/SilverApple.xcarchive" \
|
-archivePath "$CM_BUILD_DIR/SilverApple.xcarchive" \
|
||||||
CODE_SIGNING_ALLOWED=NO \
|
DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
|
||||||
CODE_SIGNING_REQUIRED=NO \
|
CODE_SIGN_STYLE=Manual
|
||||||
CODE_SIGN_IDENTITY="" \
|
|
||||||
PROVISIONING_PROFILE=""
|
|
||||||
|
|
||||||
- name: Package IPA
|
- name: Export signed IPA
|
||||||
script: |
|
script: |
|
||||||
cd "$CM_BUILD_DIR"
|
cat > /tmp/ExportOptions.plist << 'EOF'
|
||||||
mkdir -p Payload
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
cp -r SilverApple.xcarchive/Products/Applications/SilverApple.app Payload/
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
zip -r SilverApple.ipa Payload/
|
<plist version="1.0">
|
||||||
rm -rf Payload
|
<dict>
|
||||||
echo "IPA size: $(du -sh SilverApple.ipa | cut -f1)"
|
<key>method</key>
|
||||||
|
<string>ad-hoc</string>
|
||||||
|
<key>signingStyle</key>
|
||||||
|
<string>manual</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
EOF
|
||||||
|
xcodebuild -exportArchive \
|
||||||
|
-archivePath "$CM_BUILD_DIR/SilverApple.xcarchive" \
|
||||||
|
-exportPath "$CM_BUILD_DIR/export" \
|
||||||
|
-exportOptionsPlist /tmp/ExportOptions.plist
|
||||||
|
cp "$CM_BUILD_DIR/export/SilverApple.ipa" "$CM_BUILD_DIR/SilverApple.ipa"
|
||||||
|
|
||||||
- name: Publish to Gitea
|
- name: Publish to Gitea
|
||||||
script: |
|
script: |
|
||||||
set -e
|
set -exo pipefail
|
||||||
TAG="$CM_TAG"
|
[ -n "$GITEA_TOKEN" ] || { echo "ERROR: GITEA_TOKEN is not set in Codemagic env vars"; exit 1; }
|
||||||
|
echo "TAG=$CM_TAG BUILD_DIR=$CM_BUILD_DIR"
|
||||||
|
echo "GITEA_API=$GITEA_API TOKEN_SET=yes"
|
||||||
|
|
||||||
|
TAG="${CM_TAG:-$(git tag --points-at HEAD | head -1)}"
|
||||||
|
[ -n "$TAG" ] || { echo "ERROR: could not determine tag"; exit 1; }
|
||||||
VERSION="${TAG#v}"
|
VERSION="${TAG#v}"
|
||||||
IPA="$CM_BUILD_DIR/SilverApple.ipa"
|
IPA="$CM_BUILD_DIR/SilverApple.ipa"
|
||||||
|
|
||||||
|
# Verify IPA exists
|
||||||
|
ls -lh "$IPA"
|
||||||
|
|
||||||
SHA256=$(shasum -a 256 "$IPA" | awk '{print $1}')
|
SHA256=$(shasum -a 256 "$IPA" | awk '{print $1}')
|
||||||
SIZE=$(wc -c < "$IPA" | tr -d ' ')
|
SIZE=$(wc -c < "$IPA" | tr -d ' ')
|
||||||
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
@@ -64,27 +110,36 @@ workflows:
|
|||||||
altstore-source.json > altstore-source.tmp.json
|
altstore-source.json > altstore-source.tmp.json
|
||||||
mv altstore-source.tmp.json altstore-source.json
|
mv altstore-source.tmp.json altstore-source.json
|
||||||
|
|
||||||
# Commit and push updated source
|
# Get current file SHA from Gitea API
|
||||||
git remote set-url origin "https://sysadmin:$GITEA_TOKEN@git.silverlabs.uk/$GITEA_REPO.git"
|
FILE_SHA=$(curl -s \
|
||||||
git config user.name "Codemagic"
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
git config user.email "ci@silverlabs.uk"
|
"$GITEA_API/repos/$GITEA_REPO/contents/altstore-source.json" \
|
||||||
git add altstore-source.json
|
| jq -r '.sha')
|
||||||
git commit -m "chore(altstore): update source for $TAG" || echo "No changes"
|
echo "FILE_SHA=$FILE_SHA"
|
||||||
git push origin HEAD:main
|
|
||||||
|
# Update altstore-source.json via Gitea contents API
|
||||||
|
CONTENT=$(base64 -i altstore-source.json | tr -d '\n')
|
||||||
|
curl -s -X PUT "$GITEA_API/repos/$GITEA_REPO/contents/altstore-source.json" \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"message\":\"chore(altstore): update source for $TAG\",\"content\":\"$CONTENT\",\"sha\":\"$FILE_SHA\",\"branch\":\"main\"}" \
|
||||||
|
| jq -r '.commit.sha // "FAILED"'
|
||||||
|
|
||||||
# Create Gitea release
|
# Create Gitea release
|
||||||
RELEASE_ID=$(curl -sf -X POST "$GITEA_API/repos/$GITEA_REPO/releases" \
|
RELEASE_ID=$(curl -s -X POST "$GITEA_API/repos/$GITEA_REPO/releases" \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"tag_name\":\"$TAG\",\"name\":\"SilverApple $TAG\",\"body\":\"AltStore / SideStore release.\",\"draft\":false,\"prerelease\":false}" \
|
-d "{\"tag_name\":\"$TAG\",\"name\":\"SilverApple $TAG\",\"body\":\"AltStore / SideStore release.\",\"draft\":false,\"prerelease\":false}" \
|
||||||
| jq -r '.id')
|
| jq -r '.id')
|
||||||
|
echo "RELEASE_ID=$RELEASE_ID"
|
||||||
|
|
||||||
# Upload IPA
|
# Upload IPA
|
||||||
curl -sf -X POST "$GITEA_API/repos/$GITEA_REPO/releases/$RELEASE_ID/assets" \
|
curl -s -X POST "$GITEA_API/repos/$GITEA_REPO/releases/$RELEASE_ID/assets" \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
-F "attachment=@$IPA;type=application/octet-stream"
|
-F "attachment=@$IPA;type=application/octet-stream" \
|
||||||
|
| jq -r '.name // "UPLOAD_FAILED"'
|
||||||
|
|
||||||
echo "Published release $TAG → $DOWNLOAD_URL"
|
echo "Done: $TAG → $DOWNLOAD_URL"
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- $CM_BUILD_DIR/SilverApple.ipa
|
- $CM_BUILD_DIR/SilverApple.ipa
|
||||||
|
|||||||
43
project.yml
Normal file
43
project.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: SilverApple
|
||||||
|
options:
|
||||||
|
bundleIdPrefix: uk.silverlabs
|
||||||
|
deploymentTarget:
|
||||||
|
iOS: "16.0"
|
||||||
|
xcodeVersion: "15"
|
||||||
|
generateEmptyDirectories: true
|
||||||
|
|
||||||
|
targets:
|
||||||
|
SilverApple:
|
||||||
|
type: application
|
||||||
|
platform: iOS
|
||||||
|
deploymentTarget: "16.0"
|
||||||
|
sources:
|
||||||
|
- path: SilverApple
|
||||||
|
excludes:
|
||||||
|
- Info.plist
|
||||||
|
- SilverApple.entitlements
|
||||||
|
resources:
|
||||||
|
- SilverApple/Assets.xcassets
|
||||||
|
settings:
|
||||||
|
base:
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER: uk.silverlabs.silverapple
|
||||||
|
INFOPLIST_FILE: SilverApple/Info.plist
|
||||||
|
CODE_SIGN_ENTITLEMENTS: SilverApple/SilverApple.entitlements
|
||||||
|
SWIFT_VERSION: "5.9"
|
||||||
|
TARGETED_DEVICE_FAMILY: "1,2"
|
||||||
|
OTHER_LDFLAGS:
|
||||||
|
- "-framework AuthenticationServices"
|
||||||
|
- "-framework SafariServices"
|
||||||
|
- "-framework Security"
|
||||||
|
|
||||||
|
SilverAppleTests:
|
||||||
|
type: bundle.unit-test
|
||||||
|
platform: iOS
|
||||||
|
deploymentTarget: "16.0"
|
||||||
|
sources:
|
||||||
|
- path: SilverAppleTests
|
||||||
|
dependencies:
|
||||||
|
- target: SilverApple
|
||||||
|
settings:
|
||||||
|
base:
|
||||||
|
SWIFT_VERSION: "5.9"
|
||||||
8
servers.json
Normal file
8
servers.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"name": "SilverLABS Anisette",
|
||||||
|
"address": "https://anisette.silverlabs.uk"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
xtool.yml
17
xtool.yml
@@ -1,13 +1,8 @@
|
|||||||
# xTool configuration — https://xtool.sh
|
# xTool configuration — https://xtool.sh
|
||||||
# Run `xtool configure` once to set up your Apple ID credentials and Darwin SDK.
|
# Run `xtool setup` once to configure your Apple ID credentials and extract the Darwin SDK.
|
||||||
# Then: `xtool run` to build and install directly to a connected device.
|
# Then: `xtool dev` to build and deploy directly to a connected USB device.
|
||||||
|
|
||||||
targets:
|
version: 1
|
||||||
SilverApple:
|
bundleID: uk.silverlabs.silverapple
|
||||||
bundleIdentifier: uk.silverlabs.silverapple
|
infoPath: SilverApple/Info.plist
|
||||||
# Fill in your Apple Developer Team ID (10-char string from developer.apple.com)
|
entitlementsPath: SilverApple/SilverApple.entitlements
|
||||||
teamID: ""
|
|
||||||
infoPlist: SilverApple/Info.plist
|
|
||||||
entitlements: SilverApple/SilverApple.entitlements
|
|
||||||
deploymentTarget: "16.0"
|
|
||||||
deviceFamily: [iphone]
|
|
||||||
|
|||||||
Reference in New Issue
Block a user