Compare commits

...

16 Commits

Author SHA1 Message Date
mahee96
cf0a174882 CI: full rewrite - moved logic into ci.py and kept workflow scripts mostly dummy 2026-02-23 18:04:02 +05:30
mahee96
8fc38300d5 try adding libfragmentzip again with explicit dependency 2026-02-23 16:26:56 +05:30
mahee96
5460fdfec5 try adding libfragmentzip again with explicit dependency 2026-02-23 14:42:37 +05:30
mahee96
ded65e30d6 dependencies: fix few more issues with project reference 2026-02-23 14:28:53 +05:30
mahee96
b0fab7550f dependencies: fix few more issues with project reference 2026-02-23 12:51:36 +05:30
mahee96
10154954f0 dependencies: covert most back to folders except few that are required by build for reference 2026-02-23 12:45:24 +05:30
mahee96
59892247a6 dependencies: fix missing header by adding to output list 2026-02-23 12:30:19 +05:30
mahee96
f350c9fa49 dependencies: use group format for folders to see if that fixes chaining issue 2026-02-23 12:24:07 +05:30
mahee96
32fb3d706c dependencies: use group format for folders to see if that fixes chaining issue 2026-02-23 12:12:51 +05:30
mahee96
232dec2c36 wrappers: keep imports them private to file scope for encapsulation 2026-02-23 11:53:17 +05:30
mahee96
e9772afbf6 minimuxer: fix some wrapper methods 2026-02-23 11:45:15 +05:30
mahee96
014d7b8346 build: fix some more path issues 2026-02-23 11:43:46 +05:30
mahee96
8d90def5d8 minimuxer: fix some wrapper methods 2026-02-23 11:43:23 +05:30
mahee96
e891b7ec93 minimuxer: use wrapper methods 2026-02-23 11:30:11 +05:30
mahee96
e5dbf2b677 schemes: do not auto create 2026-02-23 11:29:47 +05:30
mahee96
4ff35e36e4 fix: attempt to fix the dependency chaining issue 2026-02-23 10:29:02 +05:30
29 changed files with 3016 additions and 519 deletions

View File

@@ -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

View File

@@ -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
View 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
View 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

View File

@@ -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>

View File

@@ -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.")

View File

@@ -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)
}
}

View File

@@ -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))

View File

@@ -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))
} }

View File

@@ -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))
} }

View File

@@ -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

View File

@@ -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 {

View File

@@ -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;
}; };

View File

@@ -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

View File

@@ -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;
}; };

View File

@@ -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)

View File

@@ -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
View 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()