mirror of
https://github.com/SideStore/SideStore.git
synced 2026-03-29 06:45:39 +02:00
Compare commits
126 Commits
3961688b73
...
alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dfdac41a70 | ||
|
|
62ad755920 | ||
|
|
baf1aa7fff | ||
|
|
395d2bb657 | ||
|
|
e32f4fbeae | ||
|
|
b643f6ae01 | ||
|
|
2f0fb3cb71 | ||
|
|
0f90f9bf2d | ||
|
|
1c8541fdd4 | ||
|
|
4e6882423d | ||
|
|
7c3add0e70 | ||
|
|
c4139473fa | ||
|
|
0837f82a69 | ||
|
|
c04ebd3ec6 | ||
|
|
0e81515af9 | ||
|
|
dd4c0be75d | ||
|
|
7500dd5b17 | ||
|
|
dba84ed015 | ||
|
|
dbb1e0ff3e | ||
|
|
431900368a | ||
|
|
8843a95bfc | ||
|
|
4810f3a624 | ||
|
|
954a96b988 | ||
|
|
afd32a285d | ||
|
|
f8a0ee2c84 | ||
|
|
9d01a1c116 | ||
|
|
f6db823816 | ||
|
|
dfb018bb89 | ||
|
|
adb91439e4 | ||
|
|
368a2afad4 | ||
|
|
830b66c364 | ||
|
|
40a2137069 | ||
|
|
fa752e3ae6 | ||
|
|
71b319c8e6 | ||
|
|
fd12d36980 | ||
|
|
cdc41c7954 | ||
|
|
1fe3e5a6ca | ||
|
|
bf65bce7b5 | ||
|
|
7f3647a230 | ||
|
|
850180273c | ||
|
|
6a20d50fb6 | ||
|
|
79dc65f1c8 | ||
|
|
a9a2d8780b | ||
|
|
2286e78a55 | ||
|
|
3aa3b5df65 | ||
|
|
6cdfc211ea | ||
|
|
58ff20c690 | ||
|
|
0b865729a7 | ||
|
|
7c4cf2ae62 | ||
|
|
b0c65b04f2 | ||
|
|
f3f8036693 | ||
|
|
ee7c97fd3e | ||
|
|
f5eb669a74 | ||
|
|
3af3aa071c | ||
|
|
857c1e03e0 | ||
|
|
c09c290973 | ||
|
|
7b8884a8ac | ||
|
|
fd797fdf88 | ||
|
|
b18ce43a64 | ||
|
|
e90b8a0e32 | ||
|
|
bb578316f1 | ||
|
|
9579d46bed | ||
|
|
a916293585 | ||
|
|
f634b52e44 | ||
|
|
a6baf59649 | ||
|
|
a505d04215 | ||
|
|
95953ca0e9 | ||
|
|
3b9b45a06f | ||
|
|
ac277aa6eb | ||
|
|
7926452661 | ||
|
|
dfb01c2ae5 | ||
|
|
bff192be4e | ||
|
|
a863b2f39a | ||
|
|
bf72a0edfc | ||
|
|
06ad6488cc | ||
|
|
12f84b2365 | ||
|
|
118f64de8a | ||
|
|
555bb3d985 | ||
|
|
25925aceef | ||
|
|
5444fdd9bb | ||
|
|
f91e0a6295 | ||
|
|
4e4f0f6a3f | ||
|
|
9706a43bc1 | ||
|
|
d748a89b47 | ||
|
|
f412f6df23 | ||
|
|
b8354d3d0e | ||
|
|
191ce15d55 | ||
|
|
7f8f4fa9a7 | ||
|
|
ad54cbadc1 | ||
|
|
eb251b89c9 | ||
|
|
9efea00d09 | ||
|
|
7df29ca23b | ||
|
|
d06520d46f | ||
|
|
1ad0fe23fc | ||
|
|
21bbcd69f8 | ||
|
|
fd920be3bf | ||
|
|
16bb57c825 | ||
|
|
bf5b1c935c | ||
|
|
1b1c7c58e2 | ||
|
|
31e8eb7996 | ||
|
|
47db2c3d5d | ||
|
|
967b9f7572 | ||
|
|
b91bcee70f | ||
|
|
25f34c6f69 | ||
|
|
b7085aaeca | ||
|
|
046d2788b9 | ||
|
|
f44ed0a947 | ||
|
|
d356045b5d | ||
|
|
a54881a1c8 | ||
|
|
e27d44eece | ||
|
|
c2cecb63ac | ||
|
|
381e09d87c | ||
|
|
efbb40982e | ||
|
|
c1a033a627 | ||
|
|
1449f8c74f | ||
|
|
625389ab96 | ||
|
|
f7e34cbbe9 | ||
|
|
0fe8d7fed9 | ||
|
|
1a1aa42e02 | ||
|
|
7ff4b48223 | ||
|
|
4801f6e8f2 | ||
|
|
ff28f6fa8f | ||
|
|
2d141afbaf | ||
|
|
06e38aae00 | ||
|
|
d8783230a7 | ||
|
|
6c479bfede |
63
.github/maintenance/cache.py
vendored
63
.github/maintenance/cache.py
vendored
@@ -1,63 +0,0 @@
|
||||
import requests
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Your GitHub Personal Access Token
|
||||
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
|
||||
|
||||
# Repository details
|
||||
REPO_OWNER = "SideStore"
|
||||
REPO_NAME = "SideStore"
|
||||
|
||||
|
||||
API_URL = f"https://api.github.com/repos/{REPO_OWNER}/{REPO_NAME}/actions/caches"
|
||||
|
||||
# Common headers for GitHub API calls
|
||||
HEADERS = {
|
||||
"Accept": "application/vnd.github+json",
|
||||
"Authorization": f"Bearer {GITHUB_TOKEN}"
|
||||
}
|
||||
|
||||
def list_caches():
|
||||
response = requests.get(API_URL, headers=HEADERS)
|
||||
if response.status_code != 200:
|
||||
print(f"Failed to list caches. HTTP {response.status_code}")
|
||||
print("Response:", response.text)
|
||||
sys.exit(1)
|
||||
data = response.json()
|
||||
return data.get("actions_caches", [])
|
||||
|
||||
def delete_cache(cache_id):
|
||||
delete_url = f"{API_URL}/{cache_id}"
|
||||
response = requests.delete(delete_url, headers=HEADERS)
|
||||
return response.status_code
|
||||
|
||||
def main():
|
||||
caches = list_caches()
|
||||
if not caches:
|
||||
print("No caches found.")
|
||||
return
|
||||
|
||||
print("Found caches:")
|
||||
for cache in caches:
|
||||
print(f"ID: {cache.get('id')}, Key: {cache.get('key')}")
|
||||
|
||||
print("\nDeleting caches...")
|
||||
for cache in caches:
|
||||
cache_id = cache.get("id")
|
||||
status = delete_cache(cache_id)
|
||||
if status == 204:
|
||||
print(f"Successfully deleted cache with ID: {cache_id}")
|
||||
else:
|
||||
print(f"Failed to delete cache with ID: {cache_id}. HTTP status code: {status}")
|
||||
|
||||
print("All caches processed.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
### How to use
|
||||
'''
|
||||
just export the GITHUB_TOKEN and then run this script via `python3 cache.py' to delete the caches
|
||||
'''
|
||||
99
.github/workflows/alpha.yml
vendored
99
.github/workflows/alpha.yml
vendored
@@ -2,7 +2,7 @@ name: Alpha SideStore Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [alpha]
|
||||
branches: [staging]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
@@ -13,6 +13,8 @@ jobs:
|
||||
build:
|
||||
runs-on: macos-26
|
||||
env:
|
||||
DEPLOY_KEY: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_NAME: Alpha
|
||||
CHANNEL: alpha
|
||||
UPSTREAM_CHANNEL: "nightly"
|
||||
@@ -23,36 +25,33 @@ jobs:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Find Last Successful commit
|
||||
run: |
|
||||
LAST_SUCCESSFUL_COMMIT=$(python3 scripts/ci/workflow.py last-successful-commit \
|
||||
"false" "${{ env.CHANNEL }}" || echo "")
|
||||
echo "LAST_SUCCESSFUL_COMMIT=$LAST_SUCCESSFUL_COMMIT" | tee -a $GITHUB_ENV
|
||||
|
||||
- run: brew install ldid xcbeautify
|
||||
|
||||
# --------------------------------------------------
|
||||
# runtime env setup
|
||||
# --------------------------------------------------
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "SideStore/beta-build-num"
|
||||
ref: ${{ env.CHANNEL }}
|
||||
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
path: "Dependencies/beta-build-num"
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Env
|
||||
run: |
|
||||
BUILD_NUM=$(python3 scripts/ci/workflow.py reserve_build_number 'Dependencies/beta-build-num')
|
||||
BUILD_NUM="${{ github.run_number }}"
|
||||
MARKETING_VERSION=$(python3 scripts/ci/workflow.py get-marketing-version)
|
||||
SHORT_COMMIT=$(python3 scripts/ci/workflow.py commid-id)
|
||||
SHORT_COMMIT=$(python3 scripts/ci/workflow.py commit-id)
|
||||
|
||||
QUALIFIED_VERSION=$(python3 scripts/ci/workflow.py compute-qualified \
|
||||
NORMALIZED_VERSION=$(python3 scripts/ci/workflow.py compute-normalized \
|
||||
"$MARKETING_VERSION" \
|
||||
"$BUILD_NUM" \
|
||||
"${{ env.CHANNEL }}" \
|
||||
"$SHORT_COMMIT")
|
||||
|
||||
python3 scripts/ci/workflow.py set-marketing-version "$QUALIFIED_VERSION"
|
||||
python3 scripts/ci/workflow.py set-marketing-version "$NORMALIZED_VERSION"
|
||||
|
||||
echo "BUILD_NUM=$BUILD_NUM" | tee -a $GITHUB_ENV
|
||||
echo "SHORT_COMMIT=$SHORT_COMMIT" | tee -a $GITHUB_ENV
|
||||
echo "MARKETING_VERSION=$QUALIFIED_VERSION" | tee -a $GITHUB_ENV
|
||||
echo "MARKETING_VERSION=$NORMALIZED_VERSION" | tee -a $GITHUB_ENV
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
@@ -89,7 +88,9 @@ jobs:
|
||||
python3 scripts/ci/workflow.py clean-spm-cache
|
||||
|
||||
- name: Boot simulator (async)
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
if: >
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_RUN == '1'
|
||||
run: |
|
||||
mkdir -p build/logs
|
||||
python3 scripts/ci/workflow.py boot-sim-async "iPhone 17 Pro"
|
||||
@@ -106,7 +107,9 @@ jobs:
|
||||
|
||||
- name: Tests Build
|
||||
id: test-build
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||
if: >
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_BUILD == '1'
|
||||
env:
|
||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
run: |
|
||||
@@ -125,7 +128,9 @@ jobs:
|
||||
|
||||
- name: Tests Run
|
||||
id: test-run
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
if: >
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_RUN == '1'
|
||||
env:
|
||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
run: |
|
||||
@@ -138,20 +143,24 @@ jobs:
|
||||
# --------------------------------------------------
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: encrypted-build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||
path: encrypted-build-logs.zip
|
||||
name: build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||
path: build-logs.zip
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||
if: >
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_BUILD == '1'
|
||||
with:
|
||||
name: encrypted-tests-build-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: encrypted-tests-build-logs.zip
|
||||
name: tests-build-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: tests-build-logs.zip
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
if: >
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_RUN == '1'
|
||||
with:
|
||||
name: encrypted-tests-run-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: encrypted-tests-run-logs.zip
|
||||
name: tests-run-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: tests-run-logs.zip
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -163,30 +172,21 @@ jobs:
|
||||
path: SideStore.dSYMs.zip
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: env.DEPLOY_KEY != ''
|
||||
with:
|
||||
repository: "SideStore/apps-v2.json"
|
||||
ref: "main"
|
||||
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
path: "SideStore/apps-v2.json"
|
||||
|
||||
# --------------------------------------------------
|
||||
# deploy
|
||||
# --------------------------------------------------
|
||||
- name: Deploy
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Generate Metadata
|
||||
run: |
|
||||
PRODUCT_NAME=$(python3 scripts/ci/workflow.py get-product-name)
|
||||
BUNDLE_ID=$(python3 scripts/ci/workflow.py get-bundle-id)
|
||||
SOURCE_JSON="_includes/source.json"
|
||||
python3 scripts/ci/workflow.py dump-project-settings
|
||||
PRODUCT_NAME=$(python3 scripts/ci/workflow.py read-product-name)
|
||||
BUNDLE_ID=$(python3 scripts/ci/workflow.py read-bundle-id)
|
||||
IPA_NAME="$PRODUCT_NAME.ipa"
|
||||
|
||||
LAST_SUCCESSFUL_COMMIT=$(python3 scripts/ci/workflow.py last-successful-commit \
|
||||
"${{ github.workflow }}" "$CHANNEL")
|
||||
|
||||
python3 scripts/ci/workflow.py deploy \
|
||||
SideStore/apps-v2.json \
|
||||
"$SOURCE_JSON" \
|
||||
python3 scripts/ci/workflow.py generate-metadata \
|
||||
"$CHANNEL" \
|
||||
"$SHORT_COMMIT" \
|
||||
"$MARKETING_VERSION" \
|
||||
@@ -195,8 +195,23 @@ jobs:
|
||||
"$IPA_NAME" \
|
||||
"$LAST_SUCCESSFUL_COMMIT"
|
||||
|
||||
RELEASE_NOTES=$(python3 scripts/ci/workflow.py retrieve-release-notes "$CHANNEL")
|
||||
- name: Deploy
|
||||
if: env.DEPLOY_KEY != ''
|
||||
run: |
|
||||
SOURCE_JSON="_includes/source.json"
|
||||
|
||||
python3 scripts/ci/workflow.py deploy \
|
||||
SideStore/apps-v2.json \
|
||||
"$SOURCE_JSON" \
|
||||
"$CHANNEL" \
|
||||
"$MARKETING_VERSION"
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
# upload release to GH
|
||||
# --------------------------------------------------
|
||||
- name: Upload Release
|
||||
run: |
|
||||
python3 scripts/ci/workflow.py upload-release \
|
||||
"$RELEASE_NAME" \
|
||||
"$CHANNEL" \
|
||||
|
||||
141
.github/workflows/nightly.yml
vendored
141
.github/workflows/nightly.yml
vendored
@@ -15,6 +15,8 @@ jobs:
|
||||
build:
|
||||
runs-on: macos-26
|
||||
env:
|
||||
DEPLOY_KEY: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_NAME: Nightly
|
||||
CHANNEL: nightly
|
||||
UPSTREAM_CHANNEL: ""
|
||||
@@ -25,43 +27,63 @@ jobs:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Find Last Successful commit
|
||||
run: |
|
||||
LAST_SUCCESSFUL_COMMIT=$(python3 scripts/ci/workflow.py last-successful-commit \
|
||||
"false" "${{ env.CHANNEL }}" || echo "")
|
||||
echo "LAST_SUCCESSFUL_COMMIT=$LAST_SUCCESSFUL_COMMIT" | tee -a $GITHUB_ENV
|
||||
|
||||
- name: Check for new changes (on schedule)
|
||||
id: check_changes
|
||||
if: github.event_name == 'schedule'
|
||||
run: |
|
||||
NEW_COMMITS=$(python3 scripts/ci/workflow.py count-new-commits "$LAST_SUCCESSFUL_COMMIT")
|
||||
SHOULD_BUILD=$([ "${NEW_COMMITS:-0}" -ge 1 ] && echo true || echo false)
|
||||
echo "should_build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
|
||||
echo "NEW_COMMITS=$NEW_COMMITS" | tee -a $GITHUB_ENV
|
||||
|
||||
- name: Should Skip Building (on schedule)
|
||||
id: build_gate
|
||||
run: |
|
||||
SHOULD_SKIP=$(
|
||||
{ [ "${{ github.event_name }}" = "schedule" ] && \
|
||||
[ "${{ steps.check_changes.outputs.should_build }}" != "true" ]; \
|
||||
} && echo true || echo false
|
||||
)
|
||||
echo "should_skip=$SHOULD_SKIP" >> $GITHUB_OUTPUT
|
||||
|
||||
- run: brew install ldid xcbeautify
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
|
||||
# --------------------------------------------------
|
||||
# runtime env setup
|
||||
# --------------------------------------------------
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: "SideStore/beta-build-num"
|
||||
ref: ${{ env.CHANNEL }}
|
||||
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
path: "Dependencies/beta-build-num"
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Env
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
run: |
|
||||
BUILD_NUM=$(python3 scripts/ci/workflow.py reserve_build_number 'Dependencies/beta-build-num')
|
||||
BUILD_NUM="${{ github.run_number }}"
|
||||
MARKETING_VERSION=$(python3 scripts/ci/workflow.py get-marketing-version)
|
||||
SHORT_COMMIT=$(python3 scripts/ci/workflow.py commid-id)
|
||||
SHORT_COMMIT=$(python3 scripts/ci/workflow.py commit-id)
|
||||
|
||||
QUALIFIED_VERSION=$(python3 scripts/ci/workflow.py compute-qualified \
|
||||
NORMALIZED_VERSION=$(python3 scripts/ci/workflow.py compute-normalized \
|
||||
"$MARKETING_VERSION" \
|
||||
"$BUILD_NUM" \
|
||||
"${{ env.CHANNEL }}" \
|
||||
"$SHORT_COMMIT")
|
||||
|
||||
python3 scripts/ci/workflow.py set-marketing-version "$QUALIFIED_VERSION"
|
||||
python3 scripts/ci/workflow.py set-marketing-version "$NORMALIZED_VERSION"
|
||||
|
||||
echo "BUILD_NUM=$BUILD_NUM" | tee -a $GITHUB_ENV
|
||||
echo "SHORT_COMMIT=$SHORT_COMMIT" | tee -a $GITHUB_ENV
|
||||
echo "MARKETING_VERSION=$QUALIFIED_VERSION" | tee -a $GITHUB_ENV
|
||||
echo "MARKETING_VERSION=$NORMALIZED_VERSION" | tee -a $GITHUB_ENV
|
||||
|
||||
- name: Setup Xcode
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: "26.2"
|
||||
|
||||
- name: Restore Cache (exact)
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
id: xcode-cache-exact
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
@@ -71,7 +93,9 @@ jobs:
|
||||
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: Restore Cache (last)
|
||||
if: steps.xcode-cache-exact.outputs.cache-hit != 'true'
|
||||
if: >
|
||||
steps.build_gate.outputs.should_skip != 'true' &&
|
||||
steps.xcode-cache-exact.outputs.cache-hit != 'true'
|
||||
id: xcode-cache-fallback
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
@@ -84,19 +108,23 @@ jobs:
|
||||
# build and test
|
||||
# --------------------------------------------------
|
||||
- name: Clean
|
||||
if: contains(github.event.head_commit.message, '[--clean-build]')
|
||||
if: steps.build_gate.outputs.should_skip != 'true' && contains(github.event.head_commit.message, '[--clean-build]')
|
||||
run: |
|
||||
python3 scripts/ci/workflow.py clean
|
||||
python3 scripts/ci/workflow.py clean-derived-data
|
||||
python3 scripts/ci/workflow.py clean-spm-cache
|
||||
|
||||
- name: Boot simulator (async)
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
if: >
|
||||
steps.build_gate.outputs.should_skip != 'true' &&
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_RUN == '1'
|
||||
run: |
|
||||
mkdir -p build/logs
|
||||
python3 scripts/ci/workflow.py boot-sim-async "iPhone 17 Pro"
|
||||
|
||||
- name: Build
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
id: build
|
||||
env:
|
||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
@@ -107,8 +135,11 @@ jobs:
|
||||
exit $STATUS
|
||||
|
||||
- name: Tests Build
|
||||
if: >
|
||||
steps.build_gate.outputs.should_skip != 'true' &&
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_BUILD == '1'
|
||||
id: test-build
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||
env:
|
||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
run: |
|
||||
@@ -117,7 +148,9 @@ jobs:
|
||||
exit $STATUS
|
||||
|
||||
- name: Save Cache
|
||||
if: ${{ steps.xcode-cache-fallback.outputs.cache-hit != 'true' }}
|
||||
if: >
|
||||
steps.build_gate.outputs.should_skip != 'true' &&
|
||||
steps.xcode-cache-fallback.outputs.cache-hit != 'true'
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -126,8 +159,11 @@ jobs:
|
||||
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: Tests Run
|
||||
if: >
|
||||
steps.build_gate.outputs.should_skip != 'true' &&
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_RUN == '1'
|
||||
id: test-run
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
env:
|
||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
run: |
|
||||
@@ -139,56 +175,57 @@ jobs:
|
||||
# artifacts
|
||||
# --------------------------------------------------
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
with:
|
||||
name: encrypted-build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||
path: encrypted-build-logs.zip
|
||||
name: build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||
path: build-logs.zip
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||
if: >
|
||||
steps.build_gate.outputs.should_skip != 'true' &&
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_BUILD == '1'
|
||||
with:
|
||||
name: encrypted-tests-build-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: encrypted-tests-build-logs.zip
|
||||
name: tests-build-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: tests-build-logs.zip
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
if: >
|
||||
steps.build_gate.outputs.should_skip != 'true' &&
|
||||
vars.ENABLE_TESTS == '1' &&
|
||||
vars.ENABLE_TESTS_RUN == '1'
|
||||
with:
|
||||
name: encrypted-tests-run-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: encrypted-tests-run-logs.zip
|
||||
name: tests-run-logs-${{ env.SHORT_COMMIT }}.zip
|
||||
path: tests-run-logs.zip
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
with:
|
||||
name: SideStore-${{ env.MARKETING_VERSION }}.ipa
|
||||
path: SideStore.ipa
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
with:
|
||||
name: SideStore-${{ env.MARKETING_VERSION }}-dSYMs.zip
|
||||
path: SideStore.dSYMs.zip
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
if: steps.build_gate.outputs.should_skip != 'true' && env.DEPLOY_KEY != ''
|
||||
with:
|
||||
repository: "SideStore/apps-v2.json"
|
||||
ref: "main"
|
||||
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
path: "SideStore/apps-v2.json"
|
||||
|
||||
# --------------------------------------------------
|
||||
# deploy
|
||||
# --------------------------------------------------
|
||||
- name: Deploy
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Generate Metadata
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
run: |
|
||||
PRODUCT_NAME=$(python3 scripts/ci/workflow.py get-product-name)
|
||||
BUNDLE_ID=$(python3 scripts/ci/workflow.py get-bundle-id)
|
||||
SOURCE_JSON="_includes/source.json"
|
||||
python3 scripts/ci/workflow.py dump-project-settings
|
||||
PRODUCT_NAME=$(python3 scripts/ci/workflow.py read-product-name)
|
||||
BUNDLE_ID=$(python3 scripts/ci/workflow.py read-bundle-id)
|
||||
IPA_NAME="$PRODUCT_NAME.ipa"
|
||||
|
||||
LAST_SUCCESSFUL_COMMIT=$(python3 scripts/ci/workflow.py last-successful-commit \
|
||||
"${{ github.workflow }}" "$CHANNEL")
|
||||
|
||||
python3 scripts/ci/workflow.py deploy \
|
||||
SideStore/apps-v2.json \
|
||||
"$SOURCE_JSON" \
|
||||
python3 scripts/ci/workflow.py generate-metadata \
|
||||
"$CHANNEL" \
|
||||
"$SHORT_COMMIT" \
|
||||
"$MARKETING_VERSION" \
|
||||
@@ -197,8 +234,24 @@ jobs:
|
||||
"$IPA_NAME" \
|
||||
"$LAST_SUCCESSFUL_COMMIT"
|
||||
|
||||
RELEASE_NOTES=$(python3 scripts/ci/workflow.py retrieve-release-notes "$CHANNEL")
|
||||
- name: Deploy
|
||||
if: steps.build_gate.outputs.should_skip != 'true' && env.DEPLOY_KEY != ''
|
||||
run: |
|
||||
SOURCE_JSON="_includes/source.json"
|
||||
|
||||
python3 scripts/ci/workflow.py deploy \
|
||||
SideStore/apps-v2.json \
|
||||
"$SOURCE_JSON" \
|
||||
"$CHANNEL" \
|
||||
"$MARKETING_VERSION"
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
# upload release to GH
|
||||
# --------------------------------------------------
|
||||
- name: Upload Release
|
||||
if: steps.build_gate.outputs.should_skip != 'true'
|
||||
run: |
|
||||
python3 scripts/ci/workflow.py upload-release \
|
||||
"$RELEASE_NAME" \
|
||||
"$CHANNEL" \
|
||||
|
||||
28
.github/workflows/obsolete/alpha.yml
vendored
28
.github/workflows/obsolete/alpha.yml
vendored
@@ -1,28 +0,0 @@
|
||||
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 }}
|
||||
103
.github/workflows/obsolete/beta.yml
vendored
103
.github/workflows/obsolete/beta.yml
vendored
@@ -1,103 +0,0 @@
|
||||
name: Beta SideStore build
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+-beta.[0-9]+' # example: 1.0.0-beta.1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and upload SideStore Beta
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: 'macos-14'
|
||||
version: '15.4'
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: brew install ldid
|
||||
|
||||
- name: Change version to tag
|
||||
run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Echo version
|
||||
run: echo "${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: ${{ matrix.version }}
|
||||
|
||||
|
||||
- name: Cache Build
|
||||
uses: irgaly/xcode-cache@v1
|
||||
with:
|
||||
key: xcode-cache-deriveddata-${{ github.sha }}
|
||||
restore-keys: xcode-cache-deriveddata
|
||||
|
||||
- name: Build SideStore
|
||||
run: make build | xcpretty && exit ${PIPESTATUS[0]}
|
||||
|
||||
- name: Fakesign app
|
||||
run: make fakesign
|
||||
|
||||
- name: Convert to IPA
|
||||
run: make ipa
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get current date in AltStore date form
|
||||
id: date_altstore
|
||||
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload to new beta release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: ${{ steps.version.outputs.version }}
|
||||
tag_name: ${{ github.ref_name }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
files: SideStore.ipa
|
||||
body: |
|
||||
<!-- NOTE: to reset SideSource cache, go to `https://apps.sidestore.io/reset-cache/nightly/<sidesource key>`. This is not included in the GitHub Action since it makes draft releases so they can be edited and have a changelog. -->
|
||||
Beta builds are hand-picked builds from development commits that will allow you to try out new features earlier than normal. However, **they might contain bugs and other issues. Use at your own risk!**
|
||||
|
||||
## Changelog
|
||||
|
||||
- TODO
|
||||
|
||||
## Build Info
|
||||
|
||||
Built at (UTC): `${{ steps.date.outputs.date }}`
|
||||
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
|
||||
Commit SHA: `${{ github.sha }}`
|
||||
Version: `${{ steps.version.outputs.version }}`
|
||||
|
||||
- name: Add version to IPA file name
|
||||
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
|
||||
- name: Upload SideStore.ipa Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
path: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
|
||||
- name: Upload *.dSYM Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}-dSYM
|
||||
path: ./*.dSYM/
|
||||
@@ -1,34 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Ensure we are in root directory
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
DATE=`date -u +'%Y.%m.%d'`
|
||||
BUILD_NUM=1
|
||||
|
||||
# Use RELEASE_CHANNEL from the environment variable or default to "beta"
|
||||
RELEASE_CHANNEL=${RELEASE_CHANNEL:-"beta"}
|
||||
|
||||
write() {
|
||||
sed -e "/MARKETING_VERSION = .*/s/$/-$RELEASE_CHANNEL.$DATE.$BUILD_NUM+$(git rev-parse --short HEAD)/" -i '' Build.xcconfig
|
||||
echo "$DATE,$BUILD_NUM" > build_number.txt
|
||||
}
|
||||
|
||||
if [ ! -f "build_number.txt" ]; then
|
||||
write
|
||||
exit 0
|
||||
fi
|
||||
|
||||
LAST_DATE=`cat build_number.txt | perl -n -e '/([^,]*),([^ ]*)$/ && print $1'`
|
||||
LAST_BUILD_NUM=`cat build_number.txt | perl -n -e '/([^,]*),([^ ]*)$/ && print $2'`
|
||||
|
||||
# if [[ "$DATE" != "$LAST_DATE" ]]; then
|
||||
# write
|
||||
# else
|
||||
# BUILD_NUM=`expr $LAST_BUILD_NUM + 1`
|
||||
# write
|
||||
# fi
|
||||
|
||||
# Build number is always incremental
|
||||
BUILD_NUM=`expr $LAST_BUILD_NUM + 1`
|
||||
write
|
||||
82
.github/workflows/obsolete/nightly.yml
vendored
82
.github/workflows/obsolete/nightly.yml
vendored
@@ -1,82 +0,0 @@
|
||||
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 }}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
name: Reusable SideStore Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
is_beta:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
publish:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
is_shared_build_num:
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
release_name:
|
||||
required: true
|
||||
type: string
|
||||
release_tag:
|
||||
required: true
|
||||
type: string
|
||||
upstream_tag:
|
||||
required: true
|
||||
type: string
|
||||
upstream_name:
|
||||
required: true
|
||||
type: string
|
||||
bundle_id:
|
||||
default: com.SideStore.SideStore
|
||||
required: true
|
||||
type: string
|
||||
bundle_id_suffix:
|
||||
default: ''
|
||||
required: false
|
||||
type: string
|
||||
|
||||
secrets:
|
||||
# GITHUB_TOKEN:
|
||||
# required: true
|
||||
CROSS_REPO_PUSH_KEY:
|
||||
required: true
|
||||
BUILD_LOG_ZIP_PASSWORD:
|
||||
required: false
|
||||
|
||||
|
||||
# since build cache, test-build cache, test-run cache are involved, out of order exec if serialization is on individual jobs will wreak all sorts of havoc
|
||||
# so we serialize on the entire workflow
|
||||
concurrency:
|
||||
group: serialize-workflow
|
||||
|
||||
jobs:
|
||||
shared:
|
||||
uses: ./.github/workflows/sidestore-shared.yml
|
||||
secrets: inherit
|
||||
|
||||
build:
|
||||
needs: shared
|
||||
uses: ./.github/workflows/sidestore-build.yml
|
||||
with:
|
||||
is_beta: ${{ inputs.is_beta }}
|
||||
is_shared_build_num: ${{ inputs.is_shared_build_num }}
|
||||
release_tag: ${{ inputs.release_tag }}
|
||||
short_commit: ${{ needs.shared.outputs.short-commit }}
|
||||
bundle_id: ${{ inputs.bundle_id }}
|
||||
bundle_id_suffix: ${{ inputs.bundle_id_suffix }}
|
||||
secrets: inherit
|
||||
|
||||
# tests-build:
|
||||
# if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||
# needs: shared
|
||||
# uses: ./.github/workflows/sidestore-tests-build.yml
|
||||
# with:
|
||||
# release_tag: ${{ inputs.release_tag }}
|
||||
# short_commit: ${{ needs.shared.outputs.short-commit }}
|
||||
# secrets: inherit
|
||||
|
||||
# tests-run:
|
||||
# if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
# needs: [shared, tests-build]
|
||||
# uses: ./.github/workflows/sidestore-tests-run.yml
|
||||
# with:
|
||||
# release_tag: ${{ inputs.release_tag }}
|
||||
# short_commit: ${{ needs.shared.outputs.short-commit }}
|
||||
# secrets: inherit
|
||||
|
||||
deploy:
|
||||
# needs: [shared, build, tests-build, tests-run] # Keep tests-run in needs
|
||||
needs: [shared, build] # Keep tests-run in needs
|
||||
if: ${{ always() && (needs.tests-run.result == 'skipped' || needs.tests-run.result == 'success') }}
|
||||
uses: ./.github/workflows/sidestore-deploy.yml
|
||||
with:
|
||||
is_beta: ${{ inputs.is_beta }}
|
||||
publish: ${{ inputs.publish }}
|
||||
release_name: ${{ inputs.release_name }}
|
||||
release_tag: ${{ inputs.release_tag }}
|
||||
upstream_tag: ${{ inputs.upstream_tag }}
|
||||
upstream_name: ${{ inputs.upstream_name }}
|
||||
version: ${{ needs.build.outputs.version }}
|
||||
short_commit: ${{ needs.shared.outputs.short-commit }}
|
||||
release_channel: ${{ needs.build.outputs.release-channel }}
|
||||
marketing_version: ${{ needs.build.outputs.marketing-version }}
|
||||
bundle_id: ${{ inputs.bundle_id }}
|
||||
secrets: inherit
|
||||
358
.github/workflows/obsolete/sidestore-build.yml
vendored
358
.github/workflows/obsolete/sidestore-build.yml
vendored
@@ -1,358 +0,0 @@
|
||||
name: SideStore Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
is_beta:
|
||||
type: boolean
|
||||
is_shared_build_num:
|
||||
type: boolean
|
||||
release_tag:
|
||||
type: string
|
||||
bundle_id:
|
||||
type: string
|
||||
bundle_id_suffix:
|
||||
type: string
|
||||
short_commit:
|
||||
type: string
|
||||
secrets:
|
||||
CROSS_REPO_PUSH_KEY:
|
||||
required: true
|
||||
BUILD_LOG_ZIP_PASSWORD:
|
||||
required: false
|
||||
outputs:
|
||||
version:
|
||||
value: ${{ jobs.build.outputs.version }}
|
||||
marketing-version:
|
||||
value: ${{ jobs.build.outputs.marketing-version }}
|
||||
release-channel:
|
||||
value: ${{ jobs.build.outputs.release-channel }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build SideStore - ${{ inputs.release_tag }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: 'macos-26'
|
||||
version: '26.0'
|
||||
runs-on: ${{ matrix.os }}
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
marketing-version: ${{ steps.marketing-version.outputs.MARKETING_VERSION }}
|
||||
release-channel: ${{ steps.release-channel.outputs.RELEASE_CHANNEL }}
|
||||
|
||||
steps:
|
||||
- name: Set beta status
|
||||
run: echo "IS_BETA=${{ inputs.is_beta }}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies - ldid & xcbeautify
|
||||
run: |
|
||||
brew install ldid xcbeautify
|
||||
|
||||
- name: Set ref based on is_shared_build_num
|
||||
if: ${{ inputs.is_beta }}
|
||||
id: set_ref
|
||||
run: |
|
||||
if [ "${{ inputs.is_shared_build_num }}" == "true" ]; then
|
||||
echo "ref=main" >> $GITHUB_ENV
|
||||
else
|
||||
echo "ref=${{ inputs.release_tag }}" >> $GITHUB_ENV
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Checkout SideStore/beta-build-num repo
|
||||
if: ${{ inputs.is_beta }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'SideStore/beta-build-num'
|
||||
ref: ${{ env.ref }}
|
||||
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
path: 'SideStore/beta-build-num'
|
||||
|
||||
- name: Copy build_number.txt to repo root
|
||||
if: ${{ inputs.is_beta }}
|
||||
run: |
|
||||
cp SideStore/beta-build-num/build_number.txt .
|
||||
echo "cat build_number.txt"
|
||||
cat build_number.txt
|
||||
shell: bash
|
||||
|
||||
- name: Echo Build.xcconfig
|
||||
run: |
|
||||
echo "cat Build.xcconfig"
|
||||
cat Build.xcconfig
|
||||
shell: bash
|
||||
|
||||
- name: Set Release Channel info for build number bumper
|
||||
id: release-channel
|
||||
run: |
|
||||
RELEASE_CHANNEL="${{ inputs.release_tag }}"
|
||||
echo "RELEASE_CHANNEL=${RELEASE_CHANNEL}" >> $GITHUB_ENV
|
||||
echo "RELEASE_CHANNEL=${RELEASE_CHANNEL}" >> $GITHUB_OUTPUT
|
||||
echo "RELEASE_CHANNEL=${RELEASE_CHANNEL}"
|
||||
shell: bash
|
||||
|
||||
- name: Increase build number for beta builds
|
||||
if: ${{ inputs.is_beta }}
|
||||
run: |
|
||||
bash .github/workflows/increase-beta-build-num.sh
|
||||
shell: bash
|
||||
|
||||
- name: Extract MARKETING_VERSION from Build.xcconfig
|
||||
id: version
|
||||
run: |
|
||||
version=$(grep MARKETING_VERSION Build.xcconfig | sed -e 's/MARKETING_VERSION = //g')
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
echo "version=$version"
|
||||
shell: bash
|
||||
|
||||
- name: Set MARKETING_VERSION
|
||||
if: ${{ inputs.is_beta }}
|
||||
id: marketing-version
|
||||
run: |
|
||||
# Extract version number (e.g., "0.6.0")
|
||||
version=$(echo "${{ steps.version.outputs.version }}" | sed -E 's/^[^0-9]*([0-9]+\.[0-9]+\.[0-9]+).*/\1/')
|
||||
# Extract date (YYYYMMDD) (e.g., "20250205")
|
||||
date=$(echo "${{ steps.version.outputs.version }}" | sed -E 's/.*\.([0-9]{4})\.([0-9]{2})\.([0-9]{2})\..*/\1\2\3/')
|
||||
# Extract build number (e.g., "2")
|
||||
build_num=$(echo "${{ steps.version.outputs.version }}" | sed -E 's/.*\.([0-9]+)\+.*/\1/')
|
||||
|
||||
# Combine them into the final output
|
||||
MARKETING_VERSION="${version}-${date}.${build_num}+${{ inputs.short_commit }}"
|
||||
|
||||
echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV
|
||||
echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "MARKETING_VERSION=$MARKETING_VERSION"
|
||||
shell: bash
|
||||
|
||||
- name: Echo Updated Build.xcconfig, build_number.txt
|
||||
if: ${{ inputs.is_beta }}
|
||||
run: |
|
||||
cat Build.xcconfig
|
||||
cat build_number.txt
|
||||
shell: bash
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: ${{ matrix.version }}
|
||||
|
||||
- name: (Build) Restore Xcode & SwiftPM Cache (Exact match)
|
||||
id: xcode-cache-restore
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-build-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: (Build) Restore Xcode & SwiftPM Cache (Last Available)
|
||||
id: xcode-cache-restore-recent
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-build-${{ github.ref_name }}-
|
||||
|
||||
# - name: (Build) Cache Build
|
||||
# uses: irgaly/xcode-cache@v1.8.1
|
||||
# with:
|
||||
# key: xcode-cache-deriveddata-build-${{ github.ref_name }}-${{ github.sha }}
|
||||
# restore-keys: xcode-cache-deriveddata-build-${{ github.ref_name }}-
|
||||
# swiftpm-cache-key: xcode-cache-sourcedata-build-${{ github.ref_name }}-${{ github.sha }}
|
||||
# swiftpm-cache-restore-keys: |
|
||||
# xcode-cache-sourcedata-build-${{ github.ref_name }}-
|
||||
|
||||
- name: (Build) Clean previous build artifacts
|
||||
# using 'tee' to intercept stdout and log for detailed build-log
|
||||
run: |
|
||||
make clean
|
||||
mkdir -p build/logs
|
||||
shell: bash
|
||||
|
||||
- name: (Build) List Files and derived data
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore <<<<<<<<<<"
|
||||
find SideStore -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Dependencies <<<<<<<<<<"
|
||||
find Dependencies -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
- name: Set BundleID Suffix for Sidestore build
|
||||
run: |
|
||||
echo "BUNDLE_ID_SUFFIX=${{ inputs.bundle_id_suffix }}" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
|
||||
- name: Build SideStore.xcarchive
|
||||
# using 'tee' to intercept stdout and log for detailed build-log
|
||||
run: |
|
||||
NSUnbufferedIO=YES make -B build 2>&1 | tee -a build/logs/build.log | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]}
|
||||
shell: bash
|
||||
|
||||
- name: Fakesign app
|
||||
run: make fakesign | tee -a build/logs/build.log
|
||||
shell: bash
|
||||
|
||||
- name: Convert to IPA
|
||||
run: make ipa | tee -a build/logs/build.log
|
||||
shell: bash
|
||||
|
||||
- name: (Build) 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: (Build) List Files and Build artifacts
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Build <<<<<<<<<<"
|
||||
find build -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore <<<<<<<<<<"
|
||||
find SideStore -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore.xcarchive <<<<<<<<<<"
|
||||
find SideStore.xcarchive -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
||||
echo ""
|
||||
shell: bash
|
||||
|
||||
- name: Encrypt build-logs for upload
|
||||
id: encrypt-build-log
|
||||
run: |
|
||||
DEFAULT_BUILD_LOG_PASSWORD=12345
|
||||
|
||||
BUILD_LOG_ZIP_PASSWORD=${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
BUILD_LOG_ZIP_PASSWORD=${BUILD_LOG_ZIP_PASSWORD:-$DEFAULT_BUILD_LOG_PASSWORD}
|
||||
|
||||
if [ "$BUILD_LOG_ZIP_PASSWORD" == "$DEFAULT_BUILD_LOG_PASSWORD" ]; then
|
||||
echo "Warning: BUILD_LOG_ZIP_PASSWORD is not set. Defaulting to '${DEFAULT_BUILD_LOG_PASSWORD}'."
|
||||
fi
|
||||
|
||||
pushd build/logs && zip -e -P "$BUILD_LOG_ZIP_PASSWORD" ../../encrypted-build-logs.zip * || popd
|
||||
echo "::set-output name=encrypted::true"
|
||||
shell: bash
|
||||
|
||||
- name: Upload encrypted-build-logs.zip
|
||||
id: attach-encrypted-build-log
|
||||
if: ${{ always() && steps.encrypt-build-log.outputs.encrypted == 'true' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: encrypted-build-logs-${{ steps.version.outputs.version }}.zip
|
||||
path: encrypted-build-logs.zip
|
||||
|
||||
- name: Upload SideStore.ipa Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
path: SideStore.ipa
|
||||
|
||||
- name: Zip dSYMs
|
||||
run: zip -r -9 ./SideStore.dSYMs.zip ./SideStore.xcarchive/dSYMs
|
||||
shell: bash
|
||||
|
||||
- name: Upload *.dSYM Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}-dSYMs.zip
|
||||
path: SideStore.dSYMs.zip
|
||||
|
||||
- name: Keep rolling the build numbers for each successful build
|
||||
if: ${{ inputs.is_beta }}
|
||||
run: |
|
||||
pushd SideStore/beta-build-num/
|
||||
|
||||
echo "Configure Git user (committer details)"
|
||||
git config user.name "GitHub Actions"
|
||||
git config user.email "github-actions@github.com"
|
||||
|
||||
echo "Adding files to commit"
|
||||
git add --verbose build_number.txt
|
||||
git commit -m " - updated for ${{ inputs.release_tag }} - ${{ inputs.short_commit }} deployment" || echo "No changes to commit"
|
||||
|
||||
echo "Pushing to remote repo"
|
||||
git push --verbose
|
||||
popd
|
||||
shell: bash
|
||||
|
||||
- name: Get last successful commit
|
||||
id: get_last_commit
|
||||
run: |
|
||||
# Try to get the last successful workflow run commit
|
||||
LAST_SUCCESS_SHA=$(gh run list --branch "${{ github.ref_name }}" --status success --json headSha --jq '.[0].headSha')
|
||||
echo "LAST_SUCCESS_SHA=$LAST_SUCCESS_SHA" >> $GITHUB_OUTPUT
|
||||
echo "LAST_SUCCESS_SHA=$LAST_SUCCESS_SHA" >> $GITHUB_ENV
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
|
||||
- name: Create release notes
|
||||
run: |
|
||||
LAST_SUCCESS_SHA=${{ steps.get_last_commit.outputs.LAST_SUCCESS_SHA}}
|
||||
echo "Last successful commit SHA: $LAST_SUCCESS_SHA"
|
||||
|
||||
FROM_COMMIT=$LAST_SUCCESS_SHA
|
||||
# Check if we got a valid SHA
|
||||
if [ -z "$LAST_SUCCESS_SHA" ] || [ "$LAST_SUCCESS_SHA" = "null" ]; then
|
||||
echo "No successful run found, using initial commit of branch"
|
||||
# Get the first commit of the branch (initial commit)
|
||||
FROM_COMMIT=$(git rev-list --max-parents=0 HEAD)
|
||||
fi
|
||||
|
||||
python3 update_release_notes.py $FROM_COMMIT ${{ inputs.release_tag }} ${{ github.ref_name }}
|
||||
# cat release-notes.md
|
||||
shell: bash
|
||||
|
||||
- name: Upload release-notes.md
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-notes-${{ inputs.short_commit }}.md
|
||||
path: release-notes.md
|
||||
|
||||
- name: Upload update_release_notes.py
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: update_release_notes-${{ inputs.short_commit }}.py
|
||||
path: update_release_notes.py
|
||||
|
||||
- name: Upload update_apps.py
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: update_apps-${{ inputs.short_commit }}.py
|
||||
path: update_apps.py
|
||||
281
.github/workflows/obsolete/sidestore-deploy.yml
vendored
281
.github/workflows/obsolete/sidestore-deploy.yml
vendored
@@ -1,281 +0,0 @@
|
||||
name: SideStore Deploy
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
is_beta:
|
||||
type: boolean
|
||||
publish:
|
||||
type: boolean
|
||||
release_name:
|
||||
type: string
|
||||
release_tag:
|
||||
type: string
|
||||
upstream_tag:
|
||||
type: string
|
||||
upstream_name:
|
||||
type: string
|
||||
version:
|
||||
type: string
|
||||
short_commit:
|
||||
type: string
|
||||
marketing_version:
|
||||
type: string
|
||||
release_channel:
|
||||
type: string
|
||||
bundle_id:
|
||||
type: string
|
||||
secrets:
|
||||
CROSS_REPO_PUSH_KEY:
|
||||
required: true
|
||||
# GITHUB_TOKEN:
|
||||
# required: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy SideStore - ${{ inputs.release_tag }}
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- name: Download IPA artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ inputs.version }}.ipa
|
||||
|
||||
- name: Download dSYM artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ inputs.version }}-dSYMs.zip
|
||||
|
||||
- name: Download encrypted-build-logs artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: encrypted-build-logs-${{ inputs.version }}.zip
|
||||
|
||||
- name: Download encrypted-tests-build-logs artifact
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: encrypted-tests-build-logs-${{ inputs.short_commit }}.zip
|
||||
|
||||
- name: Download encrypted-tests-run-logs artifact
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: encrypted-tests-run-logs-${{ inputs.short_commit }}.zip
|
||||
|
||||
- name: Download tests-recording artifact
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: tests-recording-${{ inputs.short_commit }}.mp4
|
||||
|
||||
- name: Download test-results artifact
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: test-results-${{ inputs.short_commit }}.zip
|
||||
|
||||
- name: Download release-notes.md
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: release-notes-${{ inputs.short_commit }}.md
|
||||
|
||||
- name: Download update_release_notes.py
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: update_release_notes-${{ inputs.short_commit }}.py
|
||||
|
||||
- name: Download update_apps.py
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: update_apps-${{ inputs.short_commit }}.py
|
||||
|
||||
- name: Read release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
CONTENT=$(python3 update_release_notes.py --retrieve ${{ inputs.release_tag }})
|
||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$CONTENT" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: List files before upload
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
find . -maxdepth 4 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
shell: bash
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Get current date in AltStore date form
|
||||
id: date_altstore
|
||||
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
|
||||
- name: List files to upload
|
||||
id: list_uploads
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
find . -maxdepth 4 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
FILES="SideStore.ipa SideStore.dSYMs.zip encrypted-build-logs.zip"
|
||||
|
||||
if [[ "${{ vars.ENABLE_TESTS }}" == "1" && "${{ vars.ENABLE_TESTS_BUILD }}" == "1" ]]; then
|
||||
FILES="$FILES encrypted-tests-build-logs.zip"
|
||||
fi
|
||||
|
||||
if [[ "${{ vars.ENABLE_TESTS }}" == "1" && "${{ vars.ENABLE_TESTS_RUN }}" == "1" ]]; then
|
||||
FILES="$FILES encrypted-tests-run-logs.zip test-results.zip tests-recording.mp4"
|
||||
fi
|
||||
|
||||
echo "Final upload list:"
|
||||
for f in $FILES; do
|
||||
if [[ -f "$f" ]]; then
|
||||
echo " ✓ $f"
|
||||
else
|
||||
echo " - $f (missing)"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "files=$FILES" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set Upstream Recommendation
|
||||
id: upstream_recommendation
|
||||
run: |
|
||||
UPSTREAM_NAME=$(echo "${{ inputs.upstream_name }}" | tr '[:upper:]' '[:lower:]')
|
||||
if [[ "$UPSTREAM_NAME" != "nightly" ]]; then
|
||||
echo "content<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "If you want to try out new features early but want a lower chance of bugs, you can look at [SideStore ${{ inputs.upstream_name }}](https://github.com/${{ github.repository }}/releases?q=${{ inputs.upstream_tag }})." >> $GITHUB_OUTPUT
|
||||
echo "" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "content=" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Upload to releases
|
||||
uses: IsaacShelton/update-existing-release@v1.3.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release: ${{ inputs.release_name }}
|
||||
tag: ${{ inputs.release_tag }}
|
||||
prerelease: ${{ inputs.is_beta }}
|
||||
files: ${{ steps.list_uploads.outputs.files }}
|
||||
body: |
|
||||
This is an ⚠️ **EXPERIMENTAL** ⚠️ ${{ inputs.release_name }} build for commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}).
|
||||
|
||||
${{ inputs.release_name }} builds are **extremely experimental builds only meant to be used by developers and beta testers. They often contain bugs and experimental features. Use at your own risk!**
|
||||
|
||||
${{ steps.upstream_recommendation.outputs.content }}
|
||||
## Build Info
|
||||
|
||||
Built at (UTC): `${{ steps.date.outputs.date }}`
|
||||
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
|
||||
Commit SHA: `${{ github.sha }}`
|
||||
Version: `${{ inputs.version }}`
|
||||
|
||||
${{ steps.release_notes.outputs.content }}
|
||||
|
||||
- name: Get formatted date
|
||||
run: |
|
||||
FORMATTED_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
echo "Formatted date: $FORMATTED_DATE"
|
||||
echo "FORMATTED_DATE=$FORMATTED_DATE" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Get size of IPA in bytes (macOS/Linux)
|
||||
run: |
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
# macOS
|
||||
IPA_SIZE=$(stat -f %z SideStore.ipa)
|
||||
else
|
||||
# Linux
|
||||
IPA_SIZE=$(stat -c %s SideStore.ipa)
|
||||
fi
|
||||
echo "IPA size in bytes: $IPA_SIZE"
|
||||
echo "IPA_SIZE=$IPA_SIZE" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Compute SHA-256 of IPA
|
||||
run: |
|
||||
SHA256_HASH=$(shasum -a 256 SideStore.ipa | awk '{ print $1 }')
|
||||
echo "SHA-256 Hash: $SHA256_HASH"
|
||||
echo "SHA256_HASH=$SHA256_HASH" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Set Release Info variables
|
||||
run: |
|
||||
echo "IS_BETA=${{ inputs.is_beta }}" >> $GITHUB_ENV
|
||||
echo "BUNDLE_IDENTIFIER=${{ inputs.bundle_id }}" >> $GITHUB_ENV
|
||||
echo "VERSION_IPA=${{ inputs.marketing_version }}" >> $GITHUB_ENV
|
||||
echo "VERSION_DATE=$FORMATTED_DATE" >> $GITHUB_ENV
|
||||
echo "RELEASE_CHANNEL=${{ inputs.release_channel }}" >> $GITHUB_ENV
|
||||
echo "SIZE=$IPA_SIZE" >> $GITHUB_ENV
|
||||
echo "SHA256=$SHA256_HASH" >> $GITHUB_ENV
|
||||
echo "DOWNLOAD_URL=https://github.com/SideStore/SideStore/releases/download/${{ inputs.release_tag }}/SideStore.ipa" >> $GITHUB_ENV
|
||||
|
||||
# Format localized description
|
||||
get_description() {
|
||||
cat <<EOF
|
||||
This is release for:
|
||||
- version: "${{ inputs.version }}"
|
||||
- revision: "${{ inputs.short_commit }}"
|
||||
- timestamp: "${{ steps.date.outputs.date }}"
|
||||
|
||||
Release Notes:
|
||||
${{ steps.release_notes.outputs.content }}
|
||||
EOF
|
||||
}
|
||||
|
||||
LOCALIZED_DESCRIPTION=$(get_description)
|
||||
echo "$LOCALIZED_DESCRIPTION"
|
||||
|
||||
# multiline strings
|
||||
echo "LOCALIZED_DESCRIPTION<<EOF" >> $GITHUB_ENV
|
||||
echo "$LOCALIZED_DESCRIPTION" >> $GITHUB_ENV
|
||||
echo "EOF" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Check if Publish updates is set
|
||||
id: check_publish
|
||||
run: |
|
||||
echo "Publish updates to source.json = ${{ inputs.publish }}"
|
||||
shell: bash
|
||||
|
||||
- name: Checkout SideStore/apps-v2.json
|
||||
if: ${{ inputs.is_beta && inputs.publish }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: 'SideStore/apps-v2.json'
|
||||
ref: 'main' # this branch is shared by all beta builds, so beta build workflows are serialized
|
||||
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||
path: 'SideStore/apps-v2.json'
|
||||
|
||||
# for stable builds, let the user manually edit the source.json
|
||||
- name: Publish to SideStore/apps-v2.json
|
||||
if: ${{ inputs.is_beta && inputs.publish }}
|
||||
id: publish-release
|
||||
shell: bash
|
||||
run: |
|
||||
# Copy and execute the update script
|
||||
pushd SideStore/apps-v2.json/
|
||||
|
||||
# Configure Git user (committer details)
|
||||
git config user.name "GitHub Actions"
|
||||
git config user.email "github-actions@github.com"
|
||||
|
||||
# update the source.json
|
||||
python3 ../../update_apps.py "./_includes/source.json"
|
||||
|
||||
# Commit changes and push using SSH
|
||||
git add --verbose ./_includes/source.json
|
||||
git commit -m " - updated for ${{ inputs.short_commit }} deployment" || echo "No changes to commit"
|
||||
|
||||
git push --verbose
|
||||
popd
|
||||
24
.github/workflows/obsolete/sidestore-shared.yml
vendored
24
.github/workflows/obsolete/sidestore-shared.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: SideStore Shared
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
short-commit:
|
||||
value: ${{ jobs.shared.outputs.short-commit }}
|
||||
|
||||
jobs:
|
||||
shared:
|
||||
name: Shared Steps
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: 'macos-15'
|
||||
steps:
|
||||
- name: Set short commit hash
|
||||
id: commit-id
|
||||
run: |
|
||||
# SHORT_COMMIT="${{ github.sha }}"
|
||||
SHORT_COMMIT=${GITHUB_SHA:0:7}
|
||||
echo "Short commit hash: $SHORT_COMMIT"
|
||||
echo "SHORT_COMMIT=$SHORT_COMMIT" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
short-commit: ${{ steps.commit-id.outputs.SHORT_COMMIT }}
|
||||
165
.github/workflows/obsolete/sidestore-tests-build.yml
vendored
165
.github/workflows/obsolete/sidestore-tests-build.yml
vendored
@@ -1,165 +0,0 @@
|
||||
name: SideStore Tests Build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
release_tag:
|
||||
type: string
|
||||
short_commit:
|
||||
type: string
|
||||
secrets:
|
||||
BUILD_LOG_ZIP_PASSWORD:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
tests-build:
|
||||
name: Tests-Build SideStore - ${{ inputs.release_tag }}
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: 'macos-26'
|
||||
version: '26.0'
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies - xcbeautify
|
||||
run: |
|
||||
brew install xcbeautify
|
||||
shell: bash
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: '26.0'
|
||||
|
||||
# - name: (Tests-Build) Cache Build
|
||||
# uses: irgaly/xcode-cache@v1.8.1
|
||||
# with:
|
||||
# key: xcode-cache-deriveddata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||
# # tests shouldn't restore cache unless it is same build
|
||||
# # restore-keys: xcode-cache-deriveddata-test-${{ github.ref_name }}-
|
||||
# swiftpm-cache-key: xcode-cache-sourcedata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||
# swiftpm-cache-restore-keys: |
|
||||
# xcode-cache-sourcedata-test-${{ github.ref_name }}-
|
||||
# delete-used-deriveddata-cache: true
|
||||
|
||||
- name: (Tests-Build) Restore Xcode & SwiftPM Cache (Exact match)
|
||||
id: xcode-cache-restore
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-tests-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: (Tests-Build) Restore Xcode & SwiftPM Cache (Last Available)
|
||||
id: xcode-cache-restore-recent
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-tests-${{ github.ref_name }}-
|
||||
|
||||
- name: Clean Derived Data (if required)
|
||||
if: ${{ vars.PERFORM_CLEAN_TESTS_BUILD == '1' }}
|
||||
run: |
|
||||
rm -rf ~/Library/Developer/Xcode/DerivedData/
|
||||
make clean
|
||||
xcodebuild clean
|
||||
shell: bash
|
||||
|
||||
- name: (Tests-Build) Clean previous build artifacts
|
||||
run: |
|
||||
make clean
|
||||
mkdir -p build/logs
|
||||
shell: bash
|
||||
|
||||
- name: (Tests-Build) List Files and derived data
|
||||
shell: bash
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore <<<<<<<<<<"
|
||||
find SideStore -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Dependencies <<<<<<<<<<"
|
||||
find Dependencies -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
- name: Build SideStore Tests
|
||||
# using 'tee' to intercept stdout and log for detailed build-log
|
||||
shell: bash
|
||||
run: |
|
||||
NSUnbufferedIO=YES make -B build-tests 2>&1 | tee -a build/logs/tests-build.log | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]}
|
||||
|
||||
- name: (Tests-Build) 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-tests-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: (Tests-Build) List Files and Build artifacts
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Build <<<<<<<<<<"
|
||||
find build -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
find ~/Library/Developer/Xcode/DerivedData -maxdepth 8 -exec ls -ld {} + | grep "Build/Products" >> tests-build-deriveddata.txt || true
|
||||
echo ""
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: tests-build-deriveddata-${{ inputs.short_commit }}.txt
|
||||
path: tests-build-deriveddata.txt
|
||||
|
||||
- name: Encrypt tests-build-logs for upload
|
||||
id: encrypt-test-log
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
DEFAULT_BUILD_LOG_PASSWORD=12345
|
||||
|
||||
BUILD_LOG_ZIP_PASSWORD=${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
BUILD_LOG_ZIP_PASSWORD=${BUILD_LOG_ZIP_PASSWORD:-$DEFAULT_BUILD_LOG_PASSWORD}
|
||||
|
||||
if [ "$BUILD_LOG_ZIP_PASSWORD" == "$DEFAULT_BUILD_LOG_PASSWORD" ]; then
|
||||
echo "Warning: BUILD_LOG_ZIP_PASSWORD is not set. Defaulting to '${DEFAULT_BUILD_LOG_PASSWORD}'."
|
||||
fi
|
||||
|
||||
pushd build/logs && zip -e -P "$BUILD_LOG_ZIP_PASSWORD" ../../encrypted-tests-build-logs.zip * || popd
|
||||
echo "::set-output name=encrypted::true"
|
||||
|
||||
- name: Upload encrypted-tests-build-logs.zip
|
||||
id: attach-encrypted-test-log
|
||||
if: always() && steps.encrypt-test-log.outputs.encrypted == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: encrypted-tests-build-logs-${{ inputs.short_commit }}.zip
|
||||
path: encrypted-tests-build-logs.zip
|
||||
196
.github/workflows/obsolete/sidestore-tests-run.yml
vendored
196
.github/workflows/obsolete/sidestore-tests-run.yml
vendored
@@ -1,196 +0,0 @@
|
||||
name: SideStore Tests Run
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
release_tag:
|
||||
type: string
|
||||
short_commit:
|
||||
type: string
|
||||
secrets:
|
||||
BUILD_LOG_ZIP_PASSWORD:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
tests-run:
|
||||
name: Tests-Run SideStore - ${{ inputs.release_tag }}
|
||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: 'macos-26'
|
||||
version: '26.0'
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Boot Simulator async(nohup) for testing
|
||||
run: |
|
||||
mkdir -p build/logs
|
||||
nohup make -B boot-sim-async </dev/null >> build/logs/tests-run.log 2>&1 &
|
||||
shell: bash
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: '26.0'
|
||||
|
||||
# - name: (Tests-Run) Cache Build
|
||||
# uses: irgaly/xcode-cache@v1.8.1
|
||||
# with:
|
||||
# # This comes from
|
||||
# key: xcode-cache-deriveddata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||
# swiftpm-cache-key: xcode-cache-sourcedata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: (Tests-Build) Restore Xcode & SwiftPM Cache (Exact match) [from tests-build job]
|
||||
id: xcode-cache-restore
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-tests-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: (Tests-Run) Clean previous build artifacts
|
||||
run: |
|
||||
make clean
|
||||
mkdir -p build/logs
|
||||
shell: bash
|
||||
|
||||
- name: (Tests-Run) List Files and derived data
|
||||
shell: bash
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore <<<<<<<<<<"
|
||||
find SideStore -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Dependencies <<<<<<<<<<"
|
||||
find Dependencies -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
find ~/Library/Developer/Xcode/DerivedData -maxdepth 8 -exec ls -ld {} + | grep "Build/Products" >> tests-run-deriveddata.txt || true
|
||||
echo ""
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: tests-run-deriveddata-${{ inputs.short_commit }}.txt
|
||||
path: tests-run-deriveddata.txt
|
||||
|
||||
# we expect simulator to have been booted by now, so exit otherwise
|
||||
- name: Simulator Boot Check
|
||||
run: |
|
||||
mkdir -p build/logs
|
||||
make -B sim-boot-check | tee -a build/logs/tests-run.log
|
||||
exit ${PIPESTATUS[0]}
|
||||
shell: bash
|
||||
|
||||
- name: Start Recording UI tests (if DEBUG_RECORD_TESTS is set to 1)
|
||||
if: ${{ vars.DEBUG_RECORD_TESTS == '1' }}
|
||||
run: |
|
||||
nohup xcrun simctl io booted recordVideo -f tests-recording.mp4 --codec h264 </dev/null > tests-recording.log 2>&1 &
|
||||
RECORD_PID=$!
|
||||
echo "RECORD_PID=$RECORD_PID" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- name: Run SideStore Tests
|
||||
# using 'tee' to intercept stdout and log for detailed build-log
|
||||
run: |
|
||||
make run-tests 2>&1 | tee -a build/logs/tests-run.log && exit ${PIPESTATUS[0]}
|
||||
# NSUnbufferedIO=YES make -B run-tests 2>&1 | tee build/logs/tests-run.log | xcpretty -r junit --output ./build/tests/test-results.xml && exit ${PIPESTATUS[0]}
|
||||
shell: bash
|
||||
|
||||
- name: Stop Recording tests
|
||||
if: ${{ always() && env.RECORD_PID != '' }}
|
||||
run: |
|
||||
kill -INT ${{ env.RECORD_PID }}
|
||||
shell: bash
|
||||
|
||||
- name: (Tests-Run) List Files and Build artifacts
|
||||
if: always()
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Build <<<<<<<<<<"
|
||||
find build -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
shell: bash
|
||||
|
||||
- name: Encrypt tests-run-logs for upload
|
||||
id: encrypt-test-log
|
||||
if: always()
|
||||
run: |
|
||||
DEFAULT_BUILD_LOG_PASSWORD=12345
|
||||
|
||||
BUILD_LOG_ZIP_PASSWORD=${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
BUILD_LOG_ZIP_PASSWORD=${BUILD_LOG_ZIP_PASSWORD:-$DEFAULT_BUILD_LOG_PASSWORD}
|
||||
|
||||
if [ "$BUILD_LOG_ZIP_PASSWORD" == "$DEFAULT_BUILD_LOG_PASSWORD" ]; then
|
||||
echo "Warning: BUILD_LOG_ZIP_PASSWORD is not set. Defaulting to '${DEFAULT_BUILD_LOG_PASSWORD}'."
|
||||
fi
|
||||
|
||||
pushd build/logs && zip -e -P "$BUILD_LOG_ZIP_PASSWORD" ../../encrypted-tests-run-logs.zip * || popd
|
||||
echo "::set-output name=encrypted::true"
|
||||
shell: bash
|
||||
|
||||
- name: Upload encrypted-tests-run-logs.zip
|
||||
id: attach-encrypted-test-log
|
||||
if: always() && steps.encrypt-test-log.outputs.encrypted == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: encrypted-tests-run-logs-${{ inputs.short_commit }}.zip
|
||||
path: encrypted-tests-run-logs.zip
|
||||
|
||||
- name: Print tests-recording.log contents (if exists)
|
||||
if: ${{ always() && env.RECORD_PID != '' }}
|
||||
run: |
|
||||
if [ -f tests-recording.log ]; then
|
||||
echo "tests-recording.log found. Its contents:"
|
||||
cat tests-recording.log
|
||||
else
|
||||
echo "tests-recording.log not found."
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Check for tests-recording.mp4 presence
|
||||
id: check-recording
|
||||
if: ${{ always() && env.RECORD_PID != '' }}
|
||||
run: |
|
||||
if [ -f tests-recording.mp4 ]; then
|
||||
echo "::set-output name=found::true"
|
||||
echo "tests-recording.mp4 found."
|
||||
else
|
||||
echo "tests-recording.mp4 not found, skipping upload."
|
||||
echo "::set-output name=found::false"
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Upload tests-recording.mp4
|
||||
id: upload-recording
|
||||
if: ${{ always() && steps.check-recording.outputs.found == 'true' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tests-recording-${{ inputs.short_commit }}.mp4
|
||||
path: tests-recording.mp4
|
||||
|
||||
- name: Zip test-results
|
||||
run: zip -r -9 ./test-results.zip ./build/tests
|
||||
shell: bash
|
||||
|
||||
- name: Upload Test Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: test-results-${{ inputs.short_commit }}.zip
|
||||
path: test-results.zip
|
||||
132
.github/workflows/pr.yml
vendored
132
.github/workflows/pr.yml
vendored
@@ -1,98 +1,90 @@
|
||||
name: Pull Request SideStore build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# types: [opened, synchronize, reopened, ready_for_review, converted_to_draft]
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
concurrency:
|
||||
group: pr-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and upload SideStore
|
||||
if: ${{ github.event.pull_request.draft == false }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: 'macos-14'
|
||||
version: '16.1'
|
||||
runs-on: macos-26
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 1 # shallow clone just for PR
|
||||
|
||||
- name: Install dependencies
|
||||
run: brew install ldid
|
||||
- run: brew install ldid xcbeautify
|
||||
|
||||
- name: Install xcbeautify
|
||||
run: brew install xcbeautify
|
||||
|
||||
- name: Add PR suffix to version
|
||||
run: sed -e "/MARKETING_VERSION = .*/s/\$/-pr.${{ github.event.pull_request.number }}+$(git rev-parse --short ${COMMIT:-HEAD})/" -i '' Build.xcconfig
|
||||
env:
|
||||
COMMIT: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Get version
|
||||
id: version
|
||||
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Echo version
|
||||
run: echo "${{ steps.version.outputs.version }}"
|
||||
- name: Setup Env
|
||||
run: |
|
||||
MARKETING_VERSION=$(python3 scripts/ci/workflow.py get-marketing-version)
|
||||
SHORT_COMMIT=$(git rev-parse --short ${{ github.event.pull_request.head.sha }})
|
||||
NORMALIZED_VERSION="${MARKETING_VERSION}-pr.${{ github.event.pull_request.number }}+${SHORT_COMMIT}"
|
||||
python3 scripts/ci/workflow.py set-marketing-version "$NORMALIZED_VERSION"
|
||||
echo "SHORT_COMMIT=$SHORT_COMMIT" | tee -a $GITHUB_ENV
|
||||
echo "MARKETING_VERSION=$NORMALIZED_VERSION" | tee -a $GITHUB_ENV
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: ${{ matrix.version }}
|
||||
xcode-version: "26.2"
|
||||
|
||||
- name: Cache Build
|
||||
uses: irgaly/xcode-cache@v1
|
||||
- name: Restore Cache (exact)
|
||||
id: xcode-cache-exact
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
key: xcode-cache-deriveddata-${{ github.sha }}
|
||||
restore-keys: xcode-cache-deriveddata-
|
||||
swiftpm-cache-key: xcode-cache-sourcedata-${{ github.sha }}
|
||||
swiftpm-cache-restore-keys: |
|
||||
xcode-cache-sourcedata-
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: List Files and derived data
|
||||
- name: Restore Cache (last)
|
||||
if: steps.xcode-cache-exact.outputs.cache-hit != 'true'
|
||||
id: xcode-cache-fallback
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-build-cache-${{ github.ref_name }}-
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore <<<<<<<<<<"
|
||||
find SideStore -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Dependencies <<<<<<<<<<"
|
||||
find Dependencies -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
||||
echo ""
|
||||
python3 scripts/ci/workflow.py build; STATUS=$?
|
||||
python3 scripts/ci/workflow.py encrypt-build
|
||||
mv SideStore.ipa SideStore-${{ env.MARKETING_VERSION }}.ipa
|
||||
exit $STATUS
|
||||
|
||||
|
||||
- name: Build SideStore
|
||||
run: NSUnbufferedIO=YES make build 2>&1 | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]}
|
||||
|
||||
- name: Fakesign app
|
||||
run: make fakesign
|
||||
|
||||
- name: Convert to IPA
|
||||
run: make ipa
|
||||
|
||||
- name: Add version to IPA file name
|
||||
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
|
||||
- name: Upload SideStore.ipa Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
- name: Save Cache
|
||||
if: ${{ steps.xcode-cache-fallback.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
path: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
- name: Upload *.dSYM Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}-dSYM
|
||||
path: ./SideStore.xcarchive/dSYMs/*
|
||||
name: build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||
path: build-logs.zip
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ env.MARKETING_VERSION }}.ipa
|
||||
path: SideStore-${{ env.MARKETING_VERSION }}.ipa
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ env.MARKETING_VERSION }}-dSYMs.zip
|
||||
path: SideStore.dSYMs.zip
|
||||
|
||||
261
.github/workflows/stable.yml
vendored
261
.github/workflows/stable.yml
vendored
@@ -1,242 +1,135 @@
|
||||
name: Stable SideStore build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+' # example: 1.0.0
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build SideStore - stable (on tag push)
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: 'macos-26'
|
||||
version: '26.0'
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: Build SideStore - stable
|
||||
runs-on: macos-26
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CHANNEL: stable
|
||||
UPSTREAM_CHANNEL: ""
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Echo Build.xcconfig
|
||||
- name: Find Last Successful commit
|
||||
run: |
|
||||
echo "cat Build.xcconfig"
|
||||
cat Build.xcconfig
|
||||
shell: bash
|
||||
LAST_SUCCESSFUL_COMMIT=$(python3 scripts/ci/workflow.py last-successful-commit \
|
||||
"true" || echo "")
|
||||
echo "LAST_SUCCESSFUL_COMMIT=$LAST_SUCCESSFUL_COMMIT" | tee -a $GITHUB_ENV
|
||||
|
||||
# - name: Change MARKETING_VERSION to the pushed tag that triggered this build
|
||||
# run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
|
||||
- run: brew install ldid xcbeautify
|
||||
|
||||
- name: Echo Updated Build.xcconfig
|
||||
- name: Setup Env
|
||||
run: |
|
||||
cat Build.xcconfig
|
||||
shell: bash
|
||||
MARKETING_VERSION=$(python3 scripts/ci/workflow.py get-marketing-version)
|
||||
SHORT_COMMIT=$(python3 scripts/ci/workflow.py commit-id)
|
||||
|
||||
- name: Extract MARKETING_VERSION from Build.xcconfig
|
||||
id: version
|
||||
run: |
|
||||
version=$(grep MARKETING_VERSION Build.xcconfig | sed -e 's/MARKETING_VERSION = //g')
|
||||
echo "version=$version" >> $GITHUB_OUTPUT
|
||||
echo "version=$version"
|
||||
|
||||
echo "MARKETING_VERSION=$version" >> $GITHUB_ENV
|
||||
echo "MARKETING_VERSION=$version" >> $GITHUB_OUTPUT
|
||||
echo "MARKETING_VERSION=$version"
|
||||
|
||||
shell: bash
|
||||
|
||||
- name: Fail the build if pushed tag and embedded MARKETING_VERSION in Build.xcconfig are mismatching
|
||||
run: |
|
||||
if [ "$MARKETING_VERSION" != "${{ github.ref_name }}" ]; then
|
||||
echo 'Version mismatch: $tag != $marketing_version ... '
|
||||
echo " expected-tag : $MARKETING_VERSION"
|
||||
echo " pushed-tag : ${{ github.ref_name }}"
|
||||
echo "Version mismatch"
|
||||
echo "Build.xcconfig: $MARKETING_VERSION"
|
||||
echo "Tag: ${{ github.ref_name }}"
|
||||
exit 1
|
||||
fi
|
||||
echo 'Version matches: $tag == $marketing_version ... '
|
||||
echo " expected-tag : $MARKETING_VERSION"
|
||||
echo " pushed-tag : ${{ github.ref_name }}"
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies - ldid & xcbeautify
|
||||
run: |
|
||||
brew install ldid xcbeautify
|
||||
echo "MARKETING_VERSION=$MARKETING_VERSION" | tee -a $GITHUB_ENV
|
||||
echo "SHORT_COMMIT=$SHORT_COMMIT" | tee -a $GITHUB_ENV
|
||||
|
||||
- name: Setup Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||
with:
|
||||
xcode-version: ${{ matrix.version }}
|
||||
xcode-version: "26.0"
|
||||
|
||||
- name: (Build) Restore Xcode & SwiftPM Cache (Exact match)
|
||||
id: xcode-cache-restore
|
||||
- name: Restore Cache (exact)
|
||||
id: xcode-cache-exact
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-build-stable-${{ github.sha }}
|
||||
key: xcode-build-cache-stable-${{ github.sha }}
|
||||
|
||||
- name: (Build) Restore Xcode & SwiftPM Cache (Last Available)
|
||||
id: xcode-cache-restore-recent
|
||||
- name: Restore Cache (last)
|
||||
if: steps.xcode-cache-exact.outputs.cache-hit != 'true'
|
||||
id: xcode-cache-fallback
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-build-stable-
|
||||
key: xcode-build-cache-stable-
|
||||
|
||||
- name: (Build) Clean previous build artifacts
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
run: |
|
||||
make clean
|
||||
mkdir -p build/logs
|
||||
shell: bash
|
||||
python3 scripts/ci/workflow.py build; STATUS=$?
|
||||
python3 scripts/ci/workflow.py encrypt-build
|
||||
exit $STATUS
|
||||
|
||||
- name: (Build) List Files and derived data
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore <<<<<<<<<<"
|
||||
find SideStore -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Dependencies <<<<<<<<<<"
|
||||
find Dependencies -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
- name: Build SideStore.xcarchive
|
||||
# using 'tee' to intercept stdout and log for detailed build-log
|
||||
run: |
|
||||
NSUnbufferedIO=YES make -B build 2>&1 | tee -a build/logs/build.log | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]}
|
||||
shell: bash
|
||||
|
||||
- name: Fakesign app
|
||||
run: make fakesign | tee -a build/logs/build.log
|
||||
shell: bash
|
||||
|
||||
- name: Convert to IPA
|
||||
run: make ipa | tee -a build/logs/build.log
|
||||
shell: bash
|
||||
|
||||
- name: (Build) Save Xcode & SwiftPM Cache
|
||||
id: cache-save
|
||||
if: ${{ steps.xcode-cache-restore.outputs.cache-hit != 'true' }}
|
||||
- name: Save Cache
|
||||
if: ${{ steps.xcode-cache-fallback.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Developer/Xcode/DerivedData
|
||||
~/Library/Caches/org.swift.swiftpm
|
||||
key: xcode-cache-build-stable-${{ github.sha }}
|
||||
|
||||
- name: (Build) List Files and Build artifacts
|
||||
run: |
|
||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||
ls -la .
|
||||
echo ""
|
||||
key: xcode-build-cache-stable-${{ github.sha }}
|
||||
|
||||
echo ">>>>>>>>> Build <<<<<<<<<<"
|
||||
find build -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore <<<<<<<<<<"
|
||||
find SideStore -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> SideStore.xcarchive <<<<<<<<<<"
|
||||
find SideStore.xcarchive -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||
echo ""
|
||||
|
||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
||||
echo ""
|
||||
shell: bash
|
||||
|
||||
- name: Encrypt build-logs for upload
|
||||
id: encrypt-build-log
|
||||
run: |
|
||||
DEFAULT_BUILD_LOG_PASSWORD=12345
|
||||
|
||||
BUILD_LOG_ZIP_PASSWORD=${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||
BUILD_LOG_ZIP_PASSWORD=${BUILD_LOG_ZIP_PASSWORD:-$DEFAULT_BUILD_LOG_PASSWORD}
|
||||
|
||||
if [ "$BUILD_LOG_ZIP_PASSWORD" == "$DEFAULT_BUILD_LOG_PASSWORD" ]; then
|
||||
echo "Warning: BUILD_LOG_ZIP_PASSWORD is not set. Defaulting to '${DEFAULT_BUILD_LOG_PASSWORD}'."
|
||||
fi
|
||||
|
||||
pushd build/logs && zip -e -P "$BUILD_LOG_ZIP_PASSWORD" ../../encrypted-build-logs.zip * || popd
|
||||
echo "::set-output name=encrypted::true"
|
||||
shell: bash
|
||||
|
||||
- name: Upload encrypted-build-logs.zip
|
||||
id: attach-encrypted-build-log
|
||||
if: ${{ always() && steps.encrypt-build-log.outputs.encrypted == 'true' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: encrypted-build-logs-${{ steps.version.outputs.version }}.zip
|
||||
path: encrypted-build-logs.zip
|
||||
name: build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||
path: build-logs.zip
|
||||
|
||||
- name: Upload SideStore.ipa Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}.ipa
|
||||
name: SideStore-${{ env.MARKETING_VERSION }}.ipa
|
||||
path: SideStore.ipa
|
||||
|
||||
- name: Zip dSYMs
|
||||
run: zip -r -9 ./SideStore.dSYMs.zip ./SideStore.xcarchive/dSYMs
|
||||
shell: bash
|
||||
|
||||
- name: Upload *.dSYM Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: SideStore-${{ steps.version.outputs.version }}-dSYMs.zip
|
||||
name: SideStore-${{ env.MARKETING_VERSION }}-dSYMs.zip
|
||||
path: SideStore.dSYMs.zip
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
- name: Generate Metadata
|
||||
run: |
|
||||
python3 scripts/ci/workflow.py dump-project-settings
|
||||
PRODUCT_NAME=$(python3 scripts/ci/workflow.py read-product-name)
|
||||
BUNDLE_ID=$(python3 scripts/ci/workflow.py read-bundle-id)
|
||||
IPA_NAME="$PRODUCT_NAME.ipa"
|
||||
|
||||
- name: Get current date in AltStore date form
|
||||
id: date_altstore
|
||||
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
python3 scripts/ci/workflow.py generate-metadata \
|
||||
"${{ github.ref_name }}" \
|
||||
"$SHORT_COMMIT" \
|
||||
"$MARKETING_VERSION" \
|
||||
"$CHANNEL" \
|
||||
"$BUNDLE_ID" \
|
||||
"$IPA_NAME" \
|
||||
"$LAST_SUCCESSFUL_COMMIT"
|
||||
|
||||
- name: Upload to releases
|
||||
uses: IsaacShelton/update-existing-release@v1.3.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
draft: true
|
||||
release: ${{ github.ref_name }} # name
|
||||
tag: ${{ github.ref_name }}
|
||||
# stick with what the user pushed, do not use latest commit or anything,
|
||||
# ex: if we want to go back to previous release due to hot issue, dev can create a new tag pointing to that older working tag/commit so as to keep it as an update (to revert major issue)
|
||||
# in this case we do not want the tag to be auto-updated to latest
|
||||
updateTag: false
|
||||
prerelease: false
|
||||
files: >
|
||||
SideStore.ipa
|
||||
SideStore.dSYMs.zip
|
||||
encrypted-build-logs.zip
|
||||
body: |
|
||||
<!-- NOTE: to reset SideSource cache, go to `https://apps.sidestore.io/reset-cache/nightly/<sidesource key>`. This is not included in the GitHub Action since it makes draft releases so they can be edited and have a changelog. -->
|
||||
## Changelog
|
||||
|
||||
- TODO
|
||||
|
||||
## Build Info
|
||||
|
||||
Built at (UTC): `${{ steps.date.outputs.date }}`
|
||||
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
|
||||
Commit SHA: `${{ github.sha }}`
|
||||
Version: `${{ steps.version.outputs.version }}`
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
python3 scripts/ci/workflow.py upload-release \
|
||||
"${{ github.ref_name }}" \
|
||||
"${{ github.ref_name }}" \
|
||||
"$GITHUB_SHA" \
|
||||
"$GITHUB_REPOSITORY" \
|
||||
"$UPSTREAM_CHANNEL" \
|
||||
"true"
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -69,4 +69,7 @@ SideStore/.skip-prebuilt-fetch-em_proxy
|
||||
test-recording.mp4
|
||||
test-recording.log
|
||||
altstore-sources.md
|
||||
local-build.sh
|
||||
local-build.sh
|
||||
|
||||
source-metadata.json
|
||||
release-notes.md
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2610"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA60C44C93D7A30E3695DD59"
|
||||
BuildableName = "libem_proxy_static.a"
|
||||
BlueprintName = "em_proxy-staticlib"
|
||||
ReferencedContainer = "container:Dependencies/em_proxy.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA60C44C93D7A30E3695DD59"
|
||||
BuildableName = "libem_proxy_static.a"
|
||||
BlueprintName = "em_proxy-staticlib"
|
||||
ReferencedContainer = "container:Dependencies/em_proxy.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2620"
|
||||
LastUpgradeVersion = "2630"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
@@ -15,10 +15,10 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA60C44C93D7A30E3695DD59"
|
||||
BuildableName = "libem_proxy_static.a"
|
||||
BlueprintName = "em_proxy-staticlib"
|
||||
ReferencedContainer = "container:em_proxy.xcodeproj">
|
||||
BlueprintIdentifier = "A85A51412F4B4532002E2E11"
|
||||
BuildableName = "libem_proxy_swift.a"
|
||||
BlueprintName = "em_proxy-swift"
|
||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
@@ -50,10 +50,10 @@
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA60C44C93D7A30E3695DD59"
|
||||
BuildableName = "libem_proxy_static.a"
|
||||
BlueprintName = "em_proxy-staticlib"
|
||||
ReferencedContainer = "container:em_proxy.xcodeproj">
|
||||
BlueprintIdentifier = "A85A51412F4B4532002E2E11"
|
||||
BuildableName = "libem_proxy_swift.a"
|
||||
BlueprintName = "em_proxy-swift"
|
||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
@@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2610"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA609C732349A560B9642892"
|
||||
BuildableName = "libminimuxer_static.a"
|
||||
BlueprintName = "minimuxer-staticlib"
|
||||
ReferencedContainer = "container:Dependencies/minimuxer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA609C732349A560B9642892"
|
||||
BuildableName = "libminimuxer_static.a"
|
||||
BlueprintName = "minimuxer-staticlib"
|
||||
ReferencedContainer = "container:Dependencies/minimuxer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
3
AltStore.xcworkspace/contents.xcworkspacedata
generated
3
AltStore.xcworkspace/contents.xcworkspacedata
generated
@@ -7,6 +7,9 @@
|
||||
<FileRef
|
||||
location = "group:Dependencies/AltSign">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Dependencies/minimuxer">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Dependencies/Roxas/Roxas.xcodeproj">
|
||||
</FileRef>
|
||||
|
||||
@@ -92,7 +92,6 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
self.setTintColor()
|
||||
self.setTintColor()
|
||||
self.prepareImageCache()
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import AltStoreCore
|
||||
import Roxas
|
||||
|
||||
import Nuke
|
||||
import Minimuxer
|
||||
|
||||
class BrowseViewController: UICollectionViewController, PeekPopPreviewing
|
||||
{
|
||||
|
||||
@@ -368,7 +368,7 @@ private extension FeaturedViewController
|
||||
#keyPath(StoreApp._source._apps),
|
||||
#keyPath(StoreApp.bundleIdentifier),
|
||||
StoreApp.altstoreAppID,
|
||||
#keyPath(StoreApp.installedApp),
|
||||
#keyPath(StoreApp.installedApp)
|
||||
)
|
||||
|
||||
let primaryFetchRequest = fetchRequest.copy() as! NSFetchRequest<StoreApp>
|
||||
|
||||
@@ -87,7 +87,7 @@ final class LaunchViewController: UIViewController, UIDocumentPickerDelegate {
|
||||
}
|
||||
|
||||
func start_minimuxer_threads(_ pairing_file: String) {
|
||||
targetMinimuxerAddress()
|
||||
retargetUsbmuxdAddr()
|
||||
let documentsDirectory = FileManager.default.documentsDirectory.absoluteString
|
||||
do {
|
||||
let loggingEnabled = UserDefaults.standard.isMinimuxerConsoleLoggingEnabled
|
||||
@@ -96,7 +96,7 @@ final class LaunchViewController: UIViewController, UIDocumentPickerDelegate {
|
||||
try! FileManager.default.removeItem(at: FileManager.default.documentsDirectory.appendingPathComponent(pairingFileName))
|
||||
displayError("minimuxer failed to start, please restart SideStore. \((error as? LocalizedError)?.failureReason ?? "UNKNOWN ERROR")")
|
||||
}
|
||||
start_auto_mounter(documentsDirectory)
|
||||
startAutoMounter(documentsDirectory)
|
||||
}
|
||||
|
||||
func fetchPairingFile() -> String? { PairingFileManager.shared.fetchPairingFile(presentingVC: self) }
|
||||
|
||||
@@ -16,6 +16,7 @@ import WidgetKit
|
||||
import AltStoreCore
|
||||
import AltSign
|
||||
import Roxas
|
||||
import Minimuxer
|
||||
|
||||
extension AppManager
|
||||
{
|
||||
@@ -997,6 +998,7 @@ extension AppManager
|
||||
case .failure(let error): completionHandler(.failure(error))
|
||||
case .success(let installedApp): completionHandler(.success(installedApp))
|
||||
}
|
||||
//UIApplication.shared.open(shortcutURLon, options: [:], completionHandler: nil)
|
||||
}
|
||||
installOperation.addDependency(sendAppOperation)
|
||||
|
||||
|
||||
@@ -701,7 +701,7 @@ private extension AuthenticationOperation
|
||||
|
||||
func registerCurrentDevice(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> Void)
|
||||
{
|
||||
guard let udid = fetch_udid()?.toString() else {
|
||||
guard let udid = fetchUDID() else {
|
||||
return completionHandler(.failure(OperationError.unknownUDID))
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ final class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<Inst
|
||||
if UserDefaults.standard.enableEMPforWireguard {
|
||||
startEMProxy(bind_addr: AppConstants.Proxy.serverURL)
|
||||
}
|
||||
targetMinimuxerAddress()
|
||||
retargetUsbmuxdAddr()
|
||||
let documentsDirectory = FileManager.default.documentsDirectory.absoluteString
|
||||
do {
|
||||
// enable minimuxer console logging only if enabled in settings
|
||||
@@ -118,7 +118,7 @@ final class BackgroundRefreshAppsOperation: ResultOperation<[String: Result<Inst
|
||||
if #available(iOS 17, *) {
|
||||
// TODO: iOS 17 and above have a new JIT implementation that is completely broken in SideStore :(
|
||||
} else {
|
||||
start_auto_mounter(documentsDirectory)
|
||||
startAutoMounter(documentsDirectory)
|
||||
}
|
||||
|
||||
self.managedObjectContext.perform {
|
||||
|
||||
@@ -42,7 +42,7 @@ final class DeactivateAppOperation: ResultOperation<InstalledApp>
|
||||
|
||||
for profile in allIdentifiers {
|
||||
do {
|
||||
try remove_provisioning_profile(profile)
|
||||
try removeProvisioningProfile(profile)
|
||||
self.progress.completedUnitCount += 1
|
||||
installedApp.isActive = false
|
||||
self.finish(.success(installedApp))
|
||||
|
||||
@@ -88,7 +88,7 @@ final class EnableJITOperation<Context: EnableJITContext>: ResultOperation<Void>
|
||||
var retries = 3
|
||||
while (retries > 0){
|
||||
do {
|
||||
try debug_app(installedApp.resignedBundleIdentifier)
|
||||
try debugApp(installedApp.resignedBundleIdentifier)
|
||||
self.finish(.success(()))
|
||||
retries = 0
|
||||
} catch {
|
||||
@@ -105,7 +105,7 @@ final class EnableJITOperation<Context: EnableJITContext>: ResultOperation<Void>
|
||||
|
||||
@available(iOS 17, *)
|
||||
func enableJITSideJITServer(serverURL: URL, installedApp: InstalledApp, completion: @escaping (Result<Void, SideJITServerErrorType>) -> Void) {
|
||||
guard let udid = fetch_udid()?.toString() else {
|
||||
guard let udid = fetchUDID() else {
|
||||
completion(.failure(.other("Unable to get UDID")))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import AltStoreCore
|
||||
import AltSign
|
||||
import Roxas
|
||||
|
||||
let shortcutURLonDelay = URL(string: "shortcuts://run-shortcut?name=TurnOnDataDelay")!
|
||||
|
||||
@objc(InstallAppOperation)
|
||||
final class InstallAppOperation: ResultOperation<InstalledApp>
|
||||
{
|
||||
@@ -176,6 +178,13 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
|
||||
|
||||
var installing = true
|
||||
if installedApp.storeApp?.bundleIdentifier.range(of: Bundle.Info.appbundleIdentifier) != nil {
|
||||
do {
|
||||
// we need to flush changes to the disk now in case the changes are lost when iOS kills current process
|
||||
try installedApp.managedObjectContext?.save()
|
||||
} catch {
|
||||
print("Failed to flush installedApp to disk: \(error)")
|
||||
}
|
||||
|
||||
// Reinstalling ourself will hang until we leave the app, so we need to exit it without force closing
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
||||
if UIApplication.shared.applicationState != .active {
|
||||
@@ -204,7 +213,14 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
|
||||
let alert = UIAlertController(title: "Finish Refresh", message: "Please reopen SideStore after the process is finished.To finish refreshing, SideStore must be moved to the background. To do this, you can either go to the Home Screen manually or by hitting Continue. Please reopen SideStore after doing this.", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default, handler: { _ in
|
||||
print("Going home")
|
||||
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
|
||||
// Cell Shortcut
|
||||
|
||||
DispatchQueue.main.async{
|
||||
// UIApplication.shared.open(shortcutURLonDelay, options: [:]) { _ in
|
||||
// print("Cell OFF Shortcut finished execution.")
|
||||
// }
|
||||
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
|
||||
}
|
||||
}))
|
||||
|
||||
DispatchQueue.main.async {
|
||||
@@ -220,7 +236,14 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
|
||||
}
|
||||
}
|
||||
}
|
||||
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
|
||||
|
||||
DispatchQueue.main.async {
|
||||
// Cell Shortcut
|
||||
// UIApplication.shared.open(shortcutURLonDelay, options: [:]) { _ in
|
||||
// print("Cell OFF Shortcut finished execution.")
|
||||
// }
|
||||
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,6 @@ private extension PatchAppOperation
|
||||
#if targetEnvironment(simulator)
|
||||
throw PatchAppError.unsupportedOperatingSystemVersion(ProcessInfo.processInfo.operatingSystemVersion)
|
||||
#else
|
||||
|
||||
let spotlightPath = "Applications/Spotlight.app/Spotlight"
|
||||
let spotlightFileURL = self.patchDirectory.appendingPathComponent(spotlightPath)
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import AltStoreCore
|
||||
import AltSign
|
||||
import Roxas
|
||||
|
||||
import Minimuxer
|
||||
|
||||
@objc(RefreshAppOperation)
|
||||
final class RefreshAppOperation: ResultOperation<InstalledApp>
|
||||
{
|
||||
@@ -46,7 +48,6 @@ final class RefreshAppOperation: ResultOperation<InstalledApp>
|
||||
|
||||
for p in profiles {
|
||||
do {
|
||||
let bytes =
|
||||
try installProvisioningProfiles(p.value.data)
|
||||
} catch {
|
||||
self.finish(.failure(MinimuxerError.ProfileInstall))
|
||||
|
||||
@@ -201,7 +201,7 @@ private extension ResignAppOperation
|
||||
|
||||
if app.isAltStoreApp
|
||||
{
|
||||
guard let udid = fetch_udid()?.toString() as? String else { throw OperationError.unknownUDID }
|
||||
guard let udid = fetchUDID() else { throw OperationError.unknownUDID }
|
||||
guard Bundle.main.object(forInfoDictionaryKey: Bundle.Info.devicePairingString) is String else { throw OperationError.unknownUDID }
|
||||
additionalValues[Bundle.Info.devicePairingString] = "<insert pairing file here>"
|
||||
additionalValues[Bundle.Info.deviceID] = udid
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
import Foundation
|
||||
import Network
|
||||
import AltStoreCore
|
||||
import Minimuxer
|
||||
|
||||
|
||||
@objc(SendAppOperation)
|
||||
final class SendAppOperation: ResultOperation<()>
|
||||
@@ -25,39 +27,50 @@ final class SendAppOperation: ResultOperation<()>
|
||||
self.progress.totalUnitCount = 1
|
||||
}
|
||||
|
||||
override func main()
|
||||
{
|
||||
override func main() {
|
||||
super.main()
|
||||
|
||||
if let error = self.context.error
|
||||
{
|
||||
|
||||
if let error = self.context.error {
|
||||
return self.finish(.failure(error))
|
||||
}
|
||||
|
||||
|
||||
guard let resignedApp = self.context.resignedApp else {
|
||||
return self.finish(.failure(OperationError.invalidParameters("SendAppOperation.main: self.resignedApp is nil")))
|
||||
}
|
||||
|
||||
// self.context.resignedApp.fileURL points to the app bundle, but we want the .ipa.
|
||||
|
||||
let shortcutURLoff = URL(string: "shortcuts://run-shortcut?name=TurnOffData")!
|
||||
|
||||
let app = AnyApp(name: resignedApp.name, bundleIdentifier: self.context.bundleIdentifier, url: resignedApp.fileURL, storeApp: nil)
|
||||
let fileURL = InstalledApp.refreshedIPAURL(for: app)
|
||||
|
||||
print("AFC App `fileURL`: \(fileURL.absoluteString)")
|
||||
|
||||
if let data = NSData(contentsOf: fileURL) {
|
||||
do {
|
||||
let bytes = Data(data)
|
||||
try yeetAppAFC(app.bundleIdentifier, bytes)
|
||||
self.progress.completedUnitCount += 1
|
||||
self.finish(.success(()))
|
||||
} catch {
|
||||
self.finish(.failure(MinimuxerError.RwAfc))
|
||||
self.progress.completedUnitCount += 1
|
||||
self.finish(.success(()))
|
||||
}
|
||||
} else {
|
||||
|
||||
// Wait for Shortcut to Finish Before Proceeding
|
||||
DispatchQueue.main.async {
|
||||
// UIApplication.shared.open(shortcutURLoff, options: [:]) { _ in
|
||||
// print("Shortcut finished execution. Proceeding with file transfer.")
|
||||
|
||||
DispatchQueue.global().async {
|
||||
self.processFile(at: fileURL, for: app.bundleIdentifier)
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private func processFile(at fileURL: URL, for bundleIdentifier: String) {
|
||||
guard let data = NSData(contentsOf: fileURL) else {
|
||||
print("IPA doesn't exist????")
|
||||
self.finish(.failure(OperationError(.appNotFound(name: resignedApp.name))))
|
||||
return self.finish(.failure(OperationError(.appNotFound(name: bundleIdentifier))))
|
||||
}
|
||||
|
||||
do {
|
||||
let bytes = Data(data)
|
||||
try yeetAppAFC(bundleIdentifier, bytes)
|
||||
self.progress.completedUnitCount += 1
|
||||
self.finish(.success(()))
|
||||
} catch {
|
||||
self.finish(.failure(MinimuxerError.RwAfc))
|
||||
self.progress.completedUnitCount += 1
|
||||
self.finish(.success(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<stackView key="tableFooterView" opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalCentering" alignment="center" spacing="15" id="48g-cT-stR">
|
||||
<rect key="frame" x="0.0" y="2442.6666641235352" width="402" height="125"/>
|
||||
<rect key="frame" x="0.0" y="2469.3333282470703" width="402" height="125"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="900" text="Follow SideStore for updates" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XFa-MY-7cV">
|
||||
@@ -494,7 +494,7 @@
|
||||
<tableViewSection id="1fc-f1-ALD">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="7Ek-Ls-QVO" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="793.66666412353516" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="794.66666412353516" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="7Ek-Ls-QVO" id="KjD-M3-oNg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -531,13 +531,13 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="hFh-X1-ZAi" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="844.66666412353516" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="845.66666412353516" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hFh-X1-ZAi" id="nCs-Ro-A6t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Clear Cache…" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="j4e-Mz-DlL">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Clear Cache…" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="j4e-Mz-DlL">
|
||||
<rect key="frame" x="29.999999999999993" y="15.333333333333334" width="114.33333333333331" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -563,28 +563,28 @@
|
||||
<tableViewSection headerTitle="" id="J90-vn-u2O">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="i4T-2q-jF3" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="931.33333015441895" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="932.33333015441895" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="i4T-2q-jF3" id="VTQ-H4-aCM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Developers" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hRA-OK-Vjw">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Developers" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hRA-OK-Vjw">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="86" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="lx9-35-OSk">
|
||||
<rect key="frame" x="217" y="15.333333333333336" width="155" height="20.333333333333329"/>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="lx9-35-OSk">
|
||||
<rect key="frame" x="217" y="15.333333333333334" width="155" height="20.333333333333329"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="SideStore Team" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JAA-iZ-VGb">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore Team" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JAA-iZ-VGb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="125.33333333333333" height="20.333333333333332"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Mmj-3V-fTb">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Mmj-3V-fTb">
|
||||
<rect key="frame" x="139.33333333333331" y="-1" width="15.666666666666657" height="22.333333333333332"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
@@ -608,29 +608,29 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="oHX-oR-nwJ" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="982.33333015441895" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="983.33333015441895" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="oHX-oR-nwJ" id="hN4-i5-igu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="UI Designer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oqY-wY-1Vf">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="UI Designer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="oqY-wY-1Vf">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="89" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="gUq-6Q-t5X">
|
||||
<rect key="frame" x="227.33333333333337" y="15.333333333333336" width="144.66666666666663" height="20.333333333333329"/>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="gUq-6Q-t5X">
|
||||
<rect key="frame" x="227.33333333333337" y="15.333333333333334" width="144.66666666666663" height="20.333333333333329"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Fabian (thdev)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ylE-VL-7Fq">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Fabian (thdev)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ylE-VL-7Fq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="115" height="20.333333333333332"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="e3L-vR-Jae">
|
||||
<rect key="frame" x="129" y="-1" width="15.666666666666657" height="22.333333333333332"/>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="e3L-vR-Jae">
|
||||
<rect key="frame" x="128.99999999999997" y="-1" width="15.666666666666657" height="22.333333333333332"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
@@ -653,29 +653,29 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="0MT-ht-Sit" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1033.3333301544189" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1034.3333301544189" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0MT-ht-Sit" id="OZp-WM-5H7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Asset Designer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fGU-Fp-XgM">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Asset Designer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fGU-Fp-XgM">
|
||||
<rect key="frame" x="29.999999999999993" y="15.333333333333334" width="115.33333333333331" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="R8B-DW-7mY">
|
||||
<rect key="frame" x="235.33333333333337" y="15.333333333333336" width="136.66666666666663" height="20.333333333333329"/>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="R8B-DW-7mY">
|
||||
<rect key="frame" x="235.33333333333337" y="15.333333333333334" width="136.66666666666663" height="20.333333333333329"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Chris (LitRitt)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hId-3P-41T">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Chris (LitRitt)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hId-3P-41T">
|
||||
<rect key="frame" x="0.0" y="0.0" width="107" height="20.333333333333332"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="baq-cE-fMY">
|
||||
<rect key="frame" x="120.99999999999997" y="-1" width="15.666666666666657" height="22.333333333333332"/>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="baq-cE-fMY">
|
||||
<rect key="frame" x="120.99999999999999" y="-1" width="15.666666666666671" height="22.333333333333332"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
@@ -698,19 +698,19 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="O5R-Al-lGj" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1084.3333301544189" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1085.3333301544189" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="O5R-Al-lGj" id="CrG-Mr-xQq">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Licenses" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D6b-cd-pVK">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Licenses" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D6b-cd-pVK">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="67.333333333333329" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="s79-GQ-khr">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="s79-GQ-khr">
|
||||
<rect key="frame" x="356.33333333333331" y="14.333333333333334" width="15.666666666666686" height="22.333333333333329"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
@@ -739,19 +739,19 @@
|
||||
<tableViewSection headerTitle="" id="swj-Wc-IR6">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="xGq-wV-SCd" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1170.9999961853027" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1171.9999961853027" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xGq-wV-SCd" id="G7G-sK-oO3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Enable Beta Updates" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1B5-BJ-Rkb">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable Beta Updates" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1B5-BJ-Rkb">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="169" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Blb-Dp-9QF">
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Blb-Dp-9QF">
|
||||
<rect key="frame" x="311" y="11.666666666666664" width="63" height="28"/>
|
||||
<connections>
|
||||
<action selector="toggleEnableBetaUpdates:" destination="aMk-Xp-UL8" eventType="valueChanged" id="9Ea-BQ-DAE"/>
|
||||
@@ -774,19 +774,19 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="eHa-Cd-p4h" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1221.9999961853027" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1222.9999961853027" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="eHa-Cd-p4h" id="V9s-7b-vkR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Beta Updates Track" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nSh-L8-ca0" userLabel="Beta Track Label">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Beta Updates Track" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nSh-L8-ca0" userLabel="Beta Track Label">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="159.66666666666666" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" ambiguous="YES" showsMenuAsPrimaryAction="YES" contentHorizontalAlignment="right" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" changesSelectionAsPrimaryAction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kYQ-zz-vjQ" userLabel="Beta Track Drop Down Button">
|
||||
<button opaque="NO" contentMode="scaleToFill" showsMenuAsPrimaryAction="YES" contentHorizontalAlignment="right" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" changesSelectionAsPrimaryAction="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kYQ-zz-vjQ" userLabel="Beta Track Drop Down Button">
|
||||
<rect key="frame" x="301.66666666666669" y="8.3333333333333321" width="70.333333333333314" height="34.333333333333343"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="50" id="n1r-LA-2uh"/>
|
||||
@@ -815,19 +815,19 @@
|
||||
<tableViewSection headerTitle="" id="OMa-EK-hRI">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="FMZ-as-Ljo" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1308.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1309.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FMZ-as-Ljo" id="JzL-Of-A3T">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Send Feedback" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pMI-Aj-nQF">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Send Feedback" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pMI-Aj-nQF">
|
||||
<rect key="frame" x="29.999999999999993" y="15.333333333333334" width="125.33333333333331" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jyy-x0-Owj">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Jyy-x0-Owj">
|
||||
<rect key="frame" x="356.33333333333331" y="14.333333333333334" width="15.666666666666686" height="22.333333333333329"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
@@ -849,19 +849,19 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="Qca-pU-sJh" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1359.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1360.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qca-pU-sJh" id="QtU-8J-VQN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="View Refresh Attempts" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sni-07-q0M">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="View Refresh Attempts" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sni-07-q0M">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="187.66666666666666" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4d3-me-Hqc">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="4d3-me-Hqc">
|
||||
<rect key="frame" x="356.33333333333331" y="14.333333333333334" width="15.666666666666686" height="22.333333333333329"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
@@ -886,19 +886,19 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VrV-qI-zXF" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1410.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1411.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VrV-qI-zXF" id="w1r-uY-4pD">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="SideJITServer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="46q-DB-5nc">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideJITServer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="46q-DB-5nc">
|
||||
<rect key="frame" x="29.999999999999993" y="15.333333333333334" width="115.33333333333331" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wvD-eZ-nQI">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="wvD-eZ-nQI">
|
||||
<rect key="frame" x="356.33333333333331" y="14.333333333333334" width="15.666666666666686" height="22.333333333333329"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
@@ -920,19 +920,19 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VNn-u4-cN8" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1461.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1462.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VNn-u4-cN8" id="4bh-qe-l2N">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Reset Pairing File" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ysS-9s-dXm">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Reset Pairing File" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ysS-9s-dXm">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="140" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r09-mH-pOD">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="r09-mH-pOD">
|
||||
<rect key="frame" x="356.33333333333331" y="14.333333333333334" width="15.666666666666686" height="22.333333333333329"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
@@ -954,19 +954,19 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="e7s-hL-kv9" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1512.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1513.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="e7s-hL-kv9" id="yjL-Mu-HTk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Anisette Servers" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eds-Dj-36y">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Anisette Servers" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eds-Dj-36y">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="135.66666666666666" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0dh-yd-7i9">
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="0dh-yd-7i9">
|
||||
<rect key="frame" x="356.33333333333331" y="14.333333333333334" width="15.666666666666686" height="22.333333333333329"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
@@ -987,20 +987,54 @@
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="LEH-wv-o2q" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1564.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="LEH-wv-o2q" id="AP9-UO-vZk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="VPN Configuration" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iie-Nj-lki" userLabel="VPN Configuration">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="151.66666666666666" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="639-RC-1ii">
|
||||
<rect key="frame" x="356.33333333333331" y="14.333333333333334" width="15.666666666666686" height="22.333333333333329"/>
|
||||
<imageReference key="image" image="chevron.right" catalog="system" symbolScale="large"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="iie-Nj-lki" firstAttribute="centerY" secondItem="AP9-UO-vZk" secondAttribute="centerY" id="Mxs-Yx-TRW"/>
|
||||
<constraint firstItem="iie-Nj-lki" firstAttribute="leading" secondItem="AP9-UO-vZk" secondAttribute="leadingMargin" id="f7R-Ql-UHL"/>
|
||||
<constraint firstItem="639-RC-1ii" firstAttribute="centerY" secondItem="AP9-UO-vZk" secondAttribute="centerY" id="tew-j2-B3V"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="639-RC-1ii" secondAttribute="trailing" id="ygE-k7-OKf"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" white="1" alpha="0.14999999999999999" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<edgeInsets key="layoutMargins" top="8" left="30" bottom="8" right="30"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="number" keyPath="style">
|
||||
<integer key="value" value="2"/>
|
||||
</userDefinedRuntimeAttribute>
|
||||
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/>
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="7rt-MT-kFH" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1563.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1615.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="7rt-MT-kFH" id="mZL-UA-6V0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Enable EMP for wireguard" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZH7-ZA-Epf" userLabel="Enable EMP for wireguard">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable EMP for wireguard" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZH7-ZA-Epf" userLabel="Enable EMP for wireguard">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="209" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8qE-hE-Ujn">
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8qE-hE-Ujn">
|
||||
<rect key="frame" x="311" y="11.666666666666664" width="63" height="28"/>
|
||||
<connections>
|
||||
<action selector="toggleEnableEMPforWireguard:" destination="aMk-Xp-UL8" eventType="valueChanged" id="B0Q-Jb-fox"/>
|
||||
@@ -1023,19 +1057,19 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="q6e-PG-mTq" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1614.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1666.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="q6e-PG-mTq" id="PRJ-Ed-P86">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Enable AppId Customization" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fq4-2u-Lgd" userLabel="Enable AppId Customization">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Enable AppId Customization" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fq4-2u-Lgd" userLabel="Enable AppId Customization">
|
||||
<rect key="frame" x="30" y="15.333333333333334" width="230" height="20.333333333333329"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="fXx-wl-F5H">
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="fXx-wl-F5H">
|
||||
<rect key="frame" x="311" y="11.666666666666664" width="63" height="28"/>
|
||||
<connections>
|
||||
<action selector="toggleEnableAppIdCustomization:" destination="aMk-Xp-UL8" eventType="valueChanged" id="gtP-5M-9Ms"/>
|
||||
@@ -1062,7 +1096,7 @@
|
||||
<tableViewSection id="ZhW-yK-wdJ">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="qjD-UK-myl" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1701.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1752.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="qjD-UK-myl" id="bcu-KT-Xee">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1090,7 +1124,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="dNh-fp-vBs" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1752.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1803.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="dNh-fp-vBs" id="Meb-tV-6br">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1118,7 +1152,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="Y6h-Bo-yec" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1803.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1854.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Y6h-Bo-yec" id="4Jf-I6-v7z">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1146,7 +1180,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="dLk-d6-X4T" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1854.6666622161865" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1905.6666622161865" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="dLk-d6-X4T" id="Okl-3m-rde">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1178,7 +1212,7 @@
|
||||
<tableViewSection headerTitle="" id="lLQ-K0-XSb">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="daQ-mk-yqC" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1941.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="1992.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="daQ-mk-yqC" id="ZkW-ZR-twy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1213,7 +1247,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="hRP-jU-2hd" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="1992.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2043.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hRP-jU-2hd" id="JhE-O4-pRg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1248,7 +1282,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="JoN-Aj-XtZ" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="2043.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2094.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JoN-Aj-XtZ" id="v8Q-VQ-Q1h">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1283,7 +1317,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="QOO-bO-4M5" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="2094.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2145.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="QOO-bO-4M5" id="VTT-z5-C89">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1311,7 +1345,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="ToB-H7-2lR" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="2145.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2196.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ToB-H7-2lR" id="Acf-xV-Isn">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1339,7 +1373,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="xtI-eU-LFb" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="2196.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2247.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xtI-eU-LFb" id="bc9-41-6mE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1373,7 +1407,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="pvu-IV-Poa" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="2247.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2298.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pvu-IV-Poa" id="zck-an-8cK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1408,7 +1442,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="9By-QW-Jw9" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="2298.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2349.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="9By-QW-Jw9" id="Dzq-gE-zyT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
@@ -1443,7 +1477,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="LzP-Qb-bmC" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="2349.3333282470703" width="402" height="51"/>
|
||||
<rect key="frame" x="0.0" y="2400.3333282470703" width="402" height="51"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="LzP-Qb-bmC" id="3rE-h0-8kb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="402" height="51"/>
|
||||
|
||||
@@ -77,6 +77,7 @@ extension SettingsViewController
|
||||
case refreshSideJITServer
|
||||
case resetPairingFile
|
||||
case anisetteServers
|
||||
case vpnConfiguration
|
||||
case enableEMPForWiregaurd
|
||||
case customizeAppId
|
||||
}
|
||||
@@ -1364,9 +1365,19 @@ extension SettingsViewController
|
||||
handleRefreshResult(result)
|
||||
})
|
||||
|
||||
let anisetteServersController = UIHostingController(rootView: anisetteServersView)
|
||||
let vc = UIHostingController(rootView: anisetteServersView)
|
||||
self.prepare(for: UIStoryboardSegue(identifier: "anisetteServers", source: self, destination: vc), sender: nil)
|
||||
|
||||
self.prepare(for: UIStoryboardSegue(identifier: "anisetteServers", source: self, destination: anisetteServersController), sender: nil)
|
||||
case .vpnConfiguration:
|
||||
let vpnConfigurationView = VPNConfigurationView()
|
||||
let vc = UIHostingController(rootView: vpnConfigurationView)
|
||||
|
||||
let appearance = UINavigationBarAppearance()
|
||||
appearance.configureWithDefaultBackground() // gives solid background
|
||||
vc.navigationItem.scrollEdgeAppearance = appearance
|
||||
vc.navigationItem.standardAppearance = appearance
|
||||
|
||||
navigationController?.pushViewController(vc, animated: true)
|
||||
case .refreshAttempts, .enableEMPForWiregaurd, .customizeAppId: break
|
||||
}
|
||||
case .signing:
|
||||
|
||||
119
AltStore/Settings/VPNConfigurationView.swift
Normal file
119
AltStore/Settings/VPNConfigurationView.swift
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// VPNConfiguration.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Magesh K on 02/03/26.
|
||||
// Copyright © 2026 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
private typealias SButton = SwiftUI.Button
|
||||
|
||||
struct VPNConfigurationView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@StateObject private var config = TunnelConfig.shared
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(header: Text("Discovered from network")) {
|
||||
Group {
|
||||
networkConfigRow(label: "Tunnel IP", text: $config.deviceIP, editable: false)
|
||||
networkConfigRow(label: "Device IP", text: $config.fakeIP, editable: false)
|
||||
networkConfigRow(label: "Subnet Mask", text: $config.subnetMask, editable: false)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
networkConfigRow(
|
||||
label: "Device IP",
|
||||
text: Binding<String?>(get: { config.overrideFakeIP }, set: { config.overrideFakeIP = $0 ?? "" }),
|
||||
editable: true
|
||||
)
|
||||
networkConfigRow(
|
||||
label: "Active",
|
||||
text: Binding<String?>(get: { config.overrideActive }, set: { _ in }),
|
||||
editable: false
|
||||
)
|
||||
} header: {
|
||||
Text("User Configuration")
|
||||
} footer: {
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
Text("Note: ")
|
||||
Text("'Device IP' is mandatory and should match exactly as in the VPN's configuration")
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("VPN Configuration")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
SButton("Confirm") {
|
||||
commitChanges()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func commitChanges() {
|
||||
bindTunnelConfig()
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
private func networkConfigRow(
|
||||
label: LocalizedStringKey,
|
||||
text: Binding<String?>,
|
||||
editable: Bool
|
||||
) -> some View {
|
||||
|
||||
let proxy = Binding<String>(
|
||||
get: { text.wrappedValue ?? "N/A" },
|
||||
set: { text.wrappedValue = $0.isEmpty || $0 == "N/A" ? nil : $0 }
|
||||
)
|
||||
|
||||
return HStack {
|
||||
Text(label)
|
||||
.foregroundColor(editable ? .primary : .gray)
|
||||
Spacer()
|
||||
TextField(label, text: proxy)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.foregroundColor(editable ? .secondary : .gray)
|
||||
.disabled(!editable)
|
||||
.keyboardType(.numbersAndPunctuation)
|
||||
.onChange(of: proxy.wrappedValue) { newValue in
|
||||
guard editable else { return }
|
||||
proxy.wrappedValue =
|
||||
newValue.filter { "0123456789.".contains($0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class TunnelConfig: ObservableObject {
|
||||
|
||||
static let shared = TunnelConfig()
|
||||
|
||||
private static let defaultOverrideIP: String = {
|
||||
if #available(iOS 26.4, *) { return "192.168.1.50" }
|
||||
return "10.7.0.1"
|
||||
}()
|
||||
|
||||
@Published var deviceIP: String?
|
||||
@Published var subnetMask: String?
|
||||
@Published var fakeIP: String?
|
||||
@Published var overrideFakeIP: String = overrideIPStorage {
|
||||
didSet { Self.overrideIPStorage = overrideFakeIP }
|
||||
}
|
||||
@Published var overrideEffective: Bool = false
|
||||
|
||||
private static var overrideIPStorage: String {
|
||||
get { UserDefaults.standard.string(forKey: "TunnelOverrideFakeIP") ?? defaultOverrideIP }
|
||||
set { UserDefaults.standard.set(newValue, forKey: "TunnelOverrideFakeIP") }
|
||||
}
|
||||
|
||||
var overrideActive: String { overrideEffective ? "Yes" : "No" }
|
||||
}
|
||||
@@ -399,6 +399,40 @@ private extension DatabaseManager
|
||||
// For backwards compatibility reasons, we cannot use localApp's buildVersion as storeBuildVersion,
|
||||
// or else the latest update will _always_ be considered new because we don't use buildVersions in our source (yet).
|
||||
installedApp = InstalledApp(resignedApp: localApp, originalBundleIdentifier: StoreApp.altstoreAppID, certificateSerialNumber: serialNumber, storeBuildVersion: nil, context: context)
|
||||
|
||||
// figure out if the current AltStoreApp is signed with "Use Main Profie" option
|
||||
// by checking if the first extension's entitlement's application-identifier matches current one
|
||||
repeat {
|
||||
guard let pluginURL = Bundle.main.builtInPlugInsURL else {
|
||||
installedApp.useMainProfile = true
|
||||
break
|
||||
}
|
||||
guard let pluginFolders = try? FileManager.default.contentsOfDirectory(at: pluginURL, includingPropertiesForKeys: nil) else {
|
||||
installedApp.useMainProfile = true
|
||||
break
|
||||
}
|
||||
|
||||
guard let pluginFolder = pluginFolders.first, let altPluginApp = ALTApplication(fileURL: pluginFolder) else {
|
||||
installedApp.useMainProfile = true
|
||||
break
|
||||
}
|
||||
|
||||
let entitlements = altPluginApp.entitlements
|
||||
guard let appId = entitlements[ALTEntitlement.applicationIdentifier] as? String else {
|
||||
installedApp.useMainProfile = false
|
||||
print("no ALTEntitlementApplicationIdentifier???")
|
||||
break
|
||||
}
|
||||
|
||||
if appId.hasSuffix(Bundle.main.bundleIdentifier!) {
|
||||
installedApp.useMainProfile = true
|
||||
} else {
|
||||
installedApp.useMainProfile = false
|
||||
}
|
||||
|
||||
|
||||
} while(false)
|
||||
|
||||
installedApp.storeApp = storeApp
|
||||
}
|
||||
|
||||
|
||||
2
Dependencies/AltSign
vendored
2
Dependencies/AltSign
vendored
Submodule Dependencies/AltSign updated: 4819a7984f...7efe511440
2
Dependencies/em_proxy
vendored
2
Dependencies/em_proxy
vendored
Submodule Dependencies/em_proxy updated: c151b90652...fcaa590843
254
Dependencies/em_proxy.xcodeproj/project.pbxproj
vendored
254
Dependencies/em_proxy.xcodeproj/project.pbxproj
vendored
@@ -1,254 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 53;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A8A5B08D2F4C387300572B4A /* em_proxy.h in Sources */ = {isa = PBXBuildFile; fileRef = 9999259129A45319005CF020 /* em_proxy.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXBuildRule section */
|
||||
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */ = {
|
||||
isa = PBXBuildRule;
|
||||
compilerSpec = com.apple.compilers.proxy.script;
|
||||
filePatterns = "*/em_proxy.h";
|
||||
fileType = pattern.proxy;
|
||||
inputFiles = (
|
||||
);
|
||||
isEditable = 0;
|
||||
name = "Cargo project build";
|
||||
outputFiles = (
|
||||
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_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";
|
||||
};
|
||||
/* End PBXBuildRule section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
9999259129A45319005CF020 /* em_proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = em_proxy.h; path = em_proxy/em_proxy.h; sourceTree = "<group>"; };
|
||||
CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libem_proxy_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
CA6094FFF69222869D176AE5 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CA6094FFF692D65BC3C892A8 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9999259129A45319005CF020 /* em_proxy.h */,
|
||||
CA6094FFF69222869D176AE5 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
CA60C44C93D7A30E3695DD59 /* em_proxy-staticlib */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = CA603DD75FB4A30E3695DD59 /* Build configuration list for PBXNativeTarget "em_proxy-staticlib" */;
|
||||
buildPhases = (
|
||||
9987603529A4610700818586 /* Fetch prebuilt binaries */,
|
||||
CA60445C3036A30E3695DD59 /* Sources */,
|
||||
);
|
||||
buildRules = (
|
||||
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "em_proxy-staticlib";
|
||||
productName = libem_proxy_static.a;
|
||||
productReference = CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
CA6094FFF692E04653AD465F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1300;
|
||||
TargetAttributes = {
|
||||
CA60C44C93D7A30E3695DD59 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = CA6094FFF69280E02D6C7F57 /* Build configuration list for PBXProject "em_proxy" */;
|
||||
compatibilityVersion = "Xcode 11.4";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = CA6094FFF692D65BC3C892A8;
|
||||
productRefGroup = CA6094FFF69222869D176AE5 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
CA60C44C93D7A30E3695DD59 /* em_proxy-staticlib */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
9987603529A4610700818586 /* Fetch prebuilt binaries */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Fetch prebuilt binaries";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
./em_proxy/em_proxy.h,
|
||||
./em_proxy/em_proxy.swift,
|
||||
"./em_proxy/libem_proxy-ios.a",
|
||||
"./em_proxy/libem_proxy-sim.a",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "#!bash\npwd\nchmod +x ./fetch-prebuilt.sh \n./fetch-prebuilt.sh em_proxy\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
CA60445C3036A30E3695DD59 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A8A5B08D2F4C387300572B4A /* em_proxy.h in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
CA604DFE779BA30E3695DD59 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libem_proxy.d;
|
||||
CARGO_XCODE_CARGO_FILE_NAME = libem_proxy.a;
|
||||
INSTALL_GROUP = "";
|
||||
INSTALL_MODE_FLAG = "";
|
||||
INSTALL_OWNER = "";
|
||||
LIB_FILE_NAME = "";
|
||||
"LIB_FILE_NAME[sdk=iphoneos*]" = "libem_proxy-ios";
|
||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libem_proxy-sim";
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
||||
PRODUCT_NAME = em_proxy_static;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
TVOS_DEPLOYMENT_TARGET = 11.5;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
CA609A517351228BE02872F8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
||||
CARGO_XCODE_BUILD_MODE = debug;
|
||||
CARGO_XCODE_FEATURES = "";
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
||||
CURRENT_PROJECT_VERSION = 0.1;
|
||||
MARKETING_VERSION = 0.1.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = em_proxy;
|
||||
SDKROOT = macosx;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
CA609A5173513CC16B37690B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
||||
CARGO_XCODE_BUILD_MODE = release;
|
||||
CARGO_XCODE_FEATURES = "";
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
||||
CURRENT_PROJECT_VERSION = 0.1;
|
||||
MARKETING_VERSION = 0.1.0;
|
||||
PRODUCT_NAME = em_proxy;
|
||||
SDKROOT = macosx;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
CA60DE07A83FA30E3695DD59 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libem_proxy.d;
|
||||
CARGO_XCODE_CARGO_FILE_NAME = libem_proxy.a;
|
||||
INSTALL_GROUP = "";
|
||||
INSTALL_MODE_FLAG = "";
|
||||
INSTALL_OWNER = "";
|
||||
LIB_FILE_NAME = "";
|
||||
"LIB_FILE_NAME[sdk=iphoneos*]" = "libem_proxy-ios";
|
||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libem_proxy-sim";
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
||||
PRODUCT_NAME = em_proxy_static;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
TVOS_DEPLOYMENT_TARGET = 11.5;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
CA603DD75FB4A30E3695DD59 /* Build configuration list for PBXNativeTarget "em_proxy-staticlib" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CA604DFE779BA30E3695DD59 /* Release */,
|
||||
CA60DE07A83FA30E3695DD59 /* Debug */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
CA6094FFF69280E02D6C7F57 /* Build configuration list for PBXProject "em_proxy" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CA609A5173513CC16B37690B /* Release */,
|
||||
CA609A517351228BE02872F8 /* Debug */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = CA6094FFF692E04653AD465F /* Project object */;
|
||||
}
|
||||
118
Dependencies/fetch-prebuilt.sh
vendored
118
Dependencies/fetch-prebuilt.sh
vendored
@@ -1,118 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Ensure we are in Dependencies directory
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Detect if Homebrew is in /opt/homebrew (Apple Silicon) or /usr/local (Intel)
|
||||
if [[ -d "/opt/homebrew" ]]; then
|
||||
export PATH="/opt/homebrew/bin:$PATH"
|
||||
elif [[ -d "/usr/local" ]]; then
|
||||
export PATH="/usr/local/bin:$PATH"
|
||||
fi
|
||||
|
||||
# Check if wget and curl are installed; if not, install them via Homebrew
|
||||
if ! command -v wget &> /dev/null; then
|
||||
echo "wget not found, attempting to install via Homebrew..."
|
||||
if command -v brew &> /dev/null; then
|
||||
brew install wget
|
||||
else
|
||||
echo "Homebrew is not installed. Please install Homebrew and rerun the script."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v curl &> /dev/null; then
|
||||
echo "curl not found, attempting to install via Homebrew..."
|
||||
if command -v brew &> /dev/null; then
|
||||
brew install curl
|
||||
else
|
||||
echo "Homebrew is not installed. Please install Homebrew and rerun the script."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
check_for_update() {
|
||||
if [ -f ".skip-prebuilt-fetch-$1" ]; then
|
||||
echo "Skipping prebuilt fetch for $1 since .skip-prebuilt-fetch-$1 exists. If you are developing $1 alongside SideStore, don't remove this file, or this script will replace your locally built binaries with the ones built by GitHub Actions."
|
||||
return
|
||||
fi
|
||||
|
||||
if [ ! -f ".last-prebuilt-fetch-$1" ]; then
|
||||
echo "0,none" > ".last-prebuilt-fetch-$1"
|
||||
fi
|
||||
|
||||
LAST_FETCH=`cat .last-prebuilt-fetch-$1 | perl -n -e '/([0-9]*),([^ ]*)$/ && print $1'`
|
||||
LAST_COMMIT=`cat .last-prebuilt-fetch-$1 | perl -n -e '/([0-9]*),([^ ]*)$/ && print $2'`
|
||||
|
||||
# Check if required library files exist
|
||||
FORCE_DOWNLOAD=false
|
||||
if [ ! -f "$1/lib$1-sim.a" ] || [ ! -f "$1/lib$1-ios.a" ]; then
|
||||
echo "Required libraries missing for $1, forcing download..."
|
||||
FORCE_DOWNLOAD=true
|
||||
fi
|
||||
|
||||
# Download if:
|
||||
# 1. Libraries are missing (FORCE_DOWNLOAD), or
|
||||
# 2. Last fetch was over 1 hour ago, or
|
||||
# 3. Force flag was passed
|
||||
if [ "$FORCE_DOWNLOAD" = true ] || [[ $LAST_FETCH -lt $(expr $(date +%s) - 3600) ]] || [[ "$2" == "force" ]]; then
|
||||
echo "Checking $1 for update"
|
||||
echo
|
||||
LATEST_COMMIT=`curl https://api.github.com/repos/SideStore/$1/releases/latest | perl -n -e '/Commit: https:\\/\\/github\\.com\\/[^\\/]*\\/[^\\/]*\\/commit\\/([^"]*)/ && print $1'`
|
||||
echo
|
||||
echo "Last commit: $LAST_COMMIT"
|
||||
echo "Latest commit: $LATEST_COMMIT"
|
||||
|
||||
NOT_UPTODATE=false
|
||||
if [[ "$LAST_COMMIT" != "$LATEST_COMMIT" ]]; then
|
||||
echo "Found update on the remote: https://api.github.com/repos/SideStore/$1/releases/latest"
|
||||
NOT_UPTODATE=true
|
||||
fi
|
||||
|
||||
# Download if:
|
||||
# 1. Libraries are missing (FORCE_DOWNLOAD), or
|
||||
# 2. New commit is available
|
||||
if [ "$FORCE_DOWNLOAD" = true ] || [ "$NOT_UPTODATE" = true ] ;then
|
||||
echo "downloading binaries"
|
||||
echo
|
||||
if [[ "$1" != "minimuxer" ]]; then
|
||||
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.swift" "https://github.com/SideStore/$1/releases/latest/download/$1.swift"
|
||||
echo
|
||||
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/generated.zip" "https://github.com/SideStore/$1/releases/latest/download/generated.zip"
|
||||
echo
|
||||
echo "Unzipping generated.zip"
|
||||
cd "$1"
|
||||
unzip ./generated.zip
|
||||
cp -v generated/* .
|
||||
# Remove all files except ones that comes checked-in from minimuxer repository
|
||||
find generated -type f ! -name 'minimuxer-Bridging-Header.h' ! -name 'minimuxer-helpers.swift' -exec rm -v {} \;
|
||||
rm generated.zip
|
||||
rmdir generated/
|
||||
cd ..
|
||||
echo "Done"
|
||||
fi
|
||||
else
|
||||
echo "Up-to-date"
|
||||
fi
|
||||
echo "$(date +%s),$LATEST_COMMIT" > ".last-prebuilt-fetch-$1"
|
||||
else
|
||||
echo "It hasn't been 1 hour and force was not specified, skipping update check for $1"
|
||||
fi
|
||||
}
|
||||
|
||||
# Allow for Xcode to check minimuxer and em_proxy separately by skipping the update check if the other one is specified as an argument
|
||||
if [[ "$1" != "em_proxy" ]]; then
|
||||
check_for_update minimuxer "$1"
|
||||
if [[ "$1" != "minimuxer" ]]; then
|
||||
echo
|
||||
fi
|
||||
fi
|
||||
if [[ "$1" != "minimuxer" ]]; then
|
||||
check_for_update em_proxy "$1"
|
||||
fi
|
||||
2
Dependencies/minimuxer
vendored
2
Dependencies/minimuxer
vendored
Submodule Dependencies/minimuxer updated: 9035aa25ae...d00e5feb20
264
Dependencies/minimuxer.xcodeproj/project.pbxproj
vendored
264
Dependencies/minimuxer.xcodeproj/project.pbxproj
vendored
@@ -1,264 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 53;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A8A5B07F2F4C355800572B4A /* minimuxer.h in Sources */ = {isa = PBXBuildFile; fileRef = A8A5B07D2F4C34FF00572B4A /* minimuxer.h */; };
|
||||
A8A5B0802F4C355800572B4A /* SwiftBridgeCore.h in Sources */ = {isa = PBXBuildFile; fileRef = A8A5B07E2F4C34FF00572B4A /* SwiftBridgeCore.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXBuildRule section */
|
||||
CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */ = {
|
||||
isa = PBXBuildRule;
|
||||
compilerSpec = com.apple.compilers.proxy.script;
|
||||
filePatterns = "*/minimuxer.h";
|
||||
fileType = pattern.proxy;
|
||||
inputFiles = (
|
||||
);
|
||||
isEditable = 0;
|
||||
name = "Cargo project build";
|
||||
outputFiles = (
|
||||
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_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";
|
||||
};
|
||||
/* End PBXBuildRule section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
A8A5B07D2F4C34FF00572B4A /* 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; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
CA6012A875F922869D176AE5 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CA6012A875F9D65BC3C892A8 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A8A5B07D2F4C34FF00572B4A /* minimuxer.h */,
|
||||
A8A5B07E2F4C34FF00572B4A /* SwiftBridgeCore.h */,
|
||||
A8A5B09C2F4C454900572B4A /* minimuxer-Bridging-Header.h */,
|
||||
CA6012A875F922869D176AE5 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
CA609C732349A560B9642892 /* minimuxer-staticlib */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = CA600589A243A560B9642892 /* Build configuration list for PBXNativeTarget "minimuxer-staticlib" */;
|
||||
buildPhases = (
|
||||
9987603629A4611D00818586 /* Fetch prebuilt binaries */,
|
||||
CA600F638141A560B9642892 /* Sources */,
|
||||
);
|
||||
buildRules = (
|
||||
CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "minimuxer-staticlib";
|
||||
productName = libminimuxer_static.a;
|
||||
productReference = CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
CA6012A875F9E04653AD465F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1300;
|
||||
TargetAttributes = {
|
||||
CA609C732349A560B9642892 = {
|
||||
CreatedOnToolsVersion = 9.2;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = CA6012A875F980E02D6C7F57 /* Build configuration list for PBXProject "minimuxer" */;
|
||||
compatibilityVersion = "Xcode 11.4";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = CA6012A875F9D65BC3C892A8;
|
||||
productRefGroup = CA6012A875F922869D176AE5 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
CA609C732349A560B9642892 /* minimuxer-staticlib */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
9987603629A4611D00818586 /* Fetch prebuilt binaries */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Fetch prebuilt binaries";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
./minimuxer/minimuxer.h,
|
||||
./minimuxer/SwiftBridgeCore.h,
|
||||
./minimuxer/minimuxer.swift,
|
||||
./minimuxer/SwiftBridgeCore.swift,
|
||||
"./minimuxer/minimuxer-Bridging-Header.h",
|
||||
"./minimuxer/minimuxer-helpers.swift",
|
||||
"./minimuxer/libminimuxer-ios.a",
|
||||
"./minimuxer/libminimuxer-sim.a",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "#!bash\npwd\nchmod +x ./fetch-prebuilt.sh \n./fetch-prebuilt.sh minimuxer\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
CA600F638141A560B9642892 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A8A5B07F2F4C355800572B4A /* minimuxer.h in Sources */,
|
||||
A8A5B0802F4C355800572B4A /* SwiftBridgeCore.h in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
CA6008D36272A560B9642892 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libminimuxer.d;
|
||||
CARGO_XCODE_CARGO_FILE_NAME = libminimuxer.a;
|
||||
INSTALL_GROUP = "";
|
||||
INSTALL_MODE_FLAG = "";
|
||||
INSTALL_OWNER = "";
|
||||
LIB_FILE_NAME = "";
|
||||
"LIB_FILE_NAME[sdk=iphoneos*]" = "libminimuxer-ios";
|
||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libminimuxer-sim";
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
||||
PRODUCT_NAME = minimuxer_static;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
TVOS_DEPLOYMENT_TARGET = 11.5;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
CA602DE9FCEDA560B9642892 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libminimuxer.d;
|
||||
CARGO_XCODE_CARGO_FILE_NAME = libminimuxer.a;
|
||||
INSTALL_GROUP = "";
|
||||
INSTALL_MODE_FLAG = "";
|
||||
INSTALL_OWNER = "";
|
||||
LIB_FILE_NAME = "";
|
||||
"LIB_FILE_NAME[sdk=iphoneos*]" = "libminimuxer-ios";
|
||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libminimuxer-sim";
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.5;
|
||||
PRODUCT_NAME = minimuxer_static;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
||||
TVOS_DEPLOYMENT_TARGET = 11.5;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
CA60A20F8EA6228BE02872F8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
||||
CARGO_XCODE_BUILD_MODE = debug;
|
||||
CARGO_XCODE_FEATURES = "";
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
||||
CURRENT_PROJECT_VERSION = 0.1;
|
||||
MARKETING_VERSION = 0.1.0;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_NAME = minimuxer;
|
||||
SDKROOT = macosx;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
CA60A20F8EA63CC16B37690B /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
||||
CARGO_XCODE_BUILD_MODE = release;
|
||||
CARGO_XCODE_FEATURES = "";
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
||||
CURRENT_PROJECT_VERSION = 0.1;
|
||||
MARKETING_VERSION = 0.1.0;
|
||||
PRODUCT_NAME = minimuxer;
|
||||
SDKROOT = macosx;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
CA600589A243A560B9642892 /* Build configuration list for PBXNativeTarget "minimuxer-staticlib" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CA602DE9FCEDA560B9642892 /* Release */,
|
||||
CA6008D36272A560B9642892 /* Debug */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
CA6012A875F980E02D6C7F57 /* Build configuration list for PBXProject "minimuxer" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CA60A20F8EA63CC16B37690B /* Release */,
|
||||
CA60A20F8EA6228BE02872F8 /* Debug */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = CA6012A875F9E04653AD465F /* Project object */;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "2620"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA609C732349A560B9642892"
|
||||
BuildableName = "libminimuxer_static.a"
|
||||
BlueprintName = "minimuxer-staticlib"
|
||||
ReferencedContainer = "container:minimuxer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CA609C732349A560B9642892"
|
||||
BuildableName = "libminimuxer_static.a"
|
||||
BlueprintName = "minimuxer-staticlib"
|
||||
ReferencedContainer = "container:minimuxer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -6,79 +6,178 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
private import minimuxer
|
||||
import Minimuxer
|
||||
|
||||
func bindTunnelConfig() {
|
||||
defer { print("[SideStore] bindTunnelConfig() completed") }
|
||||
|
||||
#if targetEnvironment(simulator)
|
||||
print("[SideStore] bindTunnelConfig() is no-op on simulator")
|
||||
#else
|
||||
print("[SideStore] bindTunnelConfig() invoked")
|
||||
|
||||
Task { @MainActor in
|
||||
let config = TunnelConfig.shared
|
||||
Minimuxer.bindTunnelConfig(
|
||||
TunnelConfigBinding(
|
||||
setDeviceIP: { value in Task { @MainActor in config.deviceIP = value } },
|
||||
setFakeIP: { value in Task { @MainActor in config.fakeIP = value } },
|
||||
setSubnetMask: { value in Task { @MainActor in config.subnetMask = value } },
|
||||
getOverrideFakeIP: { config.overrideFakeIP },
|
||||
setOverrideEffective: { value in Task { @MainActor in config.overrideEffective = value } }
|
||||
)
|
||||
)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
var isMinimuxerReady: Bool {
|
||||
var result = true
|
||||
#if targetEnvironment(simulator)
|
||||
print("isMinimuxerReady property is always true on simulator")
|
||||
return true
|
||||
print("[SideStore] isMinimuxerReady = true on simulator")
|
||||
#else
|
||||
return minimuxer.ready()
|
||||
result = Minimuxer.ready()
|
||||
print("[SideStore] isMinimuxerReady = \(result)")
|
||||
#endif
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
func retargetUsbmuxdAddr() {
|
||||
defer { print("[SideStore] retargetUsbmuxdAddr() completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("[SideStore] retargetUsbmuxdAddr() is no-op on simulator")
|
||||
#else
|
||||
print("[SideStore] retargetUsbmuxdAddr() invoked")
|
||||
Minimuxer.retargetUsbmuxdAddr()
|
||||
#endif
|
||||
}
|
||||
|
||||
func minimuxerStartWithLogger(_ pairingFile: String,_ logPath: String,_ loggingEnabled: Bool) throws {
|
||||
func minimuxerStartWithLogger(_ pairingFile: String, _ logPath: String, _ loggingEnabled: Bool) throws {
|
||||
defer { print("[SideStore] minimuxerStartWithLogger(pairingFile, logPath, dest, loggingEnabled) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("minimuxerStartWithLogger(\(pairingFile), \(logPath), \(loggingEnabled) is no-op on simulator")
|
||||
print("[SideStore] minimuxerStartWithLogger(pairingFile, logPath, loggingEnabled) is no-op on simulator")
|
||||
#else
|
||||
try minimuxer.startWithLogger(pairingFile, logPath, loggingEnabled)
|
||||
#endif
|
||||
}
|
||||
|
||||
func targetMinimuxerAddress() {
|
||||
#if targetEnvironment(simulator)
|
||||
print("targetMinimuxerAddress() is no-op on simulator")
|
||||
#else
|
||||
minimuxer.target_minimuxer_address()
|
||||
// refresh config if any
|
||||
bindTunnelConfig()
|
||||
// observe network route changes (and update device endpoint from vpn(utun))
|
||||
NetworkObserver.shared.start()
|
||||
|
||||
print("[SideStore] minimuxerStartWithLogger(pairingFile, logPath, dest, loggingEnabled) invoked")
|
||||
try Minimuxer.startWithLogger(pairingFile: pairingFile,
|
||||
logPath: logPath,
|
||||
isConsoleLoggingEnabled: loggingEnabled)
|
||||
#endif
|
||||
}
|
||||
|
||||
func installProvisioningProfiles(_ profileData: Data) throws {
|
||||
defer { print("[SideStore] installProvisioningProfiles(profileData) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("installProvisioningProfiles(\(profileData)) is no-op on simulator")
|
||||
print("[SideStore] installProvisioningProfiles(profileData) is no-op on simulator")
|
||||
#else
|
||||
try minimuxer.install_provisioning_profile(profileData.toRustByteSlice().forRust())
|
||||
print("[SideStore] installProvisioningProfiles(profileData) invoked")
|
||||
try Minimuxer.installProvisioningProfile(profile: profileData)
|
||||
#endif
|
||||
}
|
||||
|
||||
func removeProvisioningProfile(_ id: String) throws {
|
||||
defer { print("[SideStore] removeProvisioningProfile(id) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("[SideStore] removeProvisioningProfile(id) is no-op on simulator")
|
||||
#else
|
||||
print("[SideStore] removeProvisioningProfile(id) invoked")
|
||||
try Minimuxer.removeProvisioningProfile(id: id)
|
||||
#endif
|
||||
}
|
||||
|
||||
func removeApp(_ bundleId: String) throws {
|
||||
defer { print("[SideStore] removeApp(bundleId) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("removeApp(\(bundleId)) is no-op on simulator")
|
||||
print("[SideStore] removeApp(bundleId) is no-op on simulator")
|
||||
#else
|
||||
try minimuxer.remove_app(bundleId)
|
||||
print("[SideStore] removeApp(bundleId) invoked")
|
||||
try Minimuxer.removeApp(bundleId: bundleId)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
func yeetAppAFC(_ bundleId: String, _ rawBytes: Data) throws {
|
||||
defer { print("[SideStore] yeetAppAFC(bundleId, rawBytes) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("yeetAppAFC(\(bundleId), \(rawBytes)) is no-op on simulator")
|
||||
print("[SideStore] yeetAppAFC(bundleId, rawBytes) is no-op on simulator")
|
||||
#else
|
||||
try minimuxer.yeet_app_afc(bundleId, rawBytes.toRustByteSlice().forRust())
|
||||
print("[SideStore] yeetAppAFC(bundleId, rawBytes) invoked")
|
||||
try Minimuxer.yeetAppAfc(bundleId: bundleId, ipaBytes: rawBytes)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
func installIPA(_ bundleId: String) throws {
|
||||
defer { print("[SideStore] installIPA(bundleId) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("installIPA(\(bundleId)) is no-op on simulator")
|
||||
print("[SideStore] installIPA(bundleId) is no-op on simulator")
|
||||
#else
|
||||
try minimuxer.install_ipa(bundleId)
|
||||
print("[SideStore] installIPA(bundleId) invoked")
|
||||
try Minimuxer.installIpa(bundleId: bundleId)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
func fetchUDID() -> String? {
|
||||
defer { print("[SideStore] fetchUDID() completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("fetchUDID() is no-op on simulator")
|
||||
print("[SideStore] fetchUDID() is no-op on simulator")
|
||||
return "XXXXX-XXXX-XXXXX-XXXX"
|
||||
#else
|
||||
return minimuxer.fetch_udid()?.toString()
|
||||
print("[SideStore] fetchUDID() invoked")
|
||||
return Minimuxer.fetchUDID()
|
||||
#endif
|
||||
}
|
||||
|
||||
func debugApp(_ appId: String) throws {
|
||||
defer { print("[SideStore] debugApp(appId) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("[SideStore] debugApp(appId) is no-op on simulator")
|
||||
#else
|
||||
print("[SideStore] debugApp(appId) invoked")
|
||||
try Minimuxer.debugApp(appId: appId)
|
||||
#endif
|
||||
}
|
||||
|
||||
func attachDebugger(_ pid: UInt32) throws {
|
||||
defer { print("[SideStore] attachDebugger(pid) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("[SideStore] attachDebugger(pid) is no-op on simulator")
|
||||
#else
|
||||
print("[SideStore] attachDebugger(pid) invoked")
|
||||
try Minimuxer.attachDebugger(pid: pid)
|
||||
#endif
|
||||
}
|
||||
|
||||
func startAutoMounter(_ docsPath: String) {
|
||||
defer { print("[SideStore] startAutoMounter(docsPath) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("[SideStore] startAutoMounter(docsPath) is no-op on simulator")
|
||||
#else
|
||||
print("[SideStore] startAutoMounter(docsPath) invoked")
|
||||
Minimuxer.startAutoMounter(docsPath: docsPath)
|
||||
#endif
|
||||
}
|
||||
|
||||
func dumpProfiles(_ docsPath: String) throws -> String {
|
||||
defer { print("[SideStore] dumpProfiles(docsPath) completed") }
|
||||
#if targetEnvironment(simulator)
|
||||
print("[SideStore] dumpProfiles(docsPath) is no-op on simulator")
|
||||
return ""
|
||||
#else
|
||||
print("[SideStore] dumpProfiles(docsPath) invoked")
|
||||
return try Minimuxer.dumpProfiles(docsPath: docsPath)
|
||||
#endif
|
||||
}
|
||||
|
||||
func setMinimuxerDebug(_ debug: Bool) {
|
||||
defer { print("[SideStore] setMinimuxerDebug(debug) completed") }
|
||||
print("[SideStore] setMinimuxerDebug(debug) invoked")
|
||||
Minimuxer.setDebug(debug)
|
||||
}
|
||||
|
||||
extension MinimuxerError: @retroactive LocalizedError {
|
||||
public var failureReason: String? {
|
||||
@@ -89,41 +188,38 @@ extension MinimuxerError: @retroactive LocalizedError {
|
||||
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")
|
||||
return createService(name: "debug")
|
||||
case .LookupApps:
|
||||
return self.getFromDevice(name: "installed apps")
|
||||
return getFromDevice(name: "installed apps")
|
||||
case .FindApp:
|
||||
return self.getFromDevice(name: "path to the app")
|
||||
return getFromDevice(name: "path to the app")
|
||||
case .BundlePath:
|
||||
return self.getFromDevice(name: "bundle path")
|
||||
return getFromDevice(name: "bundle path")
|
||||
case .MaxPacket:
|
||||
return self.setArgument(name: "max packet")
|
||||
return setArgument(name: "max packet")
|
||||
case .WorkingDirectory:
|
||||
return self.setArgument(name: "working directory")
|
||||
return setArgument(name: "working directory")
|
||||
case .Argv:
|
||||
return self.setArgument(name: "argv")
|
||||
return setArgument(name: "argv")
|
||||
case .LaunchSuccess:
|
||||
return self.getFromDevice(name: "launch success")
|
||||
return 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")
|
||||
return createService(name: "instproxy")
|
||||
case .CreateAfc:
|
||||
return self.createService(name: "AFC")
|
||||
return 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: "")
|
||||
return NSLocalizedString("AFC was unable to manage files on the device.", comment: "")
|
||||
case .InstallApp(let message):
|
||||
return NSLocalizedString("Unable to install the app: \(message.toString())", comment: "")
|
||||
return NSLocalizedString("Unable to install the app: \(message)", comment: "")
|
||||
case .UninstallApp:
|
||||
return NSLocalizedString("Unable to uninstall the app", comment: "")
|
||||
|
||||
case .CreateMisagent:
|
||||
return self.createService(name: "misagent")
|
||||
return createService(name: "misagent")
|
||||
case .ProfileInstall:
|
||||
return NSLocalizedString("Unable to manage profiles on the device", comment: "")
|
||||
case .ProfileRemove:
|
||||
@@ -162,16 +258,16 @@ extension MinimuxerError: @retroactive LocalizedError {
|
||||
return NSLocalizedString("Mount failed", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fileprivate func createService(name: String) -> String {
|
||||
return String(format: NSLocalizedString("Cannot start a %@ server on the device.", comment: ""), name)
|
||||
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)
|
||||
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)
|
||||
String(format: NSLocalizedString("Cannot set %@ on the device.", comment: ""), name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
|
||||
### nightly
|
||||
#### What's Changed
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- altsign updated to latest
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- re added openSSL from new path
|
||||
- updated altsign to use xcframework for openSSL which was causing huge download of 1.2 GB each time
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: improve more ci worflow
|
||||
- CI: full rewrite - moved logic into ci.py and kept workflow scripts mostly dummy
|
||||
|
||||
#### Full Changelog: [38715283...99712f00](https://github.com/SideStore/SideStore/compare/38715283073ea37949a462b889ce3cad403ea499...99712f0020a4f2ae57d8d781514fa735f893c23a)
|
||||
@@ -95,6 +95,7 @@ def resolve_start_commit(last_successful: str):
|
||||
except Exception:
|
||||
return first_commit()
|
||||
|
||||
|
||||
def generate_release_notes(last_successful, tag, branch):
|
||||
current = head_commit()
|
||||
|
||||
@@ -124,7 +125,12 @@ def generate_release_notes(last_successful, tag, branch):
|
||||
for m in messages:
|
||||
section += f"{fmt_msg(m)}\n"
|
||||
|
||||
prev_authors = authors(branch)
|
||||
if commit_exists(branch):
|
||||
previous_range = branch
|
||||
else:
|
||||
previous_range = last_successful
|
||||
|
||||
prev_authors = authors(previous_range)
|
||||
recent_authors = authors(f"{last_successful}..{current}")
|
||||
new_authors = recent_authors - prev_authors
|
||||
|
||||
@@ -137,13 +143,26 @@ def generate_release_notes(last_successful, tag, branch):
|
||||
url = repo_url()
|
||||
section += (
|
||||
f"\n{HEADER_MARKER} Full Changelog: "
|
||||
f"[{last_successful[:8]}...{current[:8]}]"
|
||||
f"[{ref_display(last_successful)}...{ref_display(current)}]"
|
||||
f"({url}/compare/{last_successful}...{current})\n"
|
||||
)
|
||||
|
||||
return section
|
||||
|
||||
|
||||
def ref_display(ref):
|
||||
try:
|
||||
tag = run(f'git describe --tags --exact-match "{ref}" 2>/dev/null || true').strip()
|
||||
|
||||
# allow only semantic version tags: X.Y.Z
|
||||
if re.fullmatch(r'\d+\.\d+\.\d+', tag):
|
||||
return tag
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return ref[:8]
|
||||
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# markdown update
|
||||
# ----------------------------------------------------------
|
||||
@@ -216,7 +235,7 @@ def retrieve_tag(tag, file_path: Path):
|
||||
fr"^{TAG_MARKER} {re.escape(tag)}$",
|
||||
content,
|
||||
re.MULTILINE | re.IGNORECASE,
|
||||
)
|
||||
)
|
||||
|
||||
if not match:
|
||||
return ""
|
||||
@@ -276,4 +295,4 @@ def main():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -5,7 +5,6 @@ import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import textwrap
|
||||
import sys
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
@@ -108,13 +107,13 @@ def main():
|
||||
gen_cmd = (
|
||||
f"python3 {script} "
|
||||
f"{args.last_successful_commit} {args.release_tag} "
|
||||
f"--output-dir \"{notes_dir}\""
|
||||
f'--output-dir "{notes_dir}"'
|
||||
)
|
||||
else:
|
||||
gen_cmd = (
|
||||
f"python3 {script} "
|
||||
f"{args.short_commit} {args.release_tag} "
|
||||
f"--output-dir \"{notes_dir}\""
|
||||
f'--output-dir "{notes_dir}"'
|
||||
)
|
||||
|
||||
sh(gen_cmd, cwd=repo_root)
|
||||
@@ -140,15 +139,7 @@ def main():
|
||||
formatted = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
human = now.strftime("%c")
|
||||
|
||||
localized_description = textwrap.dedent(f"""
|
||||
This is release for:
|
||||
- version: "{args.marketing_version}"
|
||||
- revision: "{args.short_commit}"
|
||||
- timestamp: "{human}"
|
||||
|
||||
Release Notes:
|
||||
{notes}
|
||||
""").strip()
|
||||
localized_description = getFormattedLocalizedDescription(args.marketing_version, args.short_commit, human, notes)
|
||||
|
||||
metadata = {
|
||||
"is_beta": bool(args.is_beta),
|
||||
@@ -170,6 +161,16 @@ def main():
|
||||
|
||||
print(f"Wrote {out_file}")
|
||||
|
||||
def getFormattedLocalizedDescription(marketing_version, short_commit, human, notes):
|
||||
return f"""
|
||||
This is release for:
|
||||
- version: "{marketing_version}"
|
||||
- revision: "{short_commit}"
|
||||
- timestamp: "{human}"
|
||||
|
||||
Release Notes:
|
||||
{notes}
|
||||
""".lstrip("\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -6,12 +6,13 @@ import datetime
|
||||
from pathlib import Path
|
||||
import time
|
||||
import json
|
||||
import inspect
|
||||
import re
|
||||
from posix import getcwd
|
||||
|
||||
# REPO ROOT relative to script dir
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SCRIPTS = ROOT / 'scripts/ci'
|
||||
BUILD_SETTINGS_OUTFILE = "project-build-settings.txt"
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# helpers
|
||||
@@ -57,106 +58,69 @@ def getenv(name, default=""):
|
||||
def short_commit():
|
||||
return runAndGet("git rev-parse --short HEAD")
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# BUILD NUMBER RESERVATION
|
||||
# ----------------------------------------------------------
|
||||
def reserve_build_number(repo, max_attempts=5):
|
||||
repo = Path(repo).resolve()
|
||||
version_json = repo / "version.json"
|
||||
def count_new_commits(last_commit):
|
||||
if not last_commit or not last_commit.strip():
|
||||
return 0
|
||||
|
||||
def utc_now():
|
||||
return datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
try:
|
||||
total = int(runAndGet("git rev-list --count HEAD"))
|
||||
if total == 1:
|
||||
head = runAndGet("git rev-parse HEAD")
|
||||
return 1 if head != last_commit else 0
|
||||
|
||||
def current_branch():
|
||||
return runAndGet("git rev-parse --abbrev-ref HEAD", cwd=repo)
|
||||
out = runAndGet(f"git rev-list --count {last_commit}..HEAD")
|
||||
return int(out)
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def sync_with_remote(branch):
|
||||
run(f"git fetch --depth=1 origin {branch}", check=False, cwd=repo)
|
||||
run(f"git reset --hard origin/{branch}", check=False, cwd=repo)
|
||||
|
||||
def read(branch):
|
||||
defaults = {
|
||||
"build": 0,
|
||||
"issued_at": utc_now(),
|
||||
"tag": branch,
|
||||
}
|
||||
|
||||
if version_json.exists():
|
||||
data = json.loads(version_json.read_text())
|
||||
else:
|
||||
data = {}
|
||||
|
||||
for k, v in defaults.items():
|
||||
data.setdefault(k, v)
|
||||
|
||||
data["tag"] = branch
|
||||
version_json.write_text(json.dumps(data, indent=2) + "\n")
|
||||
return data
|
||||
|
||||
def write(data):
|
||||
version_json.write_text(json.dumps(data, indent=2) + "\n")
|
||||
|
||||
for attempt in range(max_attempts):
|
||||
branch = current_branch()
|
||||
sync_with_remote(branch)
|
||||
|
||||
data = read(branch)
|
||||
data["build"] += 1
|
||||
data["issued_at"] = utc_now()
|
||||
|
||||
write(data)
|
||||
|
||||
run("git add version.json", check=False, cwd=repo)
|
||||
run(
|
||||
f"git commit -m '{branch} - build no: {data['build']}' || true",
|
||||
check=False,
|
||||
cwd=repo,
|
||||
)
|
||||
|
||||
rc = subprocess.call(f"git push origin {branch}", shell=True, cwd=repo)
|
||||
|
||||
if rc == 0:
|
||||
print(f"Reserved build #{data['build']}", file=sys.stderr)
|
||||
return data["build"]
|
||||
|
||||
print("Push rejected, retrying...", file=sys.stderr)
|
||||
time.sleep(2)
|
||||
|
||||
raise SystemExit("Failed reserving build number")
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# PROJECT INFO
|
||||
# ----------------------------------------------------------
|
||||
def dump_project_settings(outdir=None):
|
||||
outfile = Path(outdir).resolve() / BUILD_SETTINGS_OUTFILE if outdir else BUILD_SETTINGS_OUTFILE
|
||||
run(f"xcodebuild -showBuildSettings 2>&1 > '{outfile}'")
|
||||
|
||||
def get_product_name():
|
||||
return runAndGet(
|
||||
"xcodebuild -showBuildSettings "
|
||||
"| grep PRODUCT_NAME "
|
||||
def _extract_setting(cmd):
|
||||
out = runAndGet(cmd + " || true").strip() # prevent grep failure from aborting
|
||||
return out if out else None
|
||||
|
||||
def _read_dumped_build_setting(name):
|
||||
return _extract_setting(
|
||||
f"cat '{BUILD_SETTINGS_OUTFILE}' "
|
||||
f"| grep '{name} = ' "
|
||||
"| tail -1 "
|
||||
"| sed -e 's/.*= //g'"
|
||||
)
|
||||
|
||||
def get_bundle_id():
|
||||
return runAndGet(
|
||||
"xcodebuild -showBuildSettings 2>&1 "
|
||||
"| grep 'PRODUCT_BUNDLE_IDENTIFIER = ' "
|
||||
def query_build_setting(name):
|
||||
return _extract_setting(
|
||||
f"xcodebuild -showBuildSettings 2>&1 "
|
||||
f"| grep '{name} = ' "
|
||||
"| tail -1 "
|
||||
"| sed -e 's/.*= //g'"
|
||||
)
|
||||
|
||||
def get_product_name(): return query_build_setting("PRODUCT_NAME")
|
||||
def get_bundle_id(): return query_build_setting("PRODUCT_BUNDLE_IDENTIFIER")
|
||||
def read_product_name(): return _read_dumped_build_setting("PRODUCT_NAME")
|
||||
def read_bundle_id(): return _read_dumped_build_setting("PRODUCT_BUNDLE_IDENTIFIER")
|
||||
|
||||
def get_marketing_version():
|
||||
return runAndGet(f"grep MARKETING_VERSION {ROOT}/Build.xcconfig | sed -e 's/MARKETING_VERSION = //g'")
|
||||
|
||||
def set_marketing_version(qualified):
|
||||
def set_marketing_version(version):
|
||||
run(
|
||||
f"sed -E -i '' "
|
||||
f"'s/^MARKETING_VERSION = .*/MARKETING_VERSION = {qualified}/' "
|
||||
f"'s/^MARKETING_VERSION = .*/MARKETING_VERSION = {version}/' "
|
||||
f"{ROOT}/Build.xcconfig"
|
||||
)
|
||||
|
||||
def compute_qualified_version(marketing, build_num, channel, short):
|
||||
date = datetime.datetime.now(datetime.UTC).strftime("%Y.%m.%d")
|
||||
return f"{marketing}-{channel}.{date}.{build_num}+{short}"
|
||||
|
||||
def compute_normalized_version(marketing, build_num, short):
|
||||
now = datetime.datetime.now(datetime.UTC)
|
||||
date = now.strftime("%Y%m%d") # normalized date
|
||||
base = marketing.strip()
|
||||
return f"{base}-{date}.{build_num}+{short}"
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# CLEAN
|
||||
@@ -251,13 +215,13 @@ def tests_run(model):
|
||||
# ----------------------------------------------------------
|
||||
|
||||
def encrypt_logs(name):
|
||||
default_pwd = "12345"
|
||||
pwd = getenv("BUILD_LOG_ZIP_PASSWORD", default_pwd)
|
||||
|
||||
if pwd == default_pwd:
|
||||
print("Warning: BUILD_LOG_ZIP_PASSWORD not set, using fallback password", file=sys.stderr)
|
||||
|
||||
run(f'cd build/logs && zip -e -P "{pwd}" ../../{name}.zip *')
|
||||
pwd = getenv("BUILD_LOG_ZIP_PASSWORD")
|
||||
cwd = getcwd()
|
||||
if not pwd or not pwd.strip():
|
||||
print("BUILD_LOG_ZIP_PASSWORD not set — logs will be uploaded UNENCRYPTED", file=sys.stderr)
|
||||
run(f'cd {cwd}/build/logs && zip -r {cwd}/{name}.zip *')
|
||||
return
|
||||
run(f'cd {cwd}/build/logs && zip -e -P "{pwd}" {cwd}/{name}.zip *')
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# RELEASE NOTES
|
||||
@@ -281,22 +245,13 @@ def retrieve_release_notes(tag):
|
||||
# ----------------------------------------------------------
|
||||
# DEPLOY SOURCE.JSON
|
||||
# ----------------------------------------------------------
|
||||
|
||||
def deploy(repo, source_json, release_tag, short_commit, marketing_version, channel, bundle_id, ipa_name, last_successful_commit=None):
|
||||
repo = (ROOT / repo).resolve()
|
||||
def generate_metadata(release_tag, short_commit, marketing_version, channel, bundle_id, ipa_name, last_successful_commit=None):
|
||||
ipa_path = ROOT / ipa_name
|
||||
source_json_path = repo / source_json
|
||||
metadata = 'source-metadata.json'
|
||||
|
||||
if not repo.exists():
|
||||
raise SystemExit(f"{repo} repo missing")
|
||||
|
||||
if not ipa_path.exists():
|
||||
raise SystemExit(f"{ipa_path} missing")
|
||||
|
||||
if not source_json_path.exists():
|
||||
raise SystemExit(f"{source_json} missing inside repo")
|
||||
|
||||
cmd = (
|
||||
f"python3 {SCRIPTS}/generate_source_metadata.py "
|
||||
f"--repo-root {ROOT} "
|
||||
@@ -311,12 +266,26 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, chan
|
||||
f"--bundle-id {bundle_id}"
|
||||
)
|
||||
|
||||
# pass only if provided
|
||||
if last_successful_commit:
|
||||
cmd += f" --last-successful-commit {last_successful_commit}"
|
||||
|
||||
run(cmd)
|
||||
|
||||
def deploy(repo, source_json, release_tag, marketing_version):
|
||||
repo = (ROOT / repo).resolve()
|
||||
source_json_path = repo / source_json
|
||||
metadata = 'source-metadata.json'
|
||||
|
||||
if not repo.exists():
|
||||
raise SystemExit(f"{repo} repo missing")
|
||||
|
||||
if not (repo / ".git").exists():
|
||||
print("Repo is not a git repository, skipping deploy", file=sys.stderr)
|
||||
return
|
||||
|
||||
if not source_json_path.exists():
|
||||
raise SystemExit(f"{source_json} missing inside repo")
|
||||
|
||||
run("git config user.name 'GitHub Actions'", check=False)
|
||||
run("git config user.email 'github-actions@github.com'", check=False)
|
||||
|
||||
@@ -325,7 +294,7 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, chan
|
||||
run("git switch main || git switch -c main origin/main", cwd=repo)
|
||||
run("git reset --hard origin/main", cwd=repo)
|
||||
# ------------------------------------------------------
|
||||
|
||||
|
||||
max_attempts = 5
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
if attempt > 1:
|
||||
@@ -348,28 +317,49 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, chan
|
||||
else:
|
||||
raise SystemExit("Deploy push failed after retries")
|
||||
|
||||
def last_successful_commit(workflow, branch):
|
||||
import json
|
||||
|
||||
def last_successful_commit(is_stable, tag=None):
|
||||
is_stable = str(is_stable).lower() in ("1", "true", "yes")
|
||||
|
||||
try:
|
||||
out = runAndGet(
|
||||
f'gh run list '
|
||||
f'--workflow "{workflow}" '
|
||||
f'--json headSha,conclusion,headBranch'
|
||||
)
|
||||
if is_stable:
|
||||
prev_tag = runAndGet(
|
||||
r'git tag --sort=-v:refname '
|
||||
r'| grep -E "^[0-9]+\.[0-9]+\.[0-9]+$" '
|
||||
r'| sed -n "2p" || true'
|
||||
).strip()
|
||||
|
||||
runs = json.loads(out)
|
||||
if prev_tag:
|
||||
return runAndGet(f'git rev-parse "{prev_tag}^{{commit}}"')
|
||||
|
||||
for r in runs:
|
||||
if r.get("conclusion") == "success" and r.get("headBranch") == branch:
|
||||
return r["headSha"]
|
||||
return None # ← changed
|
||||
|
||||
if tag:
|
||||
exists = subprocess.call(
|
||||
f'git rev-parse -q --verify "refs/tags/{tag}"',
|
||||
shell=True,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
) == 0
|
||||
|
||||
if exists:
|
||||
return runAndGet(f'git rev-parse "{tag}^{{commit}}"')
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def upload_release(release_name, release_tag, commit_sha, repo, upstream_recommendation):
|
||||
def upload_release(release_name, release_tag, commit_sha, repo, upstream_tag_recommended, is_stable=False):
|
||||
is_stable = str(is_stable).lower() in ("1", "true", "yes")
|
||||
draft = False
|
||||
prerelease = True
|
||||
latest = False
|
||||
|
||||
if is_stable:
|
||||
prerelease = False
|
||||
latest = True
|
||||
|
||||
token = getenv("GH_TOKEN")
|
||||
if token:
|
||||
os.environ["GH_TOKEN"] = token
|
||||
@@ -382,7 +372,6 @@ def upload_release(release_name, release_tag, commit_sha, repo, upstream_recomme
|
||||
meta = json.loads(metadata_path.read_text())
|
||||
|
||||
marketing_version = meta.get("version_ipa")
|
||||
is_beta = bool(meta.get("is_beta"))
|
||||
build_datetime = meta.get("version_date")
|
||||
|
||||
dt = datetime.datetime.fromisoformat(
|
||||
@@ -396,52 +385,92 @@ def upload_release(release_name, release_tag, commit_sha, repo, upstream_recomme
|
||||
f"--retrieve {release_tag} "
|
||||
f"--output-dir {ROOT}"
|
||||
)
|
||||
# normalize section header
|
||||
release_notes = re.sub(
|
||||
r'^\s*#{1,6}\s*what(?:\'?s|\s+is)?\s+(?:new|changed).*',
|
||||
"## What's Changed",
|
||||
release_notes,
|
||||
flags=re.IGNORECASE | re.MULTILINE,
|
||||
)
|
||||
|
||||
if is_stable:
|
||||
release_notes = re.sub(
|
||||
r'(?im)^[ \t]*#{1,6}[ \t]*what[’\']?s[ \t]+changed[ \t]*$',
|
||||
"## What's Changed",
|
||||
release_notes,
|
||||
flags=re.IGNORECASE | re.MULTILINE,
|
||||
)
|
||||
|
||||
upstream_block = ""
|
||||
if upstream_recommendation and upstream_recommendation.strip():
|
||||
upstream_block = upstream_recommendation.strip() + "\n\n"
|
||||
if upstream_tag_recommended and upstream_tag_recommended.strip():
|
||||
tag = upstream_tag_recommended.strip()
|
||||
upstream_block = (
|
||||
f"If you want to try out new features early but want a lower chance of bugs, "
|
||||
f"you can look at [{repo} {tag}]"
|
||||
f"(https://github.com/{repo}/releases?q={tag}).\n\n"
|
||||
)
|
||||
|
||||
raw_body = f"""
|
||||
This is an ⚠️ **EXPERIMENTAL** ⚠️ {release_name} build for commit [{commit_sha}](https://github.com/{repo}/commit/{commit_sha}).
|
||||
header = getFormattedUploadMsg(
|
||||
release_name, commit_sha, repo, upstream_block,
|
||||
built_time, built_date, marketing_version, is_stable,
|
||||
)
|
||||
|
||||
{release_name} builds are **extremely experimental builds only meant to be used by developers and beta testers. They often contain bugs and experimental features. Use at your own risk!**
|
||||
|
||||
{upstream_block}## Build Info
|
||||
|
||||
Built at (UTC): `{built_time}`
|
||||
Built at (UTC date): `{built_date}`
|
||||
Commit SHA: `{commit_sha}`
|
||||
Version: `{marketing_version}`
|
||||
"""
|
||||
|
||||
header = inspect.cleandoc(raw_body)
|
||||
body = header + "\n\n" + release_notes.lstrip() + "\n"
|
||||
body = header + release_notes.lstrip() + "\n"
|
||||
|
||||
body_file = ROOT / "release_body.md"
|
||||
body_file.write_text(body, encoding="utf-8")
|
||||
|
||||
prerelease_flag = "--prerelease" if is_beta else ""
|
||||
draft_flag = "--draft" if draft else ""
|
||||
prerelease_flag = "--prerelease" if prerelease else ""
|
||||
latest_flag = "--latest=true" if latest else ""
|
||||
|
||||
run(
|
||||
f'gh release edit "{release_tag}" '
|
||||
f'--title "{release_name}" '
|
||||
f'--notes-file "{body_file}" '
|
||||
f'{prerelease_flag}'
|
||||
)
|
||||
# create release if it doesn't exist
|
||||
exists = subprocess.call(
|
||||
f'gh release view "{release_tag}"',
|
||||
shell=True,
|
||||
cwd=ROOT,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
) == 0
|
||||
|
||||
if exists:
|
||||
run(
|
||||
f'gh release edit "{release_tag}" '
|
||||
f'--title "{release_name}" '
|
||||
f'--notes-file "{body_file}" '
|
||||
f'{draft_flag} {prerelease_flag} {latest_flag}'
|
||||
)
|
||||
else:
|
||||
run(
|
||||
f'gh release create "{release_tag}" '
|
||||
f'--title "{release_name}" '
|
||||
f'--notes-file "{body_file}" '
|
||||
f'{draft_flag} {prerelease_flag} {latest_flag}'
|
||||
)
|
||||
|
||||
run(
|
||||
f'gh release upload "{release_tag}" '
|
||||
f'SideStore.ipa SideStore.dSYMs.zip encrypted-build-logs.zip '
|
||||
f'SideStore.ipa SideStore.dSYMs.zip build-logs.zip '
|
||||
f'--clobber'
|
||||
)
|
||||
|
||||
run(f'git tag -f "{release_tag}" "{commit_sha}"')
|
||||
run(f'git push origin "refs/tags/{release_tag}" --force')
|
||||
|
||||
|
||||
def getFormattedUploadMsg(release_name, commit_sha, repo, upstream_block, built_time, built_date, marketing_version, is_stable):
|
||||
experimental_header = ""
|
||||
if not is_stable:
|
||||
experimental_header = f"""
|
||||
This is an ⚠️ **EXPERIMENTAL** ⚠️ {release_name} build for commit [{commit_sha}](https://github.com/{repo}/commit/{commit_sha}).
|
||||
|
||||
{release_name} builds are **extremely experimental builds only meant to be used by developers and beta testers. They often contain bugs and experimental features. Use at your own risk!**
|
||||
|
||||
""".lstrip("\n")
|
||||
|
||||
header = f"""
|
||||
{experimental_header}{upstream_block}## Build Info
|
||||
|
||||
Built at (UTC): `{built_time}`
|
||||
Built at (UTC date): `{built_date}`
|
||||
Commit SHA: `{commit_sha}`
|
||||
Version: `{marketing_version}`
|
||||
""".lstrip("\n")
|
||||
return header
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# ENTRYPOINT
|
||||
# ----------------------------------------------------------
|
||||
@@ -450,17 +479,20 @@ COMMANDS = {
|
||||
# ----------------------------------------------------------
|
||||
# SHARED
|
||||
# ----------------------------------------------------------
|
||||
"commid-id" : (short_commit, 0, ""),
|
||||
"commit-id" : (short_commit, 0, ""),
|
||||
"count-new-commits" : (count_new_commits, 1, "<last_successful_commit>"),
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# PROJECT INFO
|
||||
# ----------------------------------------------------------
|
||||
"get-marketing-version" : (get_marketing_version, 0, ""),
|
||||
"set-marketing-version" : (set_marketing_version, 1, "<qualified_version>"),
|
||||
"compute-qualified" : (compute_qualified_version, 4, "<marketing> <build_num> <channel> <short_commit>"),
|
||||
"reserve_build_number" : (reserve_build_number, 1, "<repo>"),
|
||||
"set-marketing-version" : (set_marketing_version, 1, "<normalized_version>"),
|
||||
"compute-normalized" : (compute_normalized_version,3, "<marketing> <build_num> <short_commit>"),
|
||||
"get-product-name" : (get_product_name, 0, ""),
|
||||
"get-bundle-id" : (get_bundle_id, 0, ""),
|
||||
"dump-project-settings" : (dump_project_settings, 0, ""),
|
||||
"read-product-name" : (read_product_name, 0, ""),
|
||||
"read-bundle-id" : (read_bundle_id, 0, ""),
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# CLEAN
|
||||
@@ -485,20 +517,22 @@ COMMANDS = {
|
||||
# ----------------------------------------------------------
|
||||
# LOG ENCRYPTION
|
||||
# ----------------------------------------------------------
|
||||
"encrypt-build" : (lambda: encrypt_logs("encrypted-build-logs"), 0, ""),
|
||||
"encrypt-tests-build" : (lambda: encrypt_logs("encrypted-tests-build-logs"), 0, ""),
|
||||
"encrypt-tests-run" : (lambda: encrypt_logs("encrypted-tests-run-logs"), 0, ""),
|
||||
"encrypt-build" : (lambda: encrypt_logs("build-logs"), 0, ""),
|
||||
"encrypt-tests-build" : (lambda: encrypt_logs("tests-build-logs"), 0, ""),
|
||||
"encrypt-tests-run" : (lambda: encrypt_logs("tests-run-logs"), 0, ""),
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# RELEASE / DEPLOY
|
||||
# ----------------------------------------------------------
|
||||
"last-successful-commit" : (last_successful_commit, 2, "<workflow_name> <branch>"),
|
||||
"release-notes" : (release_notes, 1, "<tag>"),
|
||||
"retrieve-release-notes" : (retrieve_release_notes, 1, "<tag>"),
|
||||
"deploy" : (deploy, 9,
|
||||
"<repo> <source_json> <release_tag> <short_commit> <marketing_version> <channel> <bundle_id> <ipa_name> [last_successful_commit]"),
|
||||
"upload-release" : (upload_release, 5, "<release_name> <release_tag> <commit_sha> <repo> <upstream_recommendation>"),
|
||||
}
|
||||
"last-successful-commit" : (last_successful_commit, 1, "<is_stable> [tag]"),
|
||||
"release-notes" : (release_notes, 1, "<tag>"),
|
||||
"retrieve-release-notes" : (retrieve_release_notes, 1, "<tag>"),
|
||||
"generate-metadata" : (generate_metadata, 7,
|
||||
"<release_tag> <short_commit> <marketing_version> <channel> <bundle_id> <ipa_name> [last_successful_commit]"),
|
||||
"deploy" : (deploy, 4,
|
||||
"<repo> <source_json> <release_tag> <marketing_version>"),
|
||||
"upload-release" : (upload_release, 5,
|
||||
"<release_name> <release_tag> <commit_sha> <repo> <upstream_tag_recommended> [is_stable]"),}
|
||||
|
||||
def main():
|
||||
def usage():
|
||||
@@ -524,9 +558,9 @@ def main():
|
||||
suffix = f" {arg_usage}" if arg_usage else ""
|
||||
raise SystemExit(f"Usage: workflow.py {cmd}{suffix}")
|
||||
|
||||
args = sys.argv[2:2 + argc]
|
||||
args = sys.argv[2:]
|
||||
|
||||
result = func(*args) if argc else func()
|
||||
result = func(*args) if args else func()
|
||||
|
||||
# ONLY real outputs go to stdout
|
||||
if result is not None:
|
||||
@@ -534,4 +568,4 @@ def main():
|
||||
sys.stdout.flush()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
main()
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"is_beta": false,
|
||||
"bundle_identifier": "com.SideStore.SideStore",
|
||||
"version_ipa": "0.6.3",
|
||||
"version_date": "2026-02-23T23:38:22Z",
|
||||
"release_channel": "nightly",
|
||||
"size": 29313346,
|
||||
"sha256": "51ec327bca0b0056ccd4c2eb1a130cb7c5bb21de2f303251eea3e0a7336699c4",
|
||||
"download_url": "https://github.com/SideStore/SideStore/releases/download/nightly/SideStore.ipa",
|
||||
"localized_description": "This is release for:\n - version: \"0.6.3-nightly.2026.02.24.42+abc123de\"\n - revision: \"99712f00\"\n - timestamp: \"Mon Feb 23 23:38:22 2026\"\n\nRelease Notes:\n#### What's Changed\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- altsign updated to latest\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- re added openSSL from new path\n- updated altsign to use xcframework for openSSL which was causing huge download of 1.2 GB each time\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: full rewrite - moved logic into ci.py and kept workflow scripts mostly dummy\n\n#### Full Changelog: [38715283...99712f00](https://github.com/SideStore/SideStore/compare/38715283073ea37949a462b889ce3cad403ea499...99712f0020a4f2ae57d8d781514fa735f893c23a)"
|
||||
}
|
||||
Reference in New Issue
Block a user