mirror of
https://github.com/SideStore/SideStore.git
synced 2026-03-29 23:05:39 +02:00
Compare commits
16 Commits
3927cbd438
...
cf0a174882
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf0a174882 | ||
|
|
8fc38300d5 | ||
|
|
5460fdfec5 | ||
|
|
ded65e30d6 | ||
|
|
b0fab7550f | ||
|
|
10154954f0 | ||
|
|
59892247a6 | ||
|
|
f350c9fa49 | ||
|
|
32fb3d706c | ||
|
|
232dec2c36 | ||
|
|
e9772afbf6 | ||
|
|
014d7b8346 | ||
|
|
8d90def5d8 | ||
|
|
e891b7ec93 | ||
|
|
e5dbf2b677 | ||
|
|
4ff35e36e4 |
55
.github/workflows/alpha.yml
vendored
55
.github/workflows/alpha.yml
vendored
@@ -1,28 +1,47 @@
|
|||||||
name: Alpha SideStore build
|
name: Alpha SideStore Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [develop-alpha]
|
||||||
- develop-alpha
|
|
||||||
|
|
||||||
# cancel duplicate run if from same branch
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.ref }}
|
group: ${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Reusable-build:
|
build:
|
||||||
uses: ./.github/workflows/reusable-sidestore-build.yml
|
runs-on: macos-15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
# bundle_id: "com.SideStore.SideStore.Alpha"
|
submodules: recursive
|
||||||
bundle_id: "com.SideStore.SideStore"
|
fetch-depth: 0
|
||||||
# bundle_id_suffix: ".Alpha"
|
|
||||||
is_beta: true
|
- run: brew install ldid xcbeautify
|
||||||
publish: ${{ vars.PUBLISH_ALPHA_UPDATES == 'true' }}
|
|
||||||
is_shared_build_num: false
|
- name: Shared
|
||||||
release_tag: "alpha"
|
id: shared
|
||||||
release_name: "Alpha"
|
run: python3 scripts/ci.py shared
|
||||||
upstream_tag: "nightly"
|
|
||||||
upstream_name: "Nightly"
|
- name: Beta bump
|
||||||
secrets:
|
env:
|
||||||
CROSS_REPO_PUSH_KEY: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
RELEASE_CHANNEL: alpha
|
||||||
|
run: python3 scripts/ci.py bump-beta
|
||||||
|
|
||||||
|
- name: Version
|
||||||
|
id: version
|
||||||
|
run: python3 scripts/ci.py version
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: python3 scripts/ci.py build
|
||||||
|
|
||||||
|
- name: Encrypt logs
|
||||||
|
env:
|
||||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
|
run: python3 scripts/ci.py encrypt-build
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||||
|
path: SideStore.ipa
|
||||||
176
.github/workflows/nightly.yml
vendored
176
.github/workflows/nightly.yml
vendored
@@ -1,82 +1,128 @@
|
|||||||
name: Nightly SideStore Build
|
name: Nightly SideStore Build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches: [develop]
|
||||||
- develop
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *' # Runs every night at midnight UTC
|
- cron: '0 0 * * *'
|
||||||
workflow_dispatch: # Allows manual trigger
|
workflow_dispatch:
|
||||||
|
|
||||||
# cancel duplicate run if from same branch
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.ref }}
|
group: ${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-changes:
|
build:
|
||||||
if: github.event_name == 'schedule'
|
runs-on: macos-26
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
has_changes: ${{ steps.check.outputs.has_changes }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- uses: actions/checkout@v4
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0 # Ensure full history
|
submodules: recursive
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Get last successful workflow run
|
- run: brew install ldid xcbeautify
|
||||||
id: get_last_success
|
|
||||||
run: |
|
|
||||||
LAST_SUCCESS=$(gh run list --workflow "Nightly SideStore Build" --json createdAt,conclusion \
|
|
||||||
--jq '[.[] | select(.conclusion=="success")][0].createdAt' || echo "")
|
|
||||||
echo "Last successful run: $LAST_SUCCESS"
|
|
||||||
echo "last_success=$LAST_SUCCESS" >> $GITHUB_ENV
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Check for new commits since last successful build
|
- name: Restore Xcode/SwiftPM Cache (Exact match)
|
||||||
id: check
|
id: xcode-cache-restore
|
||||||
run: |
|
uses: actions/cache/restore@v3
|
||||||
if [ -n "$LAST_SUCCESS" ]; then
|
|
||||||
NEW_COMMITS=$(git rev-list --count --since="$LAST_SUCCESS" origin/develop)
|
|
||||||
COMMIT_LOG=$(git log --since="$LAST_SUCCESS" --pretty=format:"%h %s" origin/develop)
|
|
||||||
else
|
|
||||||
NEW_COMMITS=1
|
|
||||||
COMMIT_LOG=$(git log -n 10 --pretty=format:"%h %s" origin/develop) # Show last 10 commits if no history
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Has changes: $NEW_COMMITS"
|
|
||||||
echo "New commits since last successful build:"
|
|
||||||
echo "$COMMIT_LOG"
|
|
||||||
|
|
||||||
if [ "$NEW_COMMITS" -gt 0 ]; then
|
|
||||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
LAST_SUCCESS: ${{ env.last_success }}
|
|
||||||
|
|
||||||
Reusable-build:
|
|
||||||
if: |
|
|
||||||
always() &&
|
|
||||||
(github.event_name == 'push' ||
|
|
||||||
(github.event_name == 'schedule' && needs.check-changes.result == 'success' && needs.check-changes.outputs.has_changes == 'true'))
|
|
||||||
needs: check-changes
|
|
||||||
uses: ./.github/workflows/reusable-sidestore-build.yml
|
|
||||||
with:
|
with:
|
||||||
# bundle_id: "com.SideStore.SideStore.Nightly"
|
path: |
|
||||||
bundle_id: "com.SideStore.SideStore"
|
~/Library/Developer/Xcode/DerivedData
|
||||||
# bundle_id_suffix: ".Nightly"
|
~/Library/Caches/org.swift.swiftpm
|
||||||
is_beta: true
|
key: xcode-cache-build-${{ github.ref_name }}-${{ github.sha }}
|
||||||
publish: ${{ vars.PUBLISH_NIGHTLY_UPDATES == 'true' }}
|
|
||||||
is_shared_build_num: false
|
- name: Restore Xcode/SwiftPM Cache (Last Available)
|
||||||
release_tag: "nightly"
|
id: xcode-cache-restore-recent
|
||||||
release_name: "Nightly"
|
uses: actions/cache/restore@v3
|
||||||
upstream_tag: "0.5.10"
|
with:
|
||||||
upstream_name: "Stable"
|
path: |
|
||||||
secrets:
|
~/Library/Developer/Xcode/DerivedData
|
||||||
CROSS_REPO_PUSH_KEY: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
~/Library/Caches/org.swift.swiftpm
|
||||||
|
key: xcode-cache-build-${{ github.ref_name }}-
|
||||||
|
|
||||||
|
- name: Short Commit SHA
|
||||||
|
id: shared
|
||||||
|
run: python3 scripts/ci.py commid-id
|
||||||
|
|
||||||
|
- name: Nightly Version bump
|
||||||
|
env:
|
||||||
|
RELEASE_CHANNEL: nightly
|
||||||
|
run: python3 scripts/ci.py bump-beta
|
||||||
|
|
||||||
|
- name: Version
|
||||||
|
id: version
|
||||||
|
run: python3 scripts/ci.py version
|
||||||
|
|
||||||
|
- name: Clean previous build artifacts
|
||||||
|
run: python3 scripts/ci.py clean
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: python3 scripts/ci.py build
|
||||||
|
|
||||||
|
- name: Tests Build
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||||
|
run: python3 scripts/ci.py tests-build
|
||||||
|
|
||||||
|
- name: Save Xcode & SwiftPM Cache
|
||||||
|
id: cache-save
|
||||||
|
if: ${{ steps.xcode-cache-restore.outputs.cache-hit != 'true' }}
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Developer/Xcode/DerivedData
|
||||||
|
~/Library/Caches/org.swift.swiftpm
|
||||||
|
key: xcode-cache-build-${{ github.ref_name }}-${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Tests Run
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||||
|
run: python3 scripts/ci.py tests-run
|
||||||
|
|
||||||
|
- name: Encrypt build logs
|
||||||
|
env:
|
||||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
|
run: python3 scripts/ci.py encrypt-build
|
||||||
|
|
||||||
|
- name: Encrypt tests-build logs
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||||
|
env:
|
||||||
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
|
run: python3 scripts/ci.py encrypt-tests-build
|
||||||
|
|
||||||
|
- name: Encrypt tests-run logs
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||||
|
env:
|
||||||
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
|
run: python3 scripts/ci.py encrypt-tests-run
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: encrypted-build-logs-${{ steps.version.outputs.version }}.zip
|
||||||
|
path: encrypted-build-logs.zip
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||||
|
with:
|
||||||
|
name: encrypted-tests-build-logs-${{ steps.shared.outputs.short_commit }}.zip
|
||||||
|
path: encrypted-tests-build-logs.zip
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||||
|
with:
|
||||||
|
name: encrypted-tests-run-logs-${{ steps.shared.outputs.short_commit }}.zip
|
||||||
|
path: encrypted-tests-run-logs.zip
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||||
|
path: SideStore.ipa
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: SideStore-${{ steps.version.outputs.version }}-dSYMs.zip
|
||||||
|
path: SideStore.dSYMs.zip
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
run: python3 scripts/ci.py deploy \
|
||||||
|
--release-tag nightly \
|
||||||
|
--release-name Nightly
|
||||||
28
.github/workflows/obsolete/alpha.yml
vendored
Normal file
28
.github/workflows/obsolete/alpha.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
name: Alpha SideStore build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop-alpha
|
||||||
|
|
||||||
|
# cancel duplicate run if from same branch
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Reusable-build:
|
||||||
|
uses: ./.github/workflows/reusable-sidestore-build.yml
|
||||||
|
with:
|
||||||
|
# bundle_id: "com.SideStore.SideStore.Alpha"
|
||||||
|
bundle_id: "com.SideStore.SideStore"
|
||||||
|
# bundle_id_suffix: ".Alpha"
|
||||||
|
is_beta: true
|
||||||
|
publish: ${{ vars.PUBLISH_ALPHA_UPDATES == 'true' }}
|
||||||
|
is_shared_build_num: false
|
||||||
|
release_tag: "alpha"
|
||||||
|
release_name: "Alpha"
|
||||||
|
upstream_tag: "nightly"
|
||||||
|
upstream_name: "Nightly"
|
||||||
|
secrets:
|
||||||
|
CROSS_REPO_PUSH_KEY: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||||
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
82
.github/workflows/obsolete/nightly.yml
vendored
Normal file
82
.github/workflows/obsolete/nightly.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
name: Nightly SideStore Build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *' # Runs every night at midnight UTC
|
||||||
|
workflow_dispatch: # Allows manual trigger
|
||||||
|
|
||||||
|
# cancel duplicate run if from same branch
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-changes:
|
||||||
|
if: github.event_name == 'schedule'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
has_changes: ${{ steps.check.outputs.has_changes }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # Ensure full history
|
||||||
|
|
||||||
|
- name: Get last successful workflow run
|
||||||
|
id: get_last_success
|
||||||
|
run: |
|
||||||
|
LAST_SUCCESS=$(gh run list --workflow "Nightly SideStore Build" --json createdAt,conclusion \
|
||||||
|
--jq '[.[] | select(.conclusion=="success")][0].createdAt' || echo "")
|
||||||
|
echo "Last successful run: $LAST_SUCCESS"
|
||||||
|
echo "last_success=$LAST_SUCCESS" >> $GITHUB_ENV
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Check for new commits since last successful build
|
||||||
|
id: check
|
||||||
|
run: |
|
||||||
|
if [ -n "$LAST_SUCCESS" ]; then
|
||||||
|
NEW_COMMITS=$(git rev-list --count --since="$LAST_SUCCESS" origin/develop)
|
||||||
|
COMMIT_LOG=$(git log --since="$LAST_SUCCESS" --pretty=format:"%h %s" origin/develop)
|
||||||
|
else
|
||||||
|
NEW_COMMITS=1
|
||||||
|
COMMIT_LOG=$(git log -n 10 --pretty=format:"%h %s" origin/develop) # Show last 10 commits if no history
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Has changes: $NEW_COMMITS"
|
||||||
|
echo "New commits since last successful build:"
|
||||||
|
echo "$COMMIT_LOG"
|
||||||
|
|
||||||
|
if [ "$NEW_COMMITS" -gt 0 ]; then
|
||||||
|
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
LAST_SUCCESS: ${{ env.last_success }}
|
||||||
|
|
||||||
|
Reusable-build:
|
||||||
|
if: |
|
||||||
|
always() &&
|
||||||
|
(github.event_name == 'push' ||
|
||||||
|
(github.event_name == 'schedule' && needs.check-changes.result == 'success' && needs.check-changes.outputs.has_changes == 'true'))
|
||||||
|
needs: check-changes
|
||||||
|
uses: ./.github/workflows/reusable-sidestore-build.yml
|
||||||
|
with:
|
||||||
|
# bundle_id: "com.SideStore.SideStore.Nightly"
|
||||||
|
bundle_id: "com.SideStore.SideStore"
|
||||||
|
# bundle_id_suffix: ".Nightly"
|
||||||
|
is_beta: true
|
||||||
|
publish: ${{ vars.PUBLISH_NIGHTLY_UPDATES == 'true' }}
|
||||||
|
is_shared_build_num: false
|
||||||
|
release_tag: "nightly"
|
||||||
|
release_name: "Nightly"
|
||||||
|
upstream_tag: "0.5.10"
|
||||||
|
upstream_name: "Stable"
|
||||||
|
secrets:
|
||||||
|
CROSS_REPO_PUSH_KEY: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||||
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -76,7 +76,7 @@ final class LaunchViewController: UIViewController, UIDocumentPickerDelegate {
|
|||||||
detectAndImportAccountFile()
|
detectAndImportAccountFile()
|
||||||
|
|
||||||
if UserDefaults.standard.enableEMPforWireguard {
|
if UserDefaults.standard.enableEMPforWireguard {
|
||||||
start_em_proxy(bind_addr: AppConstants.Proxy.serverURL)
|
startEMProxy(bind_addr: AppConstants.Proxy.serverURL)
|
||||||
}
|
}
|
||||||
guard let pf = fetchPairingFile() else {
|
guard let pf = fetchPairingFile() else {
|
||||||
displayError("Device pairing file not found.")
|
displayError("Device pairing file not found.")
|
||||||
|
|||||||
@@ -300,99 +300,3 @@ struct OperationError: ALTLocalizedError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MinimuxerError: LocalizedError {
|
|
||||||
public var failureReason: String? {
|
|
||||||
switch self {
|
|
||||||
case .NoDevice:
|
|
||||||
return NSLocalizedString("Cannot fetch the device from the muxer", comment: "")
|
|
||||||
case .NoConnection:
|
|
||||||
return NSLocalizedString("Unable to connect to the device, make sure LocalDevVPN is enabled and you're connected to Wi-Fi. This could mean an invalid pairing.", comment: "")
|
|
||||||
case .PairingFile:
|
|
||||||
return NSLocalizedString("Invalid pairing file. Your pairing file either didn't have a UDID, or it wasn't a valid plist. Please use iloader to replace it.", comment: "")
|
|
||||||
|
|
||||||
case .CreateDebug:
|
|
||||||
return self.createService(name: "debug")
|
|
||||||
case .LookupApps:
|
|
||||||
return self.getFromDevice(name: "installed apps")
|
|
||||||
case .FindApp:
|
|
||||||
return self.getFromDevice(name: "path to the app")
|
|
||||||
case .BundlePath:
|
|
||||||
return self.getFromDevice(name: "bundle path")
|
|
||||||
case .MaxPacket:
|
|
||||||
return self.setArgument(name: "max packet")
|
|
||||||
case .WorkingDirectory:
|
|
||||||
return self.setArgument(name: "working directory")
|
|
||||||
case .Argv:
|
|
||||||
return self.setArgument(name: "argv")
|
|
||||||
case .LaunchSuccess:
|
|
||||||
return self.getFromDevice(name: "launch success")
|
|
||||||
case .Detach:
|
|
||||||
return NSLocalizedString("Unable to detach from the app's process", comment: "")
|
|
||||||
case .Attach:
|
|
||||||
return NSLocalizedString("Unable to attach to the app's process", comment: "")
|
|
||||||
|
|
||||||
case .CreateInstproxy:
|
|
||||||
return self.createService(name: "instproxy")
|
|
||||||
case .CreateAfc:
|
|
||||||
return self.createService(name: "AFC")
|
|
||||||
case .RwAfc:
|
|
||||||
return NSLocalizedString("AFC was unable to manage files on the device. Ensure Wi-Fi and LocalDevVPN are connected. If they both are, replace your pairing using iloader.", comment: "")
|
|
||||||
case .InstallApp(let message):
|
|
||||||
return NSLocalizedString("Unable to install the app: \(message.toString())", comment: "")
|
|
||||||
case .UninstallApp:
|
|
||||||
return NSLocalizedString("Unable to uninstall the app", comment: "")
|
|
||||||
|
|
||||||
case .CreateMisagent:
|
|
||||||
return self.createService(name: "misagent")
|
|
||||||
case .ProfileInstall:
|
|
||||||
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
|
|
||||||
case .ProfileRemove:
|
|
||||||
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
|
|
||||||
case .CreateLockdown:
|
|
||||||
return NSLocalizedString("Unable to connect to lockdown", comment: "")
|
|
||||||
case .CreateCoreDevice:
|
|
||||||
return NSLocalizedString("Unable to connect to core device proxy", comment: "")
|
|
||||||
case .CreateSoftwareTunnel:
|
|
||||||
return NSLocalizedString("Unable to create software tunnel", comment: "")
|
|
||||||
case .CreateRemoteServer:
|
|
||||||
return NSLocalizedString("Unable to connect to remote server", comment: "")
|
|
||||||
case .CreateProcessControl:
|
|
||||||
return NSLocalizedString("Unable to connect to process control", comment: "")
|
|
||||||
case .GetLockdownValue:
|
|
||||||
return NSLocalizedString("Unable to get value from lockdown", comment: "")
|
|
||||||
case .Connect:
|
|
||||||
return NSLocalizedString("Unable to connect to TCP port", comment: "")
|
|
||||||
case .Close:
|
|
||||||
return NSLocalizedString("Unable to close TCP port", comment: "")
|
|
||||||
case .XpcHandshake:
|
|
||||||
return NSLocalizedString("Unable to get services from XPC", comment: "")
|
|
||||||
case .NoService:
|
|
||||||
return NSLocalizedString("Device did not contain service", comment: "")
|
|
||||||
case .InvalidProductVersion:
|
|
||||||
return NSLocalizedString("Service version was in an unexpected format", comment: "")
|
|
||||||
case .CreateFolder:
|
|
||||||
return NSLocalizedString("Unable to create DDI folder", comment: "")
|
|
||||||
case .DownloadImage:
|
|
||||||
return NSLocalizedString("Unable to download DDI", comment: "")
|
|
||||||
case .ImageLookup:
|
|
||||||
return NSLocalizedString("Unable to lookup DDI images", comment: "")
|
|
||||||
case .ImageRead:
|
|
||||||
return NSLocalizedString("Unable to read images to memory", comment: "")
|
|
||||||
case .Mount:
|
|
||||||
return NSLocalizedString("Mount failed", comment: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func createService(name: String) -> String {
|
|
||||||
return String(format: NSLocalizedString("Cannot start a %@ server on the device.", comment: ""), name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func getFromDevice(name: String) -> String {
|
|
||||||
return String(format: NSLocalizedString("Cannot fetch %@ from the device.", comment: ""), name)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func setArgument(name: String) -> String {
|
|
||||||
return String(format: NSLocalizedString("Cannot set %@ on the device.", comment: ""), name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try install_ipa(installedApp.bundleIdentifier)
|
try installIPA(installedApp.bundleIdentifier)
|
||||||
installing = false
|
installing = false
|
||||||
installedApp.refreshedDate = Date()
|
installedApp.refreshedDate = Date()
|
||||||
self.finish(.success(installedApp))
|
self.finish(.success(installedApp))
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ final class RefreshAppOperation: ResultOperation<InstalledApp>
|
|||||||
|
|
||||||
for p in profiles {
|
for p in profiles {
|
||||||
do {
|
do {
|
||||||
let bytes = p.value.data.toRustByteSlice()
|
let bytes =
|
||||||
try install_provisioning_profile(bytes.forRust())
|
try installProvisioningProfiles(p.value.data)
|
||||||
} catch {
|
} catch {
|
||||||
self.finish(.failure(MinimuxerError.ProfileInstall))
|
self.finish(.failure(MinimuxerError.ProfileInstall))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ final class RemoveAppOperation: ResultOperation<InstalledApp>
|
|||||||
let resignedBundleIdentifier = installedApp.resignedBundleIdentifier
|
let resignedBundleIdentifier = installedApp.resignedBundleIdentifier
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try remove_app(resignedBundleIdentifier)
|
try removeApp(resignedBundleIdentifier)
|
||||||
} catch {
|
} catch {
|
||||||
return self.finish(.failure(error))
|
return self.finish(.failure(error))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,7 +222,7 @@ private extension ResignAppOperation
|
|||||||
// The embedded certificate + certificate identifier are already in app bundle, no need to update them.
|
// The embedded certificate + certificate identifier are already in app bundle, no need to update them.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if infoDictionary.keys.contains(Bundle.Info.deviceID), let udid = fetch_udid()?.toString() as? String
|
else if infoDictionary.keys.contains(Bundle.Info.deviceID), let udid = fetchUDID()
|
||||||
{
|
{
|
||||||
// There is an ALTDeviceID entry, so assume the app is using AltKit and replace it with the device's UDID.
|
// There is an ALTDeviceID entry, so assume the app is using AltKit and replace it with the device's UDID.
|
||||||
additionalValues[Bundle.Info.deviceID] = udid
|
additionalValues[Bundle.Info.deviceID] = udid
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ final class SendAppOperation: ResultOperation<()>
|
|||||||
|
|
||||||
if let data = NSData(contentsOf: fileURL) {
|
if let data = NSData(contentsOf: fileURL) {
|
||||||
do {
|
do {
|
||||||
let bytes = Data(data).toRustByteSlice()
|
let bytes = Data(data)
|
||||||
try yeet_app_afc(app.bundleIdentifier, bytes.forRust())
|
try yeetAppAFC(app.bundleIdentifier, bytes)
|
||||||
self.progress.completedUnitCount += 1
|
self.progress.completedUnitCount += 1
|
||||||
self.finish(.success(()))
|
self.finish(.success(()))
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
9987603429A4555300818586 /* em_proxy.h in Sources */ = {isa = PBXBuildFile; fileRef = 9999259129A45319005CF020 /* em_proxy.h */; };
|
A8A5B08D2F4C387300572B4A /* em_proxy.h in Sources */ = {isa = PBXBuildFile; fileRef = 9999259129A45319005CF020 /* em_proxy.h */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXBuildRule section */
|
/* Begin PBXBuildRule section */
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
outputFiles = (
|
outputFiles = (
|
||||||
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
|
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
|
||||||
"$(TARGET_BUILD_DIR)/$(FULL_PRODUCT_NAME)",
|
"$(TARGET_BUILD_DIR)/$(FULL_PRODUCT_NAME)",
|
||||||
|
"$(SRCROOT)/Dependencies/em_proxy/em_proxy.h",
|
||||||
);
|
);
|
||||||
script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./em_proxy/$LIB_FILE_NAME.a\"\necho Generating Static lib: $BUILT_SRC\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
|
script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./em_proxy/$LIB_FILE_NAME.a\"\necho Generating Static lib: $BUILT_SRC\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
|
||||||
};
|
};
|
||||||
@@ -117,6 +118,9 @@
|
|||||||
);
|
);
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
./em_proxy/em_proxy.h,
|
./em_proxy/em_proxy.h,
|
||||||
|
./em_proxy/em_proxy.swift,
|
||||||
|
"./em_proxy/libem_proxy-ios.a",
|
||||||
|
"./em_proxy/libem_proxy-sim.a",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
@@ -129,7 +133,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
9987603429A4555300818586 /* em_proxy.h in Sources */,
|
A8A5B08D2F4C387300572B4A /* em_proxy.h in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
6
Dependencies/fetch-prebuilt.sh
vendored
6
Dependencies/fetch-prebuilt.sh
vendored
@@ -75,12 +75,14 @@ check_for_update() {
|
|||||||
if [ "$FORCE_DOWNLOAD" = true ] || [ "$NOT_UPTODATE" = true ] ;then
|
if [ "$FORCE_DOWNLOAD" = true ] || [ "$NOT_UPTODATE" = true ] ;then
|
||||||
echo "downloading binaries"
|
echo "downloading binaries"
|
||||||
echo
|
echo
|
||||||
wget -O "$1/lib$1-sim.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-sim.a"
|
|
||||||
if [[ "$1" != "minimuxer" ]]; then
|
if [[ "$1" != "minimuxer" ]]; then
|
||||||
wget -O "$1/lib$1-ios.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1.a"
|
wget -O "$1/lib$1-sim.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-sim.a"
|
||||||
|
wget -O "$1/lib$1-ios.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-ios.a"
|
||||||
wget -O "$1/$1.h" "https://github.com/SideStore/$1/releases/latest/download/$1.h"
|
wget -O "$1/$1.h" "https://github.com/SideStore/$1/releases/latest/download/$1.h"
|
||||||
|
wget -O "$1/$1.swift" "https://github.com/SideStore/$1/releases/latest/download/$1.swift"
|
||||||
echo
|
echo
|
||||||
else
|
else
|
||||||
|
wget -O "$1/lib$1-sim.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-sim.a"
|
||||||
wget -O "$1/lib$1-ios.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-ios.a"
|
wget -O "$1/lib$1-ios.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-ios.a"
|
||||||
wget -O "$1/generated.zip" "https://github.com/SideStore/$1/releases/latest/download/generated.zip"
|
wget -O "$1/generated.zip" "https://github.com/SideStore/$1/releases/latest/download/generated.zip"
|
||||||
echo
|
echo
|
||||||
|
|||||||
30
Dependencies/minimuxer.xcodeproj/project.pbxproj
vendored
30
Dependencies/minimuxer.xcodeproj/project.pbxproj
vendored
@@ -7,8 +7,8 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
9961EC2829BE9C2000AF2C6F /* SwiftBridgeCore.h in Sources */ = {isa = PBXBuildFile; fileRef = 9961EC2729BE9C1200AF2C6F /* SwiftBridgeCore.h */; };
|
A8A5B07F2F4C355800572B4A /* minimuxer.h in Sources */ = {isa = PBXBuildFile; fileRef = A8A5B07D2F4C34FF00572B4A /* minimuxer.h */; };
|
||||||
9987603329A454B500818586 /* minimuxer.h in Sources */ = {isa = PBXBuildFile; fileRef = 9987603229A454B500818586 /* minimuxer.h */; };
|
A8A5B0802F4C355800572B4A /* SwiftBridgeCore.h in Sources */ = {isa = PBXBuildFile; fileRef = A8A5B07E2F4C34FF00572B4A /* SwiftBridgeCore.h */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXBuildRule section */
|
/* Begin PBXBuildRule section */
|
||||||
@@ -24,27 +24,20 @@
|
|||||||
outputFiles = (
|
outputFiles = (
|
||||||
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
|
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
|
||||||
"$(TARGET_BUILD_DIR)/$(FULL_PRODUCT_NAME)",
|
"$(TARGET_BUILD_DIR)/$(FULL_PRODUCT_NAME)",
|
||||||
|
"$(SRCROOT)/Dependencies/minimuxer/minimuxer.h",
|
||||||
);
|
);
|
||||||
script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./minimuxer/${LIB_FILE_NAME}.a\"\necho Generating Static lib: $BUILT_SRC\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
|
script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./minimuxer/${LIB_FILE_NAME}.a\"\necho Generating Static lib: $BUILT_SRC\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
|
||||||
};
|
};
|
||||||
/* End PBXBuildRule section */
|
/* End PBXBuildRule section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
9961EC2729BE9C1200AF2C6F /* SwiftBridgeCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SwiftBridgeCore.h; path = minimuxer/SwiftBridgeCore.h; sourceTree = "<group>"; };
|
A8A5B07D2F4C34FF00572B4A /* minimuxer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = minimuxer.h; path = minimuxer/minimuxer.h; sourceTree = "<group>"; };
|
||||||
9987603229A454B500818586 /* minimuxer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = minimuxer.h; path = minimuxer/minimuxer.h; sourceTree = "<group>"; };
|
A8A5B07E2F4C34FF00572B4A /* SwiftBridgeCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SwiftBridgeCore.h; path = minimuxer/SwiftBridgeCore.h; sourceTree = "<group>"; };
|
||||||
|
A8A5B09C2F4C454900572B4A /* minimuxer-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "minimuxer-Bridging-Header.h"; path = "minimuxer/minimuxer-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminimuxer_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminimuxer_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
99F87D1529D8E41100B40039 /* Generated */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
9961EC2729BE9C1200AF2C6F /* SwiftBridgeCore.h */,
|
|
||||||
9987603229A454B500818586 /* minimuxer.h */,
|
|
||||||
);
|
|
||||||
name = Generated;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CA6012A875F922869D176AE5 /* Products */ = {
|
CA6012A875F922869D176AE5 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -56,7 +49,9 @@
|
|||||||
CA6012A875F9D65BC3C892A8 = {
|
CA6012A875F9D65BC3C892A8 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
99F87D1529D8E41100B40039 /* Generated */,
|
A8A5B07D2F4C34FF00572B4A /* minimuxer.h */,
|
||||||
|
A8A5B07E2F4C34FF00572B4A /* SwiftBridgeCore.h */,
|
||||||
|
A8A5B09C2F4C454900572B4A /* minimuxer-Bridging-Header.h */,
|
||||||
CA6012A875F922869D176AE5 /* Products */,
|
CA6012A875F922869D176AE5 /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -132,6 +127,9 @@
|
|||||||
./minimuxer/minimuxer.swift,
|
./minimuxer/minimuxer.swift,
|
||||||
./minimuxer/SwiftBridgeCore.swift,
|
./minimuxer/SwiftBridgeCore.swift,
|
||||||
"./minimuxer/minimuxer-Bridging-Header.h",
|
"./minimuxer/minimuxer-Bridging-Header.h",
|
||||||
|
"./minimuxer/minimuxer-helpers.swift",
|
||||||
|
"./minimuxer/libminimuxer-ios.a",
|
||||||
|
"./minimuxer/libminimuxer-sim.a",
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
@@ -144,8 +142,8 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
9961EC2829BE9C2000AF2C6F /* SwiftBridgeCore.h in Sources */,
|
A8A5B07F2F4C355800572B4A /* minimuxer.h in Sources */,
|
||||||
9987603329A454B500818586 /* minimuxer.h in Sources */,
|
A8A5B0802F4C355800572B4A /* SwiftBridgeCore.h in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
//
|
//
|
||||||
// EMProxyWrwapper.swift
|
// EMProxyWrwapper.swift
|
||||||
// AltStore
|
// SideStore
|
||||||
//
|
//
|
||||||
// Created by Magesh K on 22/02/26.
|
// Created by Magesh K on 22/02/26.
|
||||||
// Copyright © 2026 SideStore. All rights reserved.
|
// Copyright © 2026 SideStore. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import em_proxy
|
private import em_proxy
|
||||||
|
|
||||||
public func startEMProxy(bind_addr: String) {
|
public func startEMProxy(bind_addr: String) {
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
//
|
//
|
||||||
// MinimuxerWrapper.swift
|
// MinimuxerWrapper.swift
|
||||||
// SideStore
|
|
||||||
//
|
//
|
||||||
// Created by Jackson Coxson on 10/26/22.
|
// Created by Magesh K on 22/02/26.
|
||||||
|
// Copyright © 2026 SideStore. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import minimuxer
|
private import minimuxer
|
||||||
|
|
||||||
var isMinimuxerReady: Bool {
|
var isMinimuxerReady: Bool {
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
@@ -32,3 +32,146 @@ func targetMinimuxerAddress() {
|
|||||||
minimuxer.target_minimuxer_address()
|
minimuxer.target_minimuxer_address()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func installProvisioningProfiles(_ profileData: Data) throws {
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
print("installProvisioningProfiles(\(profileData)) is no-op on simulator")
|
||||||
|
#else
|
||||||
|
try minimuxer.install_provisioning_profile(profileData.toRustByteSlice().forRust())
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func removeApp(_ bundleId: String) throws {
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
print("removeApp(\(bundleId)) is no-op on simulator")
|
||||||
|
#else
|
||||||
|
try minimuxer.remove_app(bundleId)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func yeetAppAFC(_ bundleId: String, _ rawBytes: Data) throws {
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
print("yeetAppAFC(\(bundleId), \(rawBytes)) is no-op on simulator")
|
||||||
|
#else
|
||||||
|
try minimuxer.yeet_app_afc(bundleId, rawBytes.toRustByteSlice().forRust())
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func installIPA(_ bundleId: String) throws {
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
print("installIPA(\(bundleId)) is no-op on simulator")
|
||||||
|
#else
|
||||||
|
try minimuxer.install_ipa(bundleId)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func fetchUDID() -> String? {
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
print("fetchUDID() is no-op on simulator")
|
||||||
|
return "XXXXX-XXXX-XXXXX-XXXX"
|
||||||
|
#else
|
||||||
|
return minimuxer.fetch_udid()?.toString()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extension MinimuxerError: @retroactive LocalizedError {
|
||||||
|
public var failureReason: String? {
|
||||||
|
switch self {
|
||||||
|
case .NoDevice:
|
||||||
|
return NSLocalizedString("Cannot fetch the device from the muxer", comment: "")
|
||||||
|
case .NoConnection:
|
||||||
|
return NSLocalizedString("Unable to connect to the device, make sure LocalDevVPN is enabled and you're connected to Wi-Fi. This could mean an invalid pairing.", comment: "")
|
||||||
|
case .PairingFile:
|
||||||
|
return NSLocalizedString("Invalid pairing file. Your pairing file either didn't have a UDID, or it wasn't a valid plist. Please use iloader to replace it.", comment: "")
|
||||||
|
|
||||||
|
case .CreateDebug:
|
||||||
|
return self.createService(name: "debug")
|
||||||
|
case .LookupApps:
|
||||||
|
return self.getFromDevice(name: "installed apps")
|
||||||
|
case .FindApp:
|
||||||
|
return self.getFromDevice(name: "path to the app")
|
||||||
|
case .BundlePath:
|
||||||
|
return self.getFromDevice(name: "bundle path")
|
||||||
|
case .MaxPacket:
|
||||||
|
return self.setArgument(name: "max packet")
|
||||||
|
case .WorkingDirectory:
|
||||||
|
return self.setArgument(name: "working directory")
|
||||||
|
case .Argv:
|
||||||
|
return self.setArgument(name: "argv")
|
||||||
|
case .LaunchSuccess:
|
||||||
|
return self.getFromDevice(name: "launch success")
|
||||||
|
case .Detach:
|
||||||
|
return NSLocalizedString("Unable to detach from the app's process", comment: "")
|
||||||
|
case .Attach:
|
||||||
|
return NSLocalizedString("Unable to attach to the app's process", comment: "")
|
||||||
|
|
||||||
|
case .CreateInstproxy:
|
||||||
|
return self.createService(name: "instproxy")
|
||||||
|
case .CreateAfc:
|
||||||
|
return self.createService(name: "AFC")
|
||||||
|
case .RwAfc:
|
||||||
|
return NSLocalizedString("AFC was unable to manage files on the device. Ensure Wi-Fi and LocalDevVPN are connected. If they both are, replace your pairing using iloader.", comment: "")
|
||||||
|
case .InstallApp(let message):
|
||||||
|
return NSLocalizedString("Unable to install the app: \(message.toString())", comment: "")
|
||||||
|
case .UninstallApp:
|
||||||
|
return NSLocalizedString("Unable to uninstall the app", comment: "")
|
||||||
|
|
||||||
|
case .CreateMisagent:
|
||||||
|
return self.createService(name: "misagent")
|
||||||
|
case .ProfileInstall:
|
||||||
|
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
|
||||||
|
case .ProfileRemove:
|
||||||
|
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
|
||||||
|
case .CreateLockdown:
|
||||||
|
return NSLocalizedString("Unable to connect to lockdown", comment: "")
|
||||||
|
case .CreateCoreDevice:
|
||||||
|
return NSLocalizedString("Unable to connect to core device proxy", comment: "")
|
||||||
|
case .CreateSoftwareTunnel:
|
||||||
|
return NSLocalizedString("Unable to create software tunnel", comment: "")
|
||||||
|
case .CreateRemoteServer:
|
||||||
|
return NSLocalizedString("Unable to connect to remote server", comment: "")
|
||||||
|
case .CreateProcessControl:
|
||||||
|
return NSLocalizedString("Unable to connect to process control", comment: "")
|
||||||
|
case .GetLockdownValue:
|
||||||
|
return NSLocalizedString("Unable to get value from lockdown", comment: "")
|
||||||
|
case .Connect:
|
||||||
|
return NSLocalizedString("Unable to connect to TCP port", comment: "")
|
||||||
|
case .Close:
|
||||||
|
return NSLocalizedString("Unable to close TCP port", comment: "")
|
||||||
|
case .XpcHandshake:
|
||||||
|
return NSLocalizedString("Unable to get services from XPC", comment: "")
|
||||||
|
case .NoService:
|
||||||
|
return NSLocalizedString("Device did not contain service", comment: "")
|
||||||
|
case .InvalidProductVersion:
|
||||||
|
return NSLocalizedString("Service version was in an unexpected format", comment: "")
|
||||||
|
case .CreateFolder:
|
||||||
|
return NSLocalizedString("Unable to create DDI folder", comment: "")
|
||||||
|
case .DownloadImage:
|
||||||
|
return NSLocalizedString("Unable to download DDI", comment: "")
|
||||||
|
case .ImageLookup:
|
||||||
|
return NSLocalizedString("Unable to lookup DDI images", comment: "")
|
||||||
|
case .ImageRead:
|
||||||
|
return NSLocalizedString("Unable to read images to memory", comment: "")
|
||||||
|
case .Mount:
|
||||||
|
return NSLocalizedString("Mount failed", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func createService(name: String) -> String {
|
||||||
|
return String(format: NSLocalizedString("Cannot start a %@ server on the device.", comment: ""), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func getFromDevice(name: String) -> String {
|
||||||
|
return String(format: NSLocalizedString("Cannot fetch %@ from the device.", comment: ""), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func setArgument(name: String) -> String {
|
||||||
|
return String(format: NSLocalizedString("Cannot set %@ on the device.", comment: ""), name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
255
scripts/ci.py
Normal file
255
scripts/ci.py
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# helpers
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def run(cmd, check=True):
|
||||||
|
print(f"$ {cmd}", flush=True)
|
||||||
|
subprocess.run(cmd, shell=True, cwd=ROOT, check=check)
|
||||||
|
|
||||||
|
|
||||||
|
def output(name, value):
|
||||||
|
print(f"{name}={value}")
|
||||||
|
out = os.environ.get("GITHUB_OUTPUT")
|
||||||
|
if out:
|
||||||
|
with open(out, "a") as f:
|
||||||
|
f.write(f"{name}={value}\n")
|
||||||
|
|
||||||
|
|
||||||
|
def getenv(name, default=""):
|
||||||
|
return os.environ.get(name, default)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# SHARED
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def short_commit():
|
||||||
|
sha = subprocess.check_output(
|
||||||
|
"git rev-parse --short HEAD",
|
||||||
|
shell=True,
|
||||||
|
cwd=ROOT
|
||||||
|
).decode().strip()
|
||||||
|
|
||||||
|
output("short_commit", sha)
|
||||||
|
return sha
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# VERSION BUMP
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def bump_beta():
|
||||||
|
date = datetime.datetime.utcnow().strftime("%Y.%m.%d")
|
||||||
|
release_channel = getenv("RELEASE_CHANNEL", "beta")
|
||||||
|
|
||||||
|
build_file = ROOT / "build_number.txt"
|
||||||
|
xcconfig = ROOT / "Build.xcconfig"
|
||||||
|
|
||||||
|
short = subprocess.check_output(
|
||||||
|
"git rev-parse --short HEAD",
|
||||||
|
shell=True,
|
||||||
|
cwd=ROOT
|
||||||
|
).decode().strip()
|
||||||
|
|
||||||
|
def write(num):
|
||||||
|
run(
|
||||||
|
f"""sed -e "/MARKETING_VERSION = .*/s/$/-{release_channel}.{date}.{num}+{short}/" -i '' Build.xcconfig"""
|
||||||
|
)
|
||||||
|
build_file.write_text(f"{date},{num}")
|
||||||
|
|
||||||
|
if not build_file.exists():
|
||||||
|
write(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
last = build_file.read_text().strip().split(",")[1]
|
||||||
|
write(int(last) + 1)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# VERSION EXTRACTION
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def extract_version():
|
||||||
|
v = subprocess.check_output(
|
||||||
|
"grep MARKETING_VERSION Build.xcconfig | sed -e 's/MARKETING_VERSION = //g'",
|
||||||
|
shell=True,
|
||||||
|
cwd=ROOT
|
||||||
|
).decode().strip()
|
||||||
|
|
||||||
|
output("version", v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# CLEAN
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def clean():
|
||||||
|
run("make clean")
|
||||||
|
clean_derived_data()
|
||||||
|
|
||||||
|
def clean_derived_data():
|
||||||
|
run("rm -rf ~/Library/Developer/Xcode/DerivedData/*", check=False)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# BUILD
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def build():
|
||||||
|
run("make clean")
|
||||||
|
run("rm -rf ~/Library/Developer/Xcode/DerivedData/*", check=False)
|
||||||
|
run("mkdir -p build/logs")
|
||||||
|
|
||||||
|
run(
|
||||||
|
"set -o pipefail && "
|
||||||
|
"NSUnbufferedIO=YES make -B build "
|
||||||
|
"2>&1 | tee -a build/logs/build.log | xcbeautify --renderer github-actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
run("make fakesign | tee -a build/logs/build.log")
|
||||||
|
run("make ipa | tee -a build/logs/build.log")
|
||||||
|
|
||||||
|
run("zip -r -9 ./SideStore.dSYMs.zip ./SideStore.xcarchive/dSYMs")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# TESTS BUILD
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def tests_build():
|
||||||
|
run("mkdir -p build/logs")
|
||||||
|
run(
|
||||||
|
"NSUnbufferedIO=YES make -B build-tests "
|
||||||
|
"2>&1 | tee -a build/logs/tests-build.log | xcbeautify --renderer github-actions"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# TESTS RUN
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def tests_run():
|
||||||
|
run("mkdir -p build/logs")
|
||||||
|
run("nohup make -B boot-sim-async </dev/null >> build/logs/tests-run.log 2>&1 &")
|
||||||
|
|
||||||
|
run("make -B sim-boot-check | tee -a build/logs/tests-run.log")
|
||||||
|
|
||||||
|
run("make run-tests 2>&1 | tee -a build/logs/tests-run.log")
|
||||||
|
|
||||||
|
run("zip -r -9 ./test-results.zip ./build/tests")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# LOG ENCRYPTION
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def encrypt_logs(name):
|
||||||
|
pwd = getenv("BUILD_LOG_ZIP_PASSWORD", "12345")
|
||||||
|
run(
|
||||||
|
f'cd build/logs && zip -e -P "{pwd}" ../../{name}.zip *'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# RELEASE NOTES
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def release_notes(tag):
|
||||||
|
last = subprocess.check_output(
|
||||||
|
"gh run list --branch $(git branch --show-current) "
|
||||||
|
"--status success --json headSha --jq '.[0].headSha'",
|
||||||
|
shell=True,
|
||||||
|
cwd=ROOT
|
||||||
|
).decode().strip()
|
||||||
|
|
||||||
|
if not last or last == "null":
|
||||||
|
last = subprocess.check_output(
|
||||||
|
"git rev-list --max-parents=0 HEAD",
|
||||||
|
shell=True,
|
||||||
|
cwd=ROOT
|
||||||
|
).decode().strip()
|
||||||
|
|
||||||
|
run(f"python3 update_release_notes.py {last} {tag}")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# PUBLISH SOURCE.JSON
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def publish_apps(short_commit):
|
||||||
|
repo = ROOT / "SideStore/apps-v2.json"
|
||||||
|
|
||||||
|
run("git config user.name 'GitHub Actions'", check=False)
|
||||||
|
run("git config user.email 'github-actions@github.com'", check=False)
|
||||||
|
|
||||||
|
run("python3 scripts/update_apps.py './_includes/source.json'", check=False)
|
||||||
|
|
||||||
|
run("git add ./_includes/source.json", check=False)
|
||||||
|
run(
|
||||||
|
f"git commit -m ' - updated for {short_commit} deployment' || true",
|
||||||
|
check=False
|
||||||
|
)
|
||||||
|
run("git push", check=False)
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# ENTRYPOINT
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def main():
|
||||||
|
cmd = sys.argv[1]
|
||||||
|
|
||||||
|
if cmd == "commid-id":
|
||||||
|
short_commit()
|
||||||
|
|
||||||
|
elif cmd == "bump-beta":
|
||||||
|
bump_beta()
|
||||||
|
|
||||||
|
elif cmd == "version":
|
||||||
|
extract_version()
|
||||||
|
|
||||||
|
elif cmd == "clean":
|
||||||
|
clean()
|
||||||
|
|
||||||
|
elif cmd == "cleanDerivedData":
|
||||||
|
clean_derived_data()
|
||||||
|
|
||||||
|
elif cmd == "build":
|
||||||
|
build()
|
||||||
|
|
||||||
|
elif cmd == "tests-build":
|
||||||
|
tests_build()
|
||||||
|
|
||||||
|
elif cmd == "tests-run":
|
||||||
|
tests_run()
|
||||||
|
|
||||||
|
elif cmd == "encrypt-build":
|
||||||
|
encrypt_logs("encrypted-build-logs")
|
||||||
|
|
||||||
|
elif cmd == "encrypt-tests-build":
|
||||||
|
encrypt_logs("encrypted-tests-build-logs")
|
||||||
|
|
||||||
|
elif cmd == "encrypt-tests-run":
|
||||||
|
encrypt_logs("encrypted-tests-run-logs")
|
||||||
|
|
||||||
|
elif cmd == "release-notes":
|
||||||
|
release_notes(sys.argv[2])
|
||||||
|
|
||||||
|
elif cmd == "publish":
|
||||||
|
publish_apps(sys.argv[2])
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise SystemExit(f"Unknown command {cmd}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user