mirror of
https://github.com/SideStore/SideStore.git
synced 2026-04-12 05:35:38 +02:00
Compare commits
9 Commits
main
...
ny/fix/703
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2f81e4ceb | ||
|
|
05ccccded8 | ||
|
|
950cb7f5b0 | ||
|
|
7f94edc041 | ||
|
|
62f91ec7a9 | ||
|
|
22fe96f923 | ||
|
|
10552780b1 | ||
|
|
daa82d00cf | ||
|
|
b0dd0bb416 |
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
|
|
||||||
'''
|
|
||||||
4
.github/workflows/alpha.yml
vendored
4
.github/workflows/alpha.yml
vendored
@@ -10,8 +10,8 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Reusable-build:
|
Reuseable-build:
|
||||||
uses: ./.github/workflows/reusable-sidestore-build.yml
|
uses: ./.github/workflows/reusable-build-workflow.yml
|
||||||
with:
|
with:
|
||||||
# bundle_id: "com.SideStore.SideStore.Alpha"
|
# bundle_id: "com.SideStore.SideStore.Alpha"
|
||||||
bundle_id: "com.SideStore.SideStore"
|
bundle_id: "com.SideStore.SideStore"
|
||||||
|
|||||||
4
.github/workflows/nightly.yml
vendored
4
.github/workflows/nightly.yml
vendored
@@ -58,13 +58,13 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
LAST_SUCCESS: ${{ env.last_success }}
|
LAST_SUCCESS: ${{ env.last_success }}
|
||||||
|
|
||||||
Reusable-build:
|
Reuseable-build:
|
||||||
if: |
|
if: |
|
||||||
always() &&
|
always() &&
|
||||||
(github.event_name == 'push' ||
|
(github.event_name == 'push' ||
|
||||||
(github.event_name == 'schedule' && needs.check-changes.result == 'success' && needs.check-changes.outputs.has_changes == 'true'))
|
(github.event_name == 'schedule' && needs.check-changes.result == 'success' && needs.check-changes.outputs.has_changes == 'true'))
|
||||||
needs: check-changes
|
needs: check-changes
|
||||||
uses: ./.github/workflows/reusable-sidestore-build.yml
|
uses: ./.github/workflows/reusable-build-workflow.yml
|
||||||
with:
|
with:
|
||||||
# bundle_id: "com.SideStore.SideStore.Nightly"
|
# bundle_id: "com.SideStore.SideStore.Nightly"
|
||||||
bundle_id: "com.SideStore.SideStore"
|
bundle_id: "com.SideStore.SideStore"
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
serialize:
|
common:
|
||||||
name: Wait for other jobs
|
name: Wait for other jobs
|
||||||
# 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
|
# 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
|
# so we serialize on the entire workflow
|
||||||
@@ -68,13 +68,13 @@ jobs:
|
|||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build SideStore - ${{ inputs.release_tag }}
|
name: Build SideStore - ${{ inputs.release_tag }}
|
||||||
needs: serialize
|
needs: common
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: 'macos-15'
|
- os: 'macos-15'
|
||||||
version: '16.2'
|
version: '16.1'
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
outputs:
|
outputs:
|
||||||
@@ -160,7 +160,7 @@ jobs:
|
|||||||
build_num=$(echo "${{ steps.version.outputs.version }}" | sed -E 's/.*\.([0-9]+)\+.*/\1/')
|
build_num=$(echo "${{ steps.version.outputs.version }}" | sed -E 's/.*\.([0-9]+)\+.*/\1/')
|
||||||
|
|
||||||
# Combine them into the final output
|
# Combine them into the final output
|
||||||
MARKETING_VERSION="${version}-${date}.${build_num}+${{ needs.serialize.outputs.short-commit }}"
|
MARKETING_VERSION="${version}-${date}.${build_num}+${{ needs.common.outputs.short-commit }}"
|
||||||
|
|
||||||
echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV
|
echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV
|
||||||
echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_OUTPUT
|
echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_OUTPUT
|
||||||
@@ -177,32 +177,14 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
xcode-version: ${{ matrix.version }}
|
xcode-version: ${{ matrix.version }}
|
||||||
|
|
||||||
- name: (Build) Restore Xcode & SwiftPM Cache (Exact match)
|
- name: (Build) Cache Build
|
||||||
id: xcode-cache-restore
|
uses: irgaly/xcode-cache@v1
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
key: xcode-cache-deriveddata-build-${{ github.ref_name }}-${{ github.sha }}
|
||||||
~/Library/Developer/Xcode/DerivedData
|
restore-keys: xcode-cache-deriveddata-build-${{ github.ref_name }}-
|
||||||
~/Library/Caches/org.swift.swiftpm
|
swiftpm-cache-key: xcode-cache-sourcedata-build-${{ github.ref_name }}-${{ github.sha }}
|
||||||
key: xcode-cache-build-${{ github.ref_name }}-${{ github.sha }}
|
swiftpm-cache-restore-keys: |
|
||||||
|
xcode-cache-sourcedata-build-${{ github.ref_name }}-
|
||||||
- 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) Restore Pods from Cache (Exact match)
|
- name: (Build) Restore Pods from Cache (Exact match)
|
||||||
id: pods-restore
|
id: pods-restore
|
||||||
@@ -287,16 +269,6 @@ jobs:
|
|||||||
- name: Convert to IPA
|
- name: Convert to IPA
|
||||||
run: make ipa | tee -a build/logs/build.log
|
run: make ipa | tee -a build/logs/build.log
|
||||||
|
|
||||||
- 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
|
- name: (Build) List Files and Build artifacts
|
||||||
run: |
|
run: |
|
||||||
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
echo ">>>>>>>>> Workdir <<<<<<<<<<"
|
||||||
@@ -315,10 +287,6 @@ jobs:
|
|||||||
find SideStore.xcarchive -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
find SideStore.xcarchive -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
|
||||||
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
- name: Encrypt build-logs for upload
|
- name: Encrypt build-logs for upload
|
||||||
id: encrypt-build-log
|
id: encrypt-build-log
|
||||||
run: |
|
run: |
|
||||||
@@ -368,7 +336,7 @@ jobs:
|
|||||||
|
|
||||||
echo "Adding files to commit"
|
echo "Adding files to commit"
|
||||||
git add --verbose build_number.txt
|
git add --verbose build_number.txt
|
||||||
git commit -m " - updated for ${{ inputs.release_tag }} - ${{ needs.serialize.outputs.short-commit }} deployment" || echo "No changes to commit"
|
git commit -m " - updated for ${{ inputs.release_tag }} - ${{ needs.common.outputs.short-commit }} deployment" || echo "No changes to commit"
|
||||||
|
|
||||||
echo "Pushing to remote repo"
|
echo "Pushing to remote repo"
|
||||||
git push --verbose
|
git push --verbose
|
||||||
@@ -403,31 +371,30 @@ jobs:
|
|||||||
- name: Upload release-notes.md
|
- name: Upload release-notes.md
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release-notes-${{ needs.serialize.outputs.short-commit }}.md
|
name: release-notes-${{ needs.common.outputs.short-commit }}.md
|
||||||
path: release-notes.md
|
path: release-notes.md
|
||||||
|
|
||||||
- name: Upload update_release_notes.py
|
- name: Upload update_release_notes.py
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: update_release_notes-${{ needs.serialize.outputs.short-commit }}.py
|
name: update_release_notes-${{ needs.common.outputs.short-commit }}.py
|
||||||
path: update_release_notes.py
|
path: update_release_notes.py
|
||||||
|
|
||||||
- name: Upload update_apps.py
|
- name: Upload update_apps.py
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: update_apps-${{ needs.serialize.outputs.short-commit }}.py
|
name: update_apps-${{ needs.common.outputs.short-commit }}.py
|
||||||
path: update_apps.py
|
path: update_apps.py
|
||||||
|
|
||||||
tests-build:
|
tests-build:
|
||||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
|
||||||
name: Tests-Build SideStore - ${{ inputs.release_tag }}
|
name: Tests-Build SideStore - ${{ inputs.release_tag }}
|
||||||
needs: serialize
|
needs: common
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: 'macos-15'
|
- os: 'macos-15'
|
||||||
version: '16.2'
|
version: '16.1'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -443,36 +410,16 @@ jobs:
|
|||||||
- name: Setup Xcode
|
- name: Setup Xcode
|
||||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||||
with:
|
with:
|
||||||
xcode-version: '16.2'
|
xcode-version: '16.1'
|
||||||
|
|
||||||
# - name: (Tests-Build) Cache Build
|
- name: (Tests-Build) Cache Build
|
||||||
# uses: irgaly/xcode-cache@v1.8.1
|
uses: irgaly/xcode-cache@v1
|
||||||
# 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:
|
with:
|
||||||
path: |
|
key: xcode-cache-deriveddata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||||
~/Library/Developer/Xcode/DerivedData
|
restore-keys: xcode-cache-deriveddata-test-${{ github.ref_name }}-
|
||||||
~/Library/Caches/org.swift.swiftpm
|
swiftpm-cache-key: xcode-cache-sourcedata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||||
key: xcode-cache-tests-${{ github.ref_name }}-${{ github.sha }}
|
swiftpm-cache-restore-keys: |
|
||||||
|
xcode-cache-sourcedata-test-${{ github.ref_name }}-
|
||||||
- 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: (Tests-Build) Restore Pods from Cache (Exact match)
|
- name: (Tests-Build) Restore Pods from Cache (Exact match)
|
||||||
id: pods-restore
|
id: pods-restore
|
||||||
@@ -508,13 +455,6 @@ jobs:
|
|||||||
./AltStore.xcworkspace/
|
./AltStore.xcworkspace/
|
||||||
key: pods-cache-test-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
key: pods-cache-test-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
||||||
|
|
||||||
- name: Clean Derived Data (if required)
|
|
||||||
if: ${{ vars.PERFORM_CLEAN_TESTS_BUILD == '1' }}
|
|
||||||
run: |
|
|
||||||
rm -rf ~/Library/Developer/Xcode/DerivedData/
|
|
||||||
make clean
|
|
||||||
xcodebuild clean
|
|
||||||
|
|
||||||
- name: (Tests-Build) Clean previous build artifacts
|
- name: (Tests-Build) Clean previous build artifacts
|
||||||
run: |
|
run: |
|
||||||
make clean
|
make clean
|
||||||
@@ -547,16 +487,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
NSUnbufferedIO=YES make -B build-tests 2>&1 | tee -a build/logs/tests-build.log | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]}
|
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
|
- name: (Tests-Build) List Files and Build artifacts
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
@@ -568,16 +498,6 @@ jobs:
|
|||||||
find build -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
find build -maxdepth 3 -exec ls -ld {} + || true # List contents if directory exists
|
||||||
echo ""
|
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-${{ needs.serialize.outputs.short-commit }}.txt
|
|
||||||
path: tests-build-deriveddata.txt
|
|
||||||
|
|
||||||
- name: Encrypt tests-build-logs for upload
|
- name: Encrypt tests-build-logs for upload
|
||||||
id: encrypt-test-log
|
id: encrypt-test-log
|
||||||
if: always()
|
if: always()
|
||||||
@@ -599,19 +519,18 @@ jobs:
|
|||||||
if: always() && steps.encrypt-test-log.outputs.encrypted == 'true'
|
if: always() && steps.encrypt-test-log.outputs.encrypted == 'true'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: encrypted-tests-build-logs-${{ needs.serialize.outputs.short-commit }}.zip
|
name: encrypted-tests-build-logs-${{ needs.common.outputs.short-commit }}.zip
|
||||||
path: encrypted-tests-build-logs.zip
|
path: encrypted-tests-build-logs.zip
|
||||||
|
|
||||||
tests-run:
|
tests-run:
|
||||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
|
||||||
name: Tests-Run SideStore - ${{ inputs.release_tag }}
|
name: Tests-Run SideStore - ${{ inputs.release_tag }}
|
||||||
needs: [serialize, tests-build]
|
needs: [common, tests-build]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: 'macos-15'
|
- os: 'macos-15'
|
||||||
version: '16.2'
|
version: '16.1'
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -628,23 +547,16 @@ jobs:
|
|||||||
- name: Setup Xcode
|
- name: Setup Xcode
|
||||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||||
with:
|
with:
|
||||||
xcode-version: '16.2'
|
xcode-version: '16.1'
|
||||||
|
|
||||||
# - name: (Tests-Run) Cache Build
|
- name: (Tests-Run) Cache Build
|
||||||
# uses: irgaly/xcode-cache@v1.8.1
|
uses: irgaly/xcode-cache@v1
|
||||||
# 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:
|
with:
|
||||||
path: |
|
key: xcode-cache-deriveddata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||||
~/Library/Developer/Xcode/DerivedData
|
restore-keys: xcode-cache-deriveddata-test-${{ github.ref_name }}-
|
||||||
~/Library/Caches/org.swift.swiftpm
|
swiftpm-cache-key: xcode-cache-sourcedata-test-${{ github.ref_name }}-${{ github.sha }}
|
||||||
key: xcode-cache-tests-${{ github.ref_name }}-${{ github.sha }}
|
swiftpm-cache-restore-keys: |
|
||||||
|
xcode-cache-sourcedata-test-${{ github.ref_name }}-
|
||||||
|
|
||||||
- name: (Tests-Run) Restore Pods from Cache (Exact match)
|
- name: (Tests-Run) Restore Pods from Cache (Exact match)
|
||||||
id: pods-restore
|
id: pods-restore
|
||||||
@@ -704,15 +616,9 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
echo ">>>>>>>>> Xcode-Derived-Data <<<<<<<<<<"
|
||||||
find ~/Library/Developer/Xcode/DerivedData -maxdepth 8 -exec ls -ld {} + | grep "Build/Products" >> tests-run-deriveddata.txt || true
|
ls -la ~/Library/Developer/Xcode/DerivedData || true # List contents if directory exists
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: tests-run-deriveddata-${{ needs.serialize.outputs.short-commit }}.txt
|
|
||||||
path: tests-run-deriveddata.txt
|
|
||||||
|
|
||||||
# we expect simulator to have been booted by now, so exit otherwise
|
# we expect simulator to have been booted by now, so exit otherwise
|
||||||
- name: Simulator Boot Check
|
- name: Simulator Boot Check
|
||||||
run: |
|
run: |
|
||||||
@@ -770,7 +676,7 @@ jobs:
|
|||||||
if: always() && steps.encrypt-test-log.outputs.encrypted == 'true'
|
if: always() && steps.encrypt-test-log.outputs.encrypted == 'true'
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: encrypted-tests-run-logs-${{ needs.serialize.outputs.short-commit }}.zip
|
name: encrypted-tests-run-logs-${{ needs.common.outputs.short-commit }}.zip
|
||||||
path: encrypted-tests-run-logs.zip
|
path: encrypted-tests-run-logs.zip
|
||||||
|
|
||||||
- name: Print tests-recording.log contents (if exists)
|
- name: Print tests-recording.log contents (if exists)
|
||||||
@@ -800,7 +706,7 @@ jobs:
|
|||||||
if: ${{ always() && steps.check-recording.outputs.found == 'true' }}
|
if: ${{ always() && steps.check-recording.outputs.found == 'true' }}
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: tests-recording-${{ needs.serialize.outputs.short-commit }}.mp4
|
name: tests-recording-${{ needs.common.outputs.short-commit }}.mp4
|
||||||
path: tests-recording.mp4
|
path: tests-recording.mp4
|
||||||
|
|
||||||
- name: Zip test-results
|
- name: Zip test-results
|
||||||
@@ -809,14 +715,14 @@ jobs:
|
|||||||
- name: Upload Test Artifacts
|
- name: Upload Test Artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ needs.serialize.outputs.short-commit }}.zip
|
name: test-results-${{ needs.common.outputs.short-commit }}.zip
|
||||||
path: test-results.zip
|
path: test-results.zip
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
name: Deploy SideStore - ${{ inputs.release_tag }}
|
name: Deploy SideStore - ${{ inputs.release_tag }}
|
||||||
runs-on: macos-15
|
runs-on: macos-15
|
||||||
# needs: [serialize, build]
|
# needs: [common, build]
|
||||||
needs: [serialize, build, tests-build, tests-run]
|
needs: [common, build, tests-build, tests-run]
|
||||||
steps:
|
steps:
|
||||||
- name: Download IPA artifact
|
- name: Download IPA artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
@@ -834,43 +740,39 @@ jobs:
|
|||||||
name: encrypted-build-logs-${{ needs.build.outputs.version }}.zip
|
name: encrypted-build-logs-${{ needs.build.outputs.version }}.zip
|
||||||
|
|
||||||
- name: Download encrypted-tests-build-logs artifact
|
- name: Download encrypted-tests-build-logs artifact
|
||||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: encrypted-tests-build-logs-${{ needs.serialize.outputs.short-commit }}.zip
|
name: encrypted-tests-build-logs-${{ needs.common.outputs.short-commit }}.zip
|
||||||
|
|
||||||
- name: Download encrypted-tests-run-logs artifact
|
- name: Download encrypted-tests-run-logs artifact
|
||||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: encrypted-tests-run-logs-${{ needs.serialize.outputs.short-commit }}.zip
|
name: encrypted-tests-run-logs-${{ needs.common.outputs.short-commit }}.zip
|
||||||
|
|
||||||
- name: Download tests-recording artifact
|
- name: Download tests-recording artifact
|
||||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: tests-recording-${{ needs.serialize.outputs.short-commit }}.mp4
|
name: tests-recording-${{ needs.common.outputs.short-commit }}.mp4
|
||||||
|
|
||||||
- name: Download test-results artifact
|
- name: Download test-results artifact
|
||||||
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ needs.serialize.outputs.short-commit }}.zip
|
name: test-results-${{ needs.common.outputs.short-commit }}.zip
|
||||||
|
|
||||||
- name: Download release-notes.md
|
- name: Download release-notes.md
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: release-notes-${{ needs.serialize.outputs.short-commit }}.md
|
name: release-notes-${{ needs.common.outputs.short-commit }}.md
|
||||||
|
|
||||||
- name: Download update_release_notes.py
|
- name: Download update_release_notes.py
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: update_release_notes-${{ needs.serialize.outputs.short-commit }}.py
|
name: update_release_notes-${{ needs.common.outputs.short-commit }}.py
|
||||||
|
|
||||||
- name: Download update_apps.py
|
- name: Download update_apps.py
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: update_apps-${{ needs.serialize.outputs.short-commit }}.py
|
name: update_apps-${{ needs.common.outputs.short-commit }}.py
|
||||||
|
|
||||||
- name: Read release notes
|
- name: Read release notes
|
||||||
id: release_notes
|
id: release_notes
|
||||||
@@ -948,7 +850,7 @@ jobs:
|
|||||||
LOCALIZED_DESCRIPTION=$(cat <<EOF
|
LOCALIZED_DESCRIPTION=$(cat <<EOF
|
||||||
This is release for:
|
This is release for:
|
||||||
- version: "${{ needs.build.outputs.version }}"
|
- version: "${{ needs.build.outputs.version }}"
|
||||||
- revision: "${{ needs.serialize.outputs.short-commit }}"
|
- revision: "${{ needs.common.outputs.short-commit }}"
|
||||||
- timestamp: "${{ steps.date.outputs.date }}"
|
- timestamp: "${{ steps.date.outputs.date }}"
|
||||||
|
|
||||||
Release Notes:
|
Release Notes:
|
||||||
@@ -1001,7 +903,7 @@ jobs:
|
|||||||
|
|
||||||
# Commit changes and push using SSH
|
# Commit changes and push using SSH
|
||||||
git add --verbose ./_includes/source.json
|
git add --verbose ./_includes/source.json
|
||||||
git commit -m " - updated for ${{ needs.serialize.outputs.short-commit }} deployment" || echo "No changes to commit"
|
git commit -m " - updated for ${{ needs.common.outputs.short-commit }} deployment" || echo "No changes to commit"
|
||||||
|
|
||||||
git push --verbose
|
git push --verbose
|
||||||
popd
|
popd
|
||||||
104
.github/workflows/reusable-sidestore-build.yml
vendored
104
.github/workflows/reusable-sidestore-build.yml
vendored
@@ -1,104 +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
|
|
||||||
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
|
|
||||||
401
.github/workflows/sidestore-build.yml
vendored
401
.github/workflows/sidestore-build.yml
vendored
@@ -1,401 +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-15'
|
|
||||||
version: '16.2'
|
|
||||||
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) Restore Pods from Cache (Exact match)
|
|
||||||
id: pods-restore
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-build-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
|
||||||
# restore-keys: | # commented out to strictly check cache for this particular podfile
|
|
||||||
# pods-cache-
|
|
||||||
|
|
||||||
- name: (Build) Restore Pods from Cache (Last Available)
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
id: pods-restore-recent
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-build-${{ github.ref_name }}-
|
|
||||||
|
|
||||||
|
|
||||||
- name: (Build) Install CocoaPods
|
|
||||||
run: pod install
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: (Build) Save Pods to Cache
|
|
||||||
id: save-pods
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
uses: actions/cache/save@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-build-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
|
||||||
|
|
||||||
- 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 ">>>>>>>>> Pods <<<<<<<<<<"
|
|
||||||
find Pods -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
|
||||||
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
|
|
||||||
235
.github/workflows/sidestore-deploy.yml
vendored
235
.github/workflows/sidestore-deploy.yml
vendored
@@ -1,235 +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: 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: SideStore.ipa SideStore.dSYMs.zip encrypted-build-logs.zip encrypted-tests-build-logs.zip encrypted-tests-run-logs.zip test-results.zip tests-recording.mp4
|
|
||||||
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!**
|
|
||||||
|
|
||||||
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 }}).
|
|
||||||
|
|
||||||
## 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: |
|
|
||||||
# Format localized description
|
|
||||||
LOCALIZED_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
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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/sidestore-shared.yml
vendored
24
.github/workflows/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 }}
|
|
||||||
204
.github/workflows/sidestore-tests-build.yml
vendored
204
.github/workflows/sidestore-tests-build.yml
vendored
@@ -1,204 +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-15'
|
|
||||||
version: '16.2'
|
|
||||||
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: '16.2'
|
|
||||||
|
|
||||||
# - 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: (Tests-Build) Restore Pods from Cache (Exact match)
|
|
||||||
id: pods-restore
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-test-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
|
||||||
|
|
||||||
- name: (Tests-Build) Restore Pods from Cache (Last Available)
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
id: pods-restore-recent
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-test-${{ github.ref_name }}-
|
|
||||||
|
|
||||||
- name: (Tests-Build) Install CocoaPods
|
|
||||||
run: pod install
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: (Tests-Build) Save Pods to Cache
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
uses: actions/cache/save@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-test-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
|
||||||
|
|
||||||
- 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 ">>>>>>>>> Pods <<<<<<<<<<"
|
|
||||||
find Pods -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
|
||||||
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
|
|
||||||
235
.github/workflows/sidestore-tests-run.yml
vendored
235
.github/workflows/sidestore-tests-run.yml
vendored
@@ -1,235 +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-15'
|
|
||||||
version: '16.2'
|
|
||||||
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: '16.2'
|
|
||||||
|
|
||||||
# - 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) Restore Pods from Cache (Exact match)
|
|
||||||
id: pods-restore
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-test-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
|
||||||
|
|
||||||
- name: (Tests-Run) Restore Pods from Cache (Last Available)
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
id: pods-restore-recent
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-test-${{ github.ref_name }}-
|
|
||||||
|
|
||||||
- name: (Tests-Run) Install CocoaPods
|
|
||||||
run: pod install
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: (Tests-Run) Save Pods to Cache
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
uses: actions/cache/save@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-test-${{ github.ref_name }}-${{ hashFiles('Podfile') }}
|
|
||||||
|
|
||||||
- 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 ">>>>>>>>> Pods <<<<<<<<<<"
|
|
||||||
find Pods -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
|
||||||
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
|
|
||||||
266
.github/workflows/stable.yml
vendored
266
.github/workflows/stable.yml
vendored
@@ -7,268 +7,73 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build SideStore - stable (on tag push)
|
name: Build and upload SideStore
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: 'macos-15'
|
- os: 'macos-14'
|
||||||
version: '16.2'
|
version: '15.4'
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
- name: Echo Build.xcconfig
|
- name: Install dependencies
|
||||||
run: |
|
run: brew install ldid
|
||||||
echo "cat Build.xcconfig"
|
|
||||||
cat Build.xcconfig
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
# - name: Change MARKETING_VERSION to the pushed tag that triggered this build
|
- name: Change version to tag
|
||||||
# run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
|
run: sed -e '/MARKETING_VERSION = .*/s/= .*/= ${{ github.ref_name }}/' -i '' Build.xcconfig
|
||||||
|
|
||||||
- name: Echo Updated Build.xcconfig
|
- name: Get version
|
||||||
run: |
|
|
||||||
cat Build.xcconfig
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Extract MARKETING_VERSION from Build.xcconfig
|
|
||||||
id: version
|
id: version
|
||||||
run: |
|
run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT
|
||||||
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
|
- name: Echo version
|
||||||
echo "MARKETING_VERSION=$version" >> $GITHUB_OUTPUT
|
run: echo "${{ steps.version.outputs.version }}"
|
||||||
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 }}"
|
|
||||||
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
|
|
||||||
|
|
||||||
- name: Setup Xcode
|
- name: Setup Xcode
|
||||||
uses: maxim-lobanov/setup-xcode@v1.6.0
|
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||||
with:
|
with:
|
||||||
xcode-version: ${{ matrix.version }}
|
xcode-version: ${{ matrix.version }}
|
||||||
|
|
||||||
- name: (Build) Restore Xcode & SwiftPM Cache (Exact match)
|
- name: Cache Build
|
||||||
id: xcode-cache-restore
|
uses: irgaly/xcode-cache@v1
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
key: xcode-cache-deriveddata-${{ github.sha }}
|
||||||
~/Library/Developer/Xcode/DerivedData
|
restore-keys: xcode-cache-deriveddata-
|
||||||
~/Library/Caches/org.swift.swiftpm
|
swiftpm-cache-key: xcode-cache-sourcedata-${{ github.sha }}
|
||||||
key: xcode-cache-build-stable-${{ github.sha }}
|
swiftpm-cache-restore-keys: |
|
||||||
|
xcode-cache-sourcedata-
|
||||||
|
|
||||||
- name: (Build) Restore Xcode & SwiftPM Cache (Last Available)
|
- name: Build SideStore
|
||||||
id: xcode-cache-restore-recent
|
run: NSUnbufferedIO=YES make build | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]}
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/Library/Developer/Xcode/DerivedData
|
|
||||||
~/Library/Caches/org.swift.swiftpm
|
|
||||||
key: xcode-cache-build-stable-
|
|
||||||
|
|
||||||
- name: (Build) Restore Pods from Cache (Exact match)
|
|
||||||
id: pods-restore
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-build-stable-${{ hashFiles('Podfile') }}
|
|
||||||
|
|
||||||
- name: (Build) Restore Pods from Cache (Last Available)
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
id: pods-restore-recent
|
|
||||||
uses: actions/cache/restore@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-build-stable-
|
|
||||||
|
|
||||||
|
|
||||||
- name: (Build) Install CocoaPods
|
|
||||||
run: pod install
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: (Build) Save Pods to Cache
|
|
||||||
id: save-pods
|
|
||||||
if: ${{ steps.pods-restore.outputs.cache-hit != 'true' }}
|
|
||||||
uses: actions/cache/save@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
./Podfile.lock
|
|
||||||
./Pods/
|
|
||||||
./AltStore.xcworkspace/
|
|
||||||
key: pods-cache-build-stable-${{ hashFiles('Podfile') }}
|
|
||||||
|
|
||||||
- name: (Build) Clean previous build artifacts
|
|
||||||
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 ">>>>>>>>> Pods <<<<<<<<<<"
|
|
||||||
find Pods -maxdepth 2 -exec ls -ld {} + || true # List contents if directory exists
|
|
||||||
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
|
- name: Fakesign app
|
||||||
run: make fakesign | tee -a build/logs/build.log
|
run: make fakesign
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Convert to IPA
|
- name: Convert to IPA
|
||||||
run: make ipa | tee -a build/logs/build.log
|
run: make ipa
|
||||||
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-stable-${{ 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: Get current date
|
- name: Get current date
|
||||||
id: date
|
id: date
|
||||||
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
|
run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Get current date in AltStore date form
|
- name: Get current date in AltStore date form
|
||||||
id: date_altstore
|
id: date_altstore
|
||||||
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||||
shell: bash
|
|
||||||
|
|
||||||
- name: Upload to releases
|
- name: Upload to new stable release
|
||||||
uses: IsaacShelton/update-existing-release@v1.3.1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
name: ${{ steps.version.outputs.version }}
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
draft: true
|
draft: true
|
||||||
release: ${{ github.ref_name }} # name
|
files: SideStore.ipa
|
||||||
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: |
|
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. -->
|
<!-- 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
|
## Changelog
|
||||||
@@ -281,3 +86,18 @@ jobs:
|
|||||||
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
|
Built at (UTC date): `${{ steps.date_altstore.outputs.date }}`
|
||||||
Commit SHA: `${{ github.sha }}`
|
Commit SHA: `${{ github.sha }}`
|
||||||
Version: `${{ steps.version.outputs.version }}`
|
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/
|
||||||
|
|||||||
@@ -59,15 +59,6 @@
|
|||||||
A80D60D32D3DD85100CEF65D /* ReleaseTrack.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */; };
|
A80D60D32D3DD85100CEF65D /* ReleaseTrack.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */; };
|
||||||
A80D790D2D2F20AF00A40F40 /* PaginationIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */; };
|
A80D790D2D2F20AF00A40F40 /* PaginationIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */; };
|
||||||
A80D790F2D2F217000A40F40 /* PaginationDataHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */; };
|
A80D790F2D2F217000A40F40 /* PaginationDataHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */; };
|
||||||
A815AA952D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AA942D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift */; };
|
|
||||||
A815AA972D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AA962D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift */; };
|
|
||||||
A815AA992D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A815AA982D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel */; };
|
|
||||||
A815AA9F2D9104DD00929A9E /* BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AA9E2D9104DD00929A9E /* BuildInfo.swift */; };
|
|
||||||
A815AAA12D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA02D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel */; };
|
|
||||||
A815AAA72D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA42D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift */; };
|
|
||||||
A815AAA82D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA52D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift */; };
|
|
||||||
A815AAA92D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA22D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift */; };
|
|
||||||
A815AAAA2D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A815AAA32D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift */; };
|
|
||||||
A81A8CB92D68B30B0086C96F /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
|
A81A8CB92D68B30B0086C96F /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
|
||||||
A81A8CBA2D68B3110086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
|
A81A8CBA2D68B3110086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
|
||||||
A81A8CBD2D68B43F0086C96F /* LinkedHashMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */; };
|
A81A8CBD2D68B43F0086C96F /* LinkedHashMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */; };
|
||||||
@@ -78,8 +69,6 @@
|
|||||||
A81A8CD12D68BA9B0086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
|
A81A8CD12D68BA9B0086C96F /* TreeMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81A8CB02D68B0320086C96F /* TreeMap.swift */; };
|
||||||
A81A8CD22D68BAA30086C96F /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
|
A81A8CD22D68BAA30086C96F /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; };
|
||||||
A81A8CD42D68BAFF0086C96F /* DataStructureTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = A81A8CD32D68BAFF0086C96F /* DataStructureTests.xctestplan */; };
|
A81A8CD42D68BAFF0086C96F /* DataStructureTests.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = A81A8CD32D68BAFF0086C96F /* DataStructureTests.xctestplan */; };
|
||||||
A81BF9E52D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81BF9E22D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift */; };
|
|
||||||
A81BF9E72D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A81BF9E32D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift */; };
|
|
||||||
A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */; };
|
A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5708416292448DA00D42D34 /* OperatingSystemVersion+Comparable.swift */; };
|
||||||
A82067C42D03E0DE00645C0D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = A82067C32D03E0DE00645C0D /* SemanticVersion */; };
|
A82067C42D03E0DE00645C0D /* SemanticVersion in Frameworks */ = {isa = PBXBuildFile; productRef = A82067C32D03E0DE00645C0D /* SemanticVersion */; };
|
||||||
A8228B5B2D6E2C0C00F7CE0E /* (null) in Sources */ = {isa = PBXBuildFile; };
|
A8228B5B2D6E2C0C00F7CE0E /* (null) in Sources */ = {isa = PBXBuildFile; };
|
||||||
@@ -88,6 +77,7 @@
|
|||||||
A859ED5D2D1EE827003DCC58 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
A859ED5D2D1EE827003DCC58 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
A86315DF2D3EB2DE0048FA40 /* ErrorProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */; };
|
A86315DF2D3EB2DE0048FA40 /* ErrorProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */; };
|
||||||
A8696EE42D34512C00E96389 /* RemoveAppExtensionsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */; };
|
A8696EE42D34512C00E96389 /* RemoveAppExtensionsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */; };
|
||||||
|
A881E7C72D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A881E7C62D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel */; };
|
||||||
A881E7CB2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */; };
|
A881E7CB2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */; };
|
||||||
A88B8C492D35AD3200F53F9D /* OperationsLoggingContolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */; };
|
A88B8C492D35AD3200F53F9D /* OperationsLoggingContolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */; };
|
||||||
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */; };
|
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */; };
|
||||||
@@ -97,9 +87,6 @@
|
|||||||
A8AD35592D31BF2C003A28B4 /* PageInfoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8AD35582D31BF29003A28B4 /* PageInfoManager.swift */; };
|
A8AD35592D31BF2C003A28B4 /* PageInfoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8AD35582D31BF29003A28B4 /* PageInfoManager.swift */; };
|
||||||
A8B516E32D2666CA0047047C /* CoreDataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8B516E22D2666CA0047047C /* CoreDataHelper.swift */; };
|
A8B516E32D2666CA0047047C /* CoreDataHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8B516E22D2666CA0047047C /* CoreDataHelper.swift */; };
|
||||||
A8B516E62D2668170047047C /* DateTimeUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8B516E52D2668020047047C /* DateTimeUtil.swift */; };
|
A8B516E62D2668170047047C /* DateTimeUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8B516E52D2668020047047C /* DateTimeUtil.swift */; };
|
||||||
A8B645FC2D70C10300125819 /* CollapsingMarkdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8B645FB2D70C10300125819 /* CollapsingMarkdownView.swift */; };
|
|
||||||
A8B645FF2D70C1AD00125819 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = A8B645FE2D70C1AD00125819 /* MarkdownKit */; };
|
|
||||||
A8B646012D70C23E00125819 /* MarkdownKit in Frameworks */ = {isa = PBXBuildFile; productRef = A8B646002D70C23E00125819 /* MarkdownKit */; };
|
|
||||||
A8BB34E52D04EC8E000A8B4D /* minimuxer-helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A809F6A52D04DA1900F0F0F3 /* minimuxer-helpers.swift */; };
|
A8BB34E52D04EC8E000A8B4D /* minimuxer-helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A809F6A52D04DA1900F0F0F3 /* minimuxer-helpers.swift */; };
|
||||||
A8C38C242D206A3A00E83DBD /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C38C1D2D206A3A00E83DBD /* ConsoleLogger.swift */; };
|
A8C38C242D206A3A00E83DBD /* ConsoleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C38C1D2D206A3A00E83DBD /* ConsoleLogger.swift */; };
|
||||||
A8C38C262D206A3A00E83DBD /* ConsoleLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C38C1E2D206A3A00E83DBD /* ConsoleLog.swift */; };
|
A8C38C262D206A3A00E83DBD /* ConsoleLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8C38C1E2D206A3A00E83DBD /* ConsoleLog.swift */; };
|
||||||
@@ -113,7 +100,6 @@
|
|||||||
A8C6D5142D1EE8D700DF01F1 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
A8C6D5142D1EE8D700DF01F1 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
A8C6D5172D1EE95B00DF01F1 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; };
|
A8C6D5172D1EE95B00DF01F1 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; };
|
||||||
A8C6D5182D1EE95B00DF01F1 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
A8C6D5182D1EE95B00DF01F1 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
A8C924242D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = A8C924232D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel */; };
|
|
||||||
A8D484D82D0CD306002C691D /* AltBackup.ipa in Resources */ = {isa = PBXBuildFile; fileRef = A8D484D72D0CD306002C691D /* AltBackup.ipa */; };
|
A8D484D82D0CD306002C691D /* AltBackup.ipa in Resources */ = {isa = PBXBuildFile; fileRef = A8D484D72D0CD306002C691D /* AltBackup.ipa */; };
|
||||||
A8D49F532D3D2F9400844B92 /* ProcessInfo+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */; };
|
A8D49F532D3D2F9400844B92 /* ProcessInfo+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */; };
|
||||||
A8E2DB312D684E2A009E5D31 /* UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8E2DB2E2D684E2A009E5D31 /* UITests.swift */; };
|
A8E2DB312D684E2A009E5D31 /* UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8E2DB2E2D684E2A009E5D31 /* UITests.swift */; };
|
||||||
@@ -671,15 +657,6 @@
|
|||||||
A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack.swift; sourceTree = "<group>"; };
|
A80D60D12D3D705F00CEF65D /* ReleaseTrack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack.swift; sourceTree = "<group>"; };
|
||||||
A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIntent.swift; sourceTree = "<group>"; };
|
A80D790C2D2F20AF00A40F40 /* PaginationIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationIntent.swift; sourceTree = "<group>"; };
|
||||||
A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationDataHolder.swift; sourceTree = "<group>"; };
|
A80D790E2D2F217000A40F40 /* PaginationDataHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginationDataHolder.swift; sourceTree = "<group>"; };
|
||||||
A815AA942D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A815AA962D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A815AA982D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore17ToAltStore17_1.xcmappingmodel; sourceTree = "<group>"; };
|
|
||||||
A815AA9E2D9104DD00929A9E /* BuildInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildInfo.swift; sourceTree = "<group>"; };
|
|
||||||
A815AAA02D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore11ToAltStore17_1.xcmappingmodel; sourceTree = "<group>"; };
|
|
||||||
A815AAA22D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A815AAA32D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A815AAA42D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseTrack11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A815AAA52D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermission11To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A81A8CB02D68B0320086C96F /* TreeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMap.swift; sourceTree = "<group>"; };
|
A81A8CB02D68B0320086C96F /* TreeMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMap.swift; sourceTree = "<group>"; };
|
||||||
A81A8CB42D68B2180086C96F /* TreeMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMapTests.swift; sourceTree = "<group>"; };
|
A81A8CB42D68B2180086C96F /* TreeMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeMapTests.swift; sourceTree = "<group>"; };
|
||||||
A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedHashMap.swift; sourceTree = "<group>"; };
|
A81A8CBC2D68B43F0086C96F /* LinkedHashMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkedHashMap.swift; sourceTree = "<group>"; };
|
||||||
@@ -687,9 +664,6 @@
|
|||||||
A81A8CC52D68BA610086C96F /* DataStructureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DataStructureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
A81A8CC52D68BA610086C96F /* DataStructureTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DataStructureTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A81A8CC72D68BA610086C96F /* DataStructuresTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStructuresTests.swift; sourceTree = "<group>"; };
|
A81A8CC72D68BA610086C96F /* DataStructuresTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataStructuresTests.swift; sourceTree = "<group>"; };
|
||||||
A81A8CD32D68BAFF0086C96F /* DataStructureTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = DataStructureTests.xctestplan; sourceTree = "<group>"; };
|
A81A8CD32D68BAFF0086C96F /* DataStructureTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = DataStructureTests.xctestplan; sourceTree = "<group>"; };
|
||||||
A81BF9E12D84C9E900768940 /* AltStore 17_1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 17_1.xcdatamodel"; sourceTree = "<group>"; };
|
|
||||||
A81BF9E22D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppPermission17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A81BF9E32D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Source17To17_1MigrationPolicy.swift; sourceTree = "<group>"; };
|
|
||||||
A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = SideStore/AltSign/Dependencies/OpenSSL/Frameworks/OpenSSL.xcframework; sourceTree = "<group>"; };
|
A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = SideStore/AltSign/Dependencies/OpenSSL/Frameworks/OpenSSL.xcframework; sourceTree = "<group>"; };
|
||||||
A85ACB8E2D1F31C400AA3DE7 /* AltBackup.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltBackup.xcconfig; sourceTree = "<group>"; };
|
A85ACB8E2D1F31C400AA3DE7 /* AltBackup.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltBackup.xcconfig; sourceTree = "<group>"; };
|
||||||
A85ACB8F2D1F31C400AA3DE7 /* AltStore.debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStore.debug.xcconfig; sourceTree = "<group>"; };
|
A85ACB8F2D1F31C400AA3DE7 /* AltStore.debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStore.debug.xcconfig; sourceTree = "<group>"; };
|
||||||
@@ -702,8 +676,8 @@
|
|||||||
A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorProcessing.swift; sourceTree = "<group>"; };
|
A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorProcessing.swift; sourceTree = "<group>"; };
|
||||||
A868CFE32D319988002F1201 /* SingletonGenericMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingletonGenericMap.swift; sourceTree = "<group>"; };
|
A868CFE32D319988002F1201 /* SingletonGenericMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingletonGenericMap.swift; sourceTree = "<group>"; };
|
||||||
A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAppExtensionsOperation.swift; sourceTree = "<group>"; };
|
A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAppExtensionsOperation.swift; sourceTree = "<group>"; };
|
||||||
|
A881E7C62D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore11ToAltStore17.xcmappingmodel; sourceTree = "<group>"; };
|
||||||
A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp11To17MigrationPolicy.swift; sourceTree = "<group>"; };
|
A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreApp11To17MigrationPolicy.swift; sourceTree = "<group>"; };
|
||||||
A881E8562D6FBBAF00954AD2 /* DataStructureTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DataStructureTests.xcconfig; sourceTree = "<group>"; };
|
|
||||||
A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingContolView.swift; sourceTree = "<group>"; };
|
A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingContolView.swift; sourceTree = "<group>"; };
|
||||||
A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingControl.swift; sourceTree = "<group>"; };
|
A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingControl.swift; sourceTree = "<group>"; };
|
||||||
A8945AA52D059B6100D86CBE /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
A8945AA52D059B6100D86CBE /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -711,14 +685,12 @@
|
|||||||
A8AD35582D31BF29003A28B4 /* PageInfoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageInfoManager.swift; sourceTree = "<group>"; };
|
A8AD35582D31BF29003A28B4 /* PageInfoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageInfoManager.swift; sourceTree = "<group>"; };
|
||||||
A8B516E22D2666CA0047047C /* CoreDataHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataHelper.swift; sourceTree = "<group>"; };
|
A8B516E22D2666CA0047047C /* CoreDataHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataHelper.swift; sourceTree = "<group>"; };
|
||||||
A8B516E52D2668020047047C /* DateTimeUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeUtil.swift; sourceTree = "<group>"; };
|
A8B516E52D2668020047047C /* DateTimeUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeUtil.swift; sourceTree = "<group>"; };
|
||||||
A8B645FB2D70C10300125819 /* CollapsingMarkdownView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsingMarkdownView.swift; sourceTree = "<group>"; };
|
|
||||||
A8C38C1D2D206A3A00E83DBD /* ConsoleLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = "<group>"; };
|
A8C38C1D2D206A3A00E83DBD /* ConsoleLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLogger.swift; sourceTree = "<group>"; };
|
||||||
A8C38C1E2D206A3A00E83DBD /* ConsoleLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLog.swift; sourceTree = "<group>"; };
|
A8C38C1E2D206A3A00E83DBD /* ConsoleLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLog.swift; sourceTree = "<group>"; };
|
||||||
A8C38C282D206AC100E83DBD /* OutputStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputStream.swift; sourceTree = "<group>"; };
|
A8C38C282D206AC100E83DBD /* OutputStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutputStream.swift; sourceTree = "<group>"; };
|
||||||
A8C38C2B2D206AD900E83DBD /* AbstractClassError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractClassError.swift; sourceTree = "<group>"; };
|
A8C38C2B2D206AD900E83DBD /* AbstractClassError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbstractClassError.swift; sourceTree = "<group>"; };
|
||||||
A8C38C312D206B2500E83DBD /* FileOutputStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileOutputStream.swift; sourceTree = "<group>"; };
|
A8C38C312D206B2500E83DBD /* FileOutputStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileOutputStream.swift; sourceTree = "<group>"; };
|
||||||
A8C38C372D2084D000E83DBD /* ConsoleLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLogView.swift; sourceTree = "<group>"; };
|
A8C38C372D2084D000E83DBD /* ConsoleLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLogView.swift; sourceTree = "<group>"; };
|
||||||
A8C924232D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = AltStore11ToAltStore17.xcmappingmodel; sourceTree = "<group>"; };
|
|
||||||
A8D484D72D0CD306002C691D /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; path = AltBackup.ipa; sourceTree = "<group>"; };
|
A8D484D72D0CD306002C691D /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; path = AltBackup.ipa; sourceTree = "<group>"; };
|
||||||
A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+AltStore.swift"; sourceTree = "<group>"; };
|
A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+AltStore.swift"; sourceTree = "<group>"; };
|
||||||
A8E2DB212D684CBD009E5D31 /* UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
A8E2DB212D684CBD009E5D31 /* UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -1164,7 +1136,6 @@
|
|||||||
files = (
|
files = (
|
||||||
A8C6D5172D1EE95B00DF01F1 /* OpenSSL.xcframework in Frameworks */,
|
A8C6D5172D1EE95B00DF01F1 /* OpenSSL.xcframework in Frameworks */,
|
||||||
A8A543302D04F14400D72399 /* libfragmentzip.a in Frameworks */,
|
A8A543302D04F14400D72399 /* libfragmentzip.a in Frameworks */,
|
||||||
A8B646012D70C23E00125819 /* MarkdownKit in Frameworks */,
|
|
||||||
A805C3CD2D0C316A00E76BDD /* Pods_SideStore.framework in Frameworks */,
|
A805C3CD2D0C316A00E76BDD /* Pods_SideStore.framework in Frameworks */,
|
||||||
A8F838942D048ECE00ED425D /* libimobiledevice.a in Frameworks */,
|
A8F838942D048ECE00ED425D /* libimobiledevice.a in Frameworks */,
|
||||||
A8F838922D048E8F00ED425D /* libEmotionalDamage.a in Frameworks */,
|
A8F838922D048E8F00ED425D /* libEmotionalDamage.a in Frameworks */,
|
||||||
@@ -1172,7 +1143,6 @@
|
|||||||
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */,
|
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */,
|
||||||
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */,
|
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */,
|
||||||
BF1614F1250822F100767AEA /* Roxas.framework in Frameworks */,
|
BF1614F1250822F100767AEA /* Roxas.framework in Frameworks */,
|
||||||
A8B645FF2D70C1AD00125819 /* MarkdownKit in Frameworks */,
|
|
||||||
BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */,
|
BF66EE852501AE50007EE018 /* AltStoreCore.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1269,17 +1239,6 @@
|
|||||||
path = Intents;
|
path = Intents;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
A815AAA62D9108CB00929A9E /* v11-to-v17_1 */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A815AAA22D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift */,
|
|
||||||
A815AAA32D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift */,
|
|
||||||
A815AAA42D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift */,
|
|
||||||
A815AAA52D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift */,
|
|
||||||
);
|
|
||||||
path = "v11-to-v17_1";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A81A8CB22D68B2030086C96F /* UnitTests */ = {
|
A81A8CB22D68B2030086C96F /* UnitTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1310,31 +1269,10 @@
|
|||||||
A85ACB8E2D1F31C400AA3DE7 /* AltBackup.xcconfig */,
|
A85ACB8E2D1F31C400AA3DE7 /* AltBackup.xcconfig */,
|
||||||
A85ACB932D1F31C400AA3DE7 /* AltWidgetExtension.xcconfig */,
|
A85ACB932D1F31C400AA3DE7 /* AltWidgetExtension.xcconfig */,
|
||||||
A8E2DB2C2D684D39009E5D31 /* UITests.xcconfig */,
|
A8E2DB2C2D684D39009E5D31 /* UITests.xcconfig */,
|
||||||
A881E8562D6FBBAF00954AD2 /* DataStructureTests.xcconfig */,
|
|
||||||
);
|
);
|
||||||
path = xcconfigs;
|
path = xcconfigs;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
A85E43732D8AF82100E89240 /* v11-to-v17 */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
D5185B812AE1E71D00646E33 /* Source11To17MigrationPolicy.swift */,
|
|
||||||
A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */,
|
|
||||||
);
|
|
||||||
path = "v11-to-v17";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A85E43742D8AF84200E89240 /* v17-to-v17_1 */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A81BF9E32D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift */,
|
|
||||||
A815AA942D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift */,
|
|
||||||
A815AA962D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift */,
|
|
||||||
A81BF9E22D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift */,
|
|
||||||
);
|
|
||||||
path = "v17-to-v17_1";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A86315DD2D3EB2BD0048FA40 /* errors */ = {
|
A86315DD2D3EB2BD0048FA40 /* errors */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1406,22 +1344,6 @@
|
|||||||
path = database;
|
path = database;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
A8B645F82D70C0DD00125819 /* Views */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A8B645FA2D70C0F600125819 /* UIKit */,
|
|
||||||
);
|
|
||||||
path = Views;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A8B645FA2D70C0F600125819 /* UIKit */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A8B645FB2D70C10300125819 /* CollapsingMarkdownView.swift */,
|
|
||||||
);
|
|
||||||
path = UIKit;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A8C38C1C2D2068D100E83DBD /* Utils */ = {
|
A8C38C1C2D2068D100E83DBD /* Utils */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1447,7 +1369,6 @@
|
|||||||
A8C38C272D206AA500E83DBD /* common */ = {
|
A8C38C272D206AA500E83DBD /* common */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A815AA9E2D9104DD00929A9E /* BuildInfo.swift */,
|
|
||||||
A8B516E52D2668020047047C /* DateTimeUtil.swift */,
|
A8B516E52D2668020047047C /* DateTimeUtil.swift */,
|
||||||
A8C38C282D206AC100E83DBD /* OutputStream.swift */,
|
A8C38C282D206AC100E83DBD /* OutputStream.swift */,
|
||||||
A8C38C312D206B2500E83DBD /* FileOutputStream.swift */,
|
A8C38C312D206B2500E83DBD /* FileOutputStream.swift */,
|
||||||
@@ -1504,7 +1425,6 @@
|
|||||||
A8F66C072D04C025009689E6 /* SideStore */ = {
|
A8F66C072D04C025009689E6 /* SideStore */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A8B645F82D70C0DD00125819 /* Views */,
|
|
||||||
A8E2DB352D6850A9009E5D31 /* Tests */,
|
A8E2DB352D6850A9009E5D31 /* Tests */,
|
||||||
A8F66C5C2D04D433009689E6 /* minimuxer */,
|
A8F66C5C2D04D433009689E6 /* minimuxer */,
|
||||||
A8F66C602D04D464009689E6 /* minimuxer.xcodeproj */,
|
A8F66C602D04D464009689E6 /* minimuxer.xcodeproj */,
|
||||||
@@ -1876,11 +1796,10 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF66EEAF2501AECA007EE018 /* InstalledAppPolicy.swift */,
|
BF66EEAF2501AECA007EE018 /* InstalledAppPolicy.swift */,
|
||||||
|
D5185B812AE1E71D00646E33 /* Source11To17MigrationPolicy.swift */,
|
||||||
BF66EEAE2501AECA007EE018 /* StoreAppPolicy.swift */,
|
BF66EEAE2501AECA007EE018 /* StoreAppPolicy.swift */,
|
||||||
D5F99A1928D12B1400476A16 /* StoreApp10ToStoreApp11Policy.swift */,
|
D5F99A1928D12B1400476A16 /* StoreApp10ToStoreApp11Policy.swift */,
|
||||||
A85E43732D8AF82100E89240 /* v11-to-v17 */,
|
A881E7CA2D6EF5AB00954AD2 /* StoreApp11To17MigrationPolicy.swift */,
|
||||||
A85E43742D8AF84200E89240 /* v17-to-v17_1 */,
|
|
||||||
A815AAA62D9108CB00929A9E /* v11-to-v17_1 */,
|
|
||||||
);
|
);
|
||||||
path = Policies;
|
path = Policies;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1888,9 +1807,7 @@
|
|||||||
BF66EEB02501AECA007EE018 /* Mapping Models */ = {
|
BF66EEB02501AECA007EE018 /* Mapping Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
A815AAA02D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel */,
|
A881E7C62D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel */,
|
||||||
A815AA982D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel */,
|
|
||||||
A8C924232D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel */,
|
|
||||||
D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */,
|
D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */,
|
||||||
D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */,
|
D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */,
|
||||||
BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */,
|
BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */,
|
||||||
@@ -2763,7 +2680,6 @@
|
|||||||
D58D5F2C26DFE68E00E55E38 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */,
|
D58D5F2C26DFE68E00E55E38 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */,
|
||||||
D5FB7A2C2AA2859400EF863D /* XCRemoteSwiftPackageReference "swift-argument-parser" */,
|
D5FB7A2C2AA2859400EF863D /* XCRemoteSwiftPackageReference "swift-argument-parser" */,
|
||||||
A82067C22D03E0DE00645C0D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
|
A82067C22D03E0DE00645C0D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
|
||||||
A8B645FD2D70C1AD00125819 /* XCRemoteSwiftPackageReference "MarkdownKit" */,
|
|
||||||
);
|
);
|
||||||
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -3156,7 +3072,6 @@
|
|||||||
A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */,
|
A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */,
|
||||||
D5FB28EE2ADDF89800A1C337 /* KnownSource.swift in Sources */,
|
D5FB28EE2ADDF89800A1C337 /* KnownSource.swift in Sources */,
|
||||||
BF66EED32501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel in Sources */,
|
BF66EED32501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel in Sources */,
|
||||||
A815AA972D90E16400929A9E /* ReleaseTrack17To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
BF66EEA52501AEC5007EE018 /* Benefit.swift in Sources */,
|
BF66EEA52501AEC5007EE018 /* Benefit.swift in Sources */,
|
||||||
D52B4ABF2AF183F0005991C3 /* WebViewController.swift in Sources */,
|
D52B4ABF2AF183F0005991C3 /* WebViewController.swift in Sources */,
|
||||||
BF66EED22501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel in Sources */,
|
BF66EED22501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel in Sources */,
|
||||||
@@ -3165,26 +3080,17 @@
|
|||||||
BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */,
|
BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */,
|
||||||
BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */,
|
BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */,
|
||||||
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */,
|
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */,
|
||||||
A8C924242D8E73B1009D9953 /* AltStore11ToAltStore17.xcmappingmodel in Sources */,
|
|
||||||
D552EB062AF453F900A3AB4D /* URL+Normalized.swift in Sources */,
|
D552EB062AF453F900A3AB4D /* URL+Normalized.swift in Sources */,
|
||||||
BFAECC522501B0A400528F27 /* CodableError.swift in Sources */,
|
BFAECC522501B0A400528F27 /* CodableError.swift in Sources */,
|
||||||
A8FD915F2D046F5200322782 /* UserInfoValue.swift in Sources */,
|
A8FD915F2D046F5200322782 /* UserInfoValue.swift in Sources */,
|
||||||
D5F9821D2AB900060045751F /* AppScreenshot.swift in Sources */,
|
D5F9821D2AB900060045751F /* AppScreenshot.swift in Sources */,
|
||||||
BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */,
|
BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */,
|
||||||
BF66EEDF2501AECA007EE018 /* PatreonAccount.swift in Sources */,
|
BF66EEDF2501AECA007EE018 /* PatreonAccount.swift in Sources */,
|
||||||
A815AAA12D9108BC00929A9E /* AltStore11ToAltStore17_1.xcmappingmodel in Sources */,
|
|
||||||
BFAECC532501B0A400528F27 /* ServerProtocol.swift in Sources */,
|
BFAECC532501B0A400528F27 /* ServerProtocol.swift in Sources */,
|
||||||
A81BF9E52D84CB0C00768940 /* AppPermission17To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
A81BF9E72D84CB0C00768940 /* Source17To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
BFAECC572501B0A400528F27 /* ConnectionManager.swift in Sources */,
|
BFAECC572501B0A400528F27 /* ConnectionManager.swift in Sources */,
|
||||||
BF66EE9D2501AEC1007EE018 /* AppProtocol.swift in Sources */,
|
BF66EE9D2501AEC1007EE018 /* AppProtocol.swift in Sources */,
|
||||||
D519AD46292D665B004B12F9 /* Managed.swift in Sources */,
|
D519AD46292D665B004B12F9 /* Managed.swift in Sources */,
|
||||||
D52A2F972ACB40F700BDF8E3 /* Logger+AltStore.swift in Sources */,
|
D52A2F972ACB40F700BDF8E3 /* Logger+AltStore.swift in Sources */,
|
||||||
A815AA952D90D2A100929A9E /* StoreApp17To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
A815AAA72D9108CB00929A9E /* ReleaseTrack11To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
A815AAA82D9108CB00929A9E /* AppPermission11To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
A815AAA92D9108CB00929A9E /* Source11To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
A815AAAA2D9108CB00929A9E /* StoreApp11To17_1MigrationPolicy.swift in Sources */,
|
|
||||||
BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */,
|
BFC712C42512D5F100AB5EBE /* XPCConnection.swift in Sources */,
|
||||||
D5CA0C4B280E141900469595 /* ManagedPatron.swift in Sources */,
|
D5CA0C4B280E141900469595 /* ManagedPatron.swift in Sources */,
|
||||||
BF66EE8C2501AEB2007EE018 /* Keychain.swift in Sources */,
|
BF66EE8C2501AEB2007EE018 /* Keychain.swift in Sources */,
|
||||||
@@ -3247,11 +3153,11 @@
|
|||||||
BF66EECE2501AECA007EE018 /* InstalledAppPolicy.swift in Sources */,
|
BF66EECE2501AECA007EE018 /* InstalledAppPolicy.swift in Sources */,
|
||||||
BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */,
|
BF1FE359251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift in Sources */,
|
||||||
BF66EEA62501AEC5007EE018 /* PatreonAPI.swift in Sources */,
|
BF66EEA62501AEC5007EE018 /* PatreonAPI.swift in Sources */,
|
||||||
|
A881E7C72D6EF58C00954AD2 /* AltStore11ToAltStore17.xcmappingmodel in Sources */,
|
||||||
BF66EED02501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel in Sources */,
|
BF66EED02501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel in Sources */,
|
||||||
BF66EEDC2501AECA007EE018 /* MergePolicy.swift in Sources */,
|
BF66EEDC2501AECA007EE018 /* MergePolicy.swift in Sources */,
|
||||||
BF66EEE22501AECA007EE018 /* InstalledExtension.swift in Sources */,
|
BF66EEE22501AECA007EE018 /* InstalledExtension.swift in Sources */,
|
||||||
BF66EED62501AECA007EE018 /* NewsItem.swift in Sources */,
|
BF66EED62501AECA007EE018 /* NewsItem.swift in Sources */,
|
||||||
A815AA992D90E5E500929A9E /* AltStore17ToAltStore17_1.xcmappingmodel in Sources */,
|
|
||||||
BF66EEA72501AEC5007EE018 /* Campaign.swift in Sources */,
|
BF66EEA72501AEC5007EE018 /* Campaign.swift in Sources */,
|
||||||
D52C8F032AFC56F000CA0BDD /* StoreCategory.swift in Sources */,
|
D52C8F032AFC56F000CA0BDD /* StoreCategory.swift in Sources */,
|
||||||
BF66EE992501AEBC007EE018 /* ALTSourceUserInfoKey.m in Sources */,
|
BF66EE992501AEBC007EE018 /* ALTSourceUserInfoKey.m in Sources */,
|
||||||
@@ -3384,7 +3290,6 @@
|
|||||||
D57FE84428C7DB7100216002 /* ErrorLogViewController.swift in Sources */,
|
D57FE84428C7DB7100216002 /* ErrorLogViewController.swift in Sources */,
|
||||||
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */,
|
A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */,
|
||||||
D50107EC2ADF2E1A0069F2A1 /* AddSourceTextFieldCell.swift in Sources */,
|
D50107EC2ADF2E1A0069F2A1 /* AddSourceTextFieldCell.swift in Sources */,
|
||||||
A8B645FC2D70C10300125819 /* CollapsingMarkdownView.swift in Sources */,
|
|
||||||
D5151BD92A8FF64300C96F28 /* RefreshAllAppsIntent.swift in Sources */,
|
D5151BD92A8FF64300C96F28 /* RefreshAllAppsIntent.swift in Sources */,
|
||||||
BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */,
|
BFBE0007250AD0E70080826E /* ViewAppIntentHandler.swift in Sources */,
|
||||||
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */,
|
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */,
|
||||||
@@ -3400,7 +3305,6 @@
|
|||||||
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
||||||
BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */,
|
BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */,
|
||||||
0EE7FDC42BE8BC7900D1E390 /* ALTLocalizedError.swift in Sources */,
|
0EE7FDC42BE8BC7900D1E390 /* ALTLocalizedError.swift in Sources */,
|
||||||
A815AA9F2D9104DD00929A9E /* BuildInfo.swift in Sources */,
|
|
||||||
BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */,
|
BFF00D342501BDCF00746320 /* IntentHandler.swift in Sources */,
|
||||||
BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */,
|
BFDBBD80246CB84F004ED2F3 /* RemoveAppBackupOperation.swift in Sources */,
|
||||||
A8C38C2A2D206AC100E83DBD /* OutputStream.swift in Sources */,
|
A8C38C2A2D206AC100E83DBD /* OutputStream.swift in Sources */,
|
||||||
@@ -3628,7 +3532,6 @@
|
|||||||
};
|
};
|
||||||
A81A8CCA2D68BA610086C96F /* Debug */ = {
|
A81A8CCA2D68BA610086C96F /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = A881E8562D6FBBAF00954AD2 /* DataStructureTests.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
@@ -3643,7 +3546,7 @@
|
|||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
PRODUCT_BUNDLE_IDENTIFIER = com.SideStore.SideStore.DataStructuresTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
@@ -3654,7 +3557,6 @@
|
|||||||
};
|
};
|
||||||
A81A8CCB2D68BA610086C96F /* Release */ = {
|
A81A8CCB2D68BA610086C96F /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = A881E8562D6FBBAF00954AD2 /* DataStructureTests.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
@@ -3671,7 +3573,7 @@
|
|||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
PRODUCT_BUNDLE_IDENTIFIER = com.SideStore.SideStore.DataStructuresTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
@@ -3698,7 +3600,7 @@
|
|||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
PRODUCT_BUNDLE_IDENTIFIER = "--PRODUCT-BUNDLE-IDENTIFIER-.UITests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
@@ -3716,7 +3618,6 @@
|
|||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
@@ -3725,7 +3626,7 @@
|
|||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
PRODUCT_BUNDLE_IDENTIFIER = "--PRODUCT-BUNDLE-IDENTIFIER-.UITests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
@@ -4353,14 +4254,6 @@
|
|||||||
minimumVersion = 0.4.0;
|
minimumVersion = 0.4.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
A8B645FD2D70C1AD00125819 /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/bmoliveira/MarkdownKit.git";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMajorVersion;
|
|
||||||
minimumVersion = 1.7.1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
D58D5F2C26DFE68E00E55E38 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = {
|
D58D5F2C26DFE68E00E55E38 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin.git";
|
repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin.git";
|
||||||
@@ -4385,16 +4278,6 @@
|
|||||||
package = A82067C22D03E0DE00645C0D /* XCRemoteSwiftPackageReference "SemanticVersion" */;
|
package = A82067C22D03E0DE00645C0D /* XCRemoteSwiftPackageReference "SemanticVersion" */;
|
||||||
productName = SemanticVersion;
|
productName = SemanticVersion;
|
||||||
};
|
};
|
||||||
A8B645FE2D70C1AD00125819 /* MarkdownKit */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = A8B645FD2D70C1AD00125819 /* XCRemoteSwiftPackageReference "MarkdownKit" */;
|
|
||||||
productName = MarkdownKit;
|
|
||||||
};
|
|
||||||
A8B646002D70C23E00125819 /* MarkdownKit */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = A8B645FD2D70C1AD00125819 /* XCRemoteSwiftPackageReference "MarkdownKit" */;
|
|
||||||
productName = MarkdownKit;
|
|
||||||
};
|
|
||||||
A8C6D50B2D1EE87600DF01F1 /* AltSign-Static */ = {
|
A8C6D50B2D1EE87600DF01F1 /* AltSign-Static */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = "AltSign-Static";
|
productName = "AltSign-Static";
|
||||||
@@ -4409,7 +4292,6 @@
|
|||||||
BF66EEB72501AECA007EE018 /* AltStore.xcdatamodeld */ = {
|
BF66EEB72501AECA007EE018 /* AltStore.xcdatamodeld */ = {
|
||||||
isa = XCVersionGroup;
|
isa = XCVersionGroup;
|
||||||
children = (
|
children = (
|
||||||
A81BF9E12D84C9E900768940 /* AltStore 17_1.xcdatamodel */,
|
|
||||||
D51E83802B86926B0092FC61 /* AltStore 17.xcdatamodel */,
|
D51E83802B86926B0092FC61 /* AltStore 17.xcdatamodel */,
|
||||||
D52E988928D002D30032BE6B /* AltStore 11.xcdatamodel */,
|
D52E988928D002D30032BE6B /* AltStore 11.xcdatamodel */,
|
||||||
D5CA0C4C280E242500469595 /* AltStore 10.xcdatamodel */,
|
D5CA0C4C280E242500469595 /* AltStore 10.xcdatamodel */,
|
||||||
@@ -4423,7 +4305,7 @@
|
|||||||
BF66EEBD2501AECA007EE018 /* AltStore 2.xcdatamodel */,
|
BF66EEBD2501AECA007EE018 /* AltStore 2.xcdatamodel */,
|
||||||
BF66EEB92501AECA007EE018 /* AltStore.xcdatamodel */,
|
BF66EEB92501AECA007EE018 /* AltStore.xcdatamodel */,
|
||||||
);
|
);
|
||||||
currentVersion = A81BF9E12D84C9E900768940 /* AltStore 17_1.xcdatamodel */;
|
currentVersion = D51E83802B86926B0092FC61 /* AltStore 17.xcdatamodel */;
|
||||||
path = AltStore.xcdatamodeld;
|
path = AltStore.xcdatamodeld;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
versionGroupType = wrapper.xcdatamodel;
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
|||||||
@@ -43,10 +43,8 @@ final class AppContentViewController: UITableViewController
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
@IBOutlet private var subtitleLabel: UILabel!
|
@IBOutlet private var subtitleLabel: UILabel!
|
||||||
// @IBOutlet private var descriptionTextView: CollapsingTextView!
|
@IBOutlet private var descriptionTextView: CollapsingTextView!
|
||||||
@IBOutlet private var descriptionTextView: CollapsingMarkdownView!
|
@IBOutlet private var versionDescriptionTextView: CollapsingTextView!
|
||||||
// @IBOutlet private var versionDescriptionTextView: CollapsingTextView!
|
|
||||||
@IBOutlet private var versionDescriptionTextView: CollapsingMarkdownView!
|
|
||||||
@IBOutlet private var versionLabel: UILabel!
|
@IBOutlet private var versionLabel: UILabel!
|
||||||
@IBOutlet private var versionDateLabel: UILabel!
|
@IBOutlet private var versionDateLabel: UILabel!
|
||||||
@IBOutlet private var sizeLabel: UILabel!
|
@IBOutlet private var sizeLabel: UILabel!
|
||||||
@@ -57,32 +55,35 @@ final class AppContentViewController: UITableViewController
|
|||||||
@IBOutlet private(set) var appDetailCollectionViewController: AppDetailCollectionViewController!
|
@IBOutlet private(set) var appDetailCollectionViewController: AppDetailCollectionViewController!
|
||||||
@IBOutlet private var appDetailCollectionViewHeightConstraint: NSLayoutConstraint!
|
@IBOutlet private var appDetailCollectionViewHeightConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.tableView.contentInset.bottom = 20
|
self.tableView.contentInset.bottom = 20
|
||||||
|
|
||||||
self.subtitleLabel.text = self.app.subtitle
|
self.subtitleLabel.text = self.app.subtitle
|
||||||
let desc = self.app.localizedDescription
|
self.descriptionTextView.text = self.app.localizedDescription
|
||||||
self.descriptionTextView.text = desc
|
|
||||||
|
|
||||||
if let version = self.app.latestAvailableVersion {
|
if let version = self.app.latestAvailableVersion
|
||||||
self.versionDescriptionTextView.text = version.localizedDescription ?? "nil"
|
{
|
||||||
|
self.versionDescriptionTextView.text = version.localizedDescription
|
||||||
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.localizedVersion)
|
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.localizedVersion)
|
||||||
self.versionDateLabel.text = Date().relativeDateString(since: version.date)
|
self.versionDateLabel.text = Date().relativeDateString(since: version.date)
|
||||||
self.sizeLabel.text = ByteCountFormatter.string(fromByteCount: version.size, countStyle: .file)
|
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: version.size)
|
||||||
} else {
|
}
|
||||||
self.versionDescriptionTextView.text = "nil"
|
else
|
||||||
|
{
|
||||||
|
self.versionDescriptionTextView.text = nil
|
||||||
self.versionLabel.text = nil
|
self.versionLabel.text = nil
|
||||||
self.versionDateLabel.text = nil
|
self.versionDateLabel.text = nil
|
||||||
self.sizeLabel.text = ByteCountFormatter.string(fromByteCount: 0, countStyle: .file)
|
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.descriptionTextView.maximumNumberOfLines = 5
|
self.descriptionTextView.maximumNumberOfLines = 5
|
||||||
self.versionDescriptionTextView.maximumNumberOfLines = 5
|
self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
|
||||||
|
|
||||||
self.descriptionTextView.toggleButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
|
self.versionDescriptionTextView.maximumNumberOfLines = 3
|
||||||
self.versionDescriptionTextView.toggleButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
|
self.versionDescriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLayoutSubviews()
|
override func viewDidLayoutSubviews()
|
||||||
@@ -161,12 +162,8 @@ private extension AppContentViewController
|
|||||||
|
|
||||||
switch sender
|
switch sender
|
||||||
{
|
{
|
||||||
case self.descriptionTextView.toggleButton:
|
case self.descriptionTextView.moreButton: indexPath = IndexPath(row: Row.description.rawValue, section: 0)
|
||||||
indexPath = IndexPath(row: Row.description.rawValue, section: 0)
|
case self.versionDescriptionTextView.moreButton: indexPath = IndexPath(row: Row.versionDescription.rawValue, section: 0)
|
||||||
|
|
||||||
case self.versionDescriptionTextView.toggleButton:
|
|
||||||
indexPath = IndexPath(row: Row.versionDescription.rawValue, section: 0)
|
|
||||||
|
|
||||||
default: return
|
default: return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -287,7 +287,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="98"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="98"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Hello Me" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Pyt-8D-BZA" customClass="CollapsingMarkdownView" customModule="SideStore" customModuleProvider="target">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Hello Me" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="Pyt-8D-BZA" customClass="CollapsingTextView" customModule="SideStore" customModuleProvider="target">
|
||||||
<rect key="frame" x="20" y="20" width="335" height="34"/>
|
<rect key="frame" x="20" y="20" width="335" height="34"/>
|
||||||
<color key="backgroundColor" name="Background"/>
|
<color key="backgroundColor" name="Background"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||||
@@ -353,7 +353,7 @@
|
|||||||
</stackView>
|
</stackView>
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingMarkdownView" customModule="SideStore" customModuleProvider="target">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="wQF-WY-Gdz" customClass="CollapsingTextView" customModule="SideStore" customModuleProvider="target">
|
||||||
<rect key="frame" x="20" y="59.5" width="335" height="34"/>
|
<rect key="frame" x="20" y="59.5" width="335" height="34"/>
|
||||||
<color key="backgroundColor" name="Background"/>
|
<color key="backgroundColor" name="Background"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||||
|
|||||||
@@ -13,18 +13,21 @@ final class CollapsingTextView: UITextView
|
|||||||
var isCollapsed = true {
|
var isCollapsed = true {
|
||||||
didSet {
|
didSet {
|
||||||
guard self.isCollapsed != oldValue else { return }
|
guard self.isCollapsed != oldValue else { return }
|
||||||
|
self.shouldResetLayout = true
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var maximumNumberOfLines = 2 {
|
var maximumNumberOfLines = 2 {
|
||||||
didSet {
|
didSet {
|
||||||
|
self.shouldResetLayout = true
|
||||||
self.setNeedsLayout()
|
self.setNeedsLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var lineSpacing: Double = 2 {
|
var lineSpacing: Double = 2 {
|
||||||
didSet {
|
didSet {
|
||||||
|
self.shouldResetLayout = true
|
||||||
|
|
||||||
if #available(iOS 16, *)
|
if #available(iOS 16, *)
|
||||||
{
|
{
|
||||||
@@ -39,6 +42,7 @@ final class CollapsingTextView: UITextView
|
|||||||
|
|
||||||
override var text: String! {
|
override var text: String! {
|
||||||
didSet {
|
didSet {
|
||||||
|
self.shouldResetLayout = true
|
||||||
|
|
||||||
guard #available(iOS 16, *) else { return }
|
guard #available(iOS 16, *) else { return }
|
||||||
self.updateText()
|
self.updateText()
|
||||||
@@ -47,6 +51,9 @@ final class CollapsingTextView: UITextView
|
|||||||
|
|
||||||
let moreButton = UIButton(type: .system)
|
let moreButton = UIButton(type: .system)
|
||||||
|
|
||||||
|
private var shouldResetLayout: Bool = false
|
||||||
|
private var previousSize: CGSize?
|
||||||
|
|
||||||
override init(frame: CGRect, textContainer: NSTextContainer?)
|
override init(frame: CGRect, textContainer: NSTextContainer?)
|
||||||
{
|
{
|
||||||
super.init(frame: frame, textContainer: textContainer)
|
super.init(frame: frame, textContainer: textContainer)
|
||||||
@@ -108,6 +115,8 @@ final class CollapsingTextView: UITextView
|
|||||||
height: font.lineHeight)
|
height: font.lineHeight)
|
||||||
self.moreButton.frame = moreButtonFrame
|
self.moreButton.frame = moreButtonFrame
|
||||||
|
|
||||||
|
if self.shouldResetLayout || self.previousSize != self.bounds.size
|
||||||
|
{
|
||||||
if self.isCollapsed
|
if self.isCollapsed
|
||||||
{
|
{
|
||||||
let boundingSize = self.attributedText.boundingRect(with: CGSize(width: self.textContainer.size.width, height: .infinity), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
|
let boundingSize = self.attributedText.boundingRect(with: CGSize(width: self.textContainer.size.width, height: .infinity), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
|
||||||
@@ -142,6 +151,10 @@ final class CollapsingTextView: UITextView
|
|||||||
|
|
||||||
self.invalidateIntrinsicContentSize()
|
self.invalidateIntrinsicContentSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.shouldResetLayout = false
|
||||||
|
self.previousSize = self.bounds.size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension CollapsingTextView
|
private extension CollapsingTextView
|
||||||
|
|||||||
@@ -242,6 +242,7 @@
|
|||||||
<key>public.filename-extension</key>
|
<key>public.filename-extension</key>
|
||||||
<array>
|
<array>
|
||||||
<string>mobiledevicepairing</string>
|
<string>mobiledevicepairing</string>
|
||||||
|
<string>mobiledevicepair</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
|||||||
@@ -235,8 +235,7 @@ private extension MyAppsViewController
|
|||||||
cell.layoutMargins.right = self.view.layoutMargins.right
|
cell.layoutMargins.right = self.view.layoutMargins.right
|
||||||
|
|
||||||
cell.tintColor = app.tintColor ?? .altPrimary
|
cell.tintColor = app.tintColor ?? .altPrimary
|
||||||
cell.versionDescriptionTextView.maximumNumberOfLines = 2
|
cell.versionDescriptionTextView.text = latestSupportedVersion.localizedDescription
|
||||||
cell.versionDescriptionTextView.text = latestSupportedVersion.localizedDescription ?? "nil"
|
|
||||||
|
|
||||||
cell.bannerView.iconImageView.image = nil
|
cell.bannerView.iconImageView.image = nil
|
||||||
cell.bannerView.iconImageView.isIndicatingActivity = true
|
cell.bannerView.iconImageView.isIndicatingActivity = true
|
||||||
@@ -282,9 +281,12 @@ private extension MyAppsViewController
|
|||||||
cell.mode = .collapsed
|
cell.mode = .collapsed
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.versionDescriptionTextView.toggleButton.addTarget(self, action: #selector(MyAppsViewController.toggleUpdateCellMode(_:)), for: .primaryActionTriggered)
|
cell.versionDescriptionTextView.moreButton.addTarget(self, action: #selector(MyAppsViewController.toggleUpdateCellMode(_:)), for: .primaryActionTriggered)
|
||||||
|
|
||||||
cell.setNeedsLayout()
|
cell.setNeedsLayout()
|
||||||
|
|
||||||
|
// Below lines are necessary to avoid "more" button layout issues.
|
||||||
|
cell.versionDescriptionTextView.setNeedsLayout()
|
||||||
cell.layoutIfNeeded()
|
cell.layoutIfNeeded()
|
||||||
}
|
}
|
||||||
dataSource.prefetchHandler = { (installedApp, indexPath, completionHandler) in
|
dataSource.prefetchHandler = { (installedApp, indexPath, completionHandler) in
|
||||||
@@ -722,30 +724,23 @@ private extension MyAppsViewController
|
|||||||
|
|
||||||
let cell = self.collectionView.cellForItem(at: indexPath) as? UpdateCollectionViewCell
|
let cell = self.collectionView.cellForItem(at: indexPath) as? UpdateCollectionViewCell
|
||||||
|
|
||||||
// Toggle the state
|
|
||||||
if self.expandedAppUpdates.contains(installedApp.bundleIdentifier)
|
if self.expandedAppUpdates.contains(installedApp.bundleIdentifier)
|
||||||
{
|
{
|
||||||
self.expandedAppUpdates.remove(installedApp.bundleIdentifier)
|
self.expandedAppUpdates.remove(installedApp.bundleIdentifier)
|
||||||
// Set collapsed mode on the cell
|
|
||||||
cell?.mode = .collapsed
|
cell?.mode = .collapsed
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
self.expandedAppUpdates.insert(installedApp.bundleIdentifier)
|
self.expandedAppUpdates.insert(installedApp.bundleIdentifier)
|
||||||
// Set expanded mode on the cell
|
|
||||||
cell?.mode = .expanded
|
cell?.mode = .expanded
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cached size so it's recalculated
|
|
||||||
self.cachedUpdateSizes[installedApp.bundleIdentifier] = nil
|
self.cachedUpdateSizes[installedApp.bundleIdentifier] = nil
|
||||||
|
|
||||||
// Animate the change smoothly with a duration
|
|
||||||
UIView.animate(withDuration: 0.25) {
|
|
||||||
self.collectionView.performBatchUpdates({
|
self.collectionView.performBatchUpdates({
|
||||||
self.collectionView.collectionViewLayout.invalidateLayout()
|
self.collectionView.collectionViewLayout.invalidateLayout()
|
||||||
}, completion: nil)
|
}, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func refreshApp(_ sender: UIButton)
|
@IBAction func refreshApp(_ sender: UIButton)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,19 +21,12 @@ extension UpdateCollectionViewCell
|
|||||||
{
|
{
|
||||||
var mode: Mode = .expanded {
|
var mode: Mode = .expanded {
|
||||||
didSet {
|
didSet {
|
||||||
switch self.mode {
|
self.update()
|
||||||
case .collapsed:
|
|
||||||
self.versionDescriptionTextView.isCollapsed = true
|
|
||||||
case .expanded:
|
|
||||||
self.versionDescriptionTextView.isCollapsed = false
|
|
||||||
}
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet var bannerView: AppBannerView!
|
@IBOutlet var bannerView: AppBannerView!
|
||||||
// @IBOutlet var versionDescriptionTextView: CollapsingTextView!
|
@IBOutlet var versionDescriptionTextView: CollapsingTextView!
|
||||||
@IBOutlet var versionDescriptionTextView: CollapsingMarkdownView!
|
|
||||||
|
|
||||||
@IBOutlet private var blurView: UIVisualEffectView!
|
@IBOutlet private var blurView: UIVisualEffectView!
|
||||||
|
|
||||||
@@ -92,16 +85,16 @@ extension UpdateCollectionViewCell
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize
|
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize
|
||||||
// {
|
{
|
||||||
// // Ensure cell is laid out so it will report correct size.
|
// Ensure cell is laid out so it will report correct size.
|
||||||
// self.versionDescriptionTextView.setNeedsLayout()
|
self.versionDescriptionTextView.setNeedsLayout()
|
||||||
// self.versionDescriptionTextView.layoutIfNeeded()
|
self.versionDescriptionTextView.layoutIfNeeded()
|
||||||
//
|
|
||||||
// let size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
|
let size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
|
||||||
//
|
|
||||||
// return size
|
return size
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension UpdateCollectionViewCell
|
private extension UpdateCollectionViewCell
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="23504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23506"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="UpdateCell" id="Kqf-Pv-ca3" customClass="UpdateCollectionViewCell" customModule="SideStore" customModuleProvider="target">
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="UpdateCell" id="Kqf-Pv-ca3" customClass="UpdateCollectionViewCell" customModule="AltStore" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="125"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="125"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="uYl-PH-DuP">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="uYl-PH-DuP">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="343" height="125"/>
|
<rect key="frame" x="0.0" y="0.0" width="343" height="125"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Nop-pL-Icx" customClass="AppBannerView" customModule="SideStore" customModuleProvider="target">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Nop-pL-Icx" customClass="AppBannerView" customModule="AltStore" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="343" height="50"/>
|
<rect key="frame" x="0.0" y="0.0" width="343" height="50"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="88" id="EPP-7O-1Ad"/>
|
<constraint firstAttribute="height" constant="88" id="EPP-7O-1Ad"/>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="firstBaseline" translatesAutoresizingMaskIntoConstraints="NO" id="RSR-5W-7tt" userLabel="Release Notes">
|
<stackView opaque="NO" contentMode="scaleToFill" alignment="firstBaseline" translatesAutoresizingMaskIntoConstraints="NO" id="RSR-5W-7tt" userLabel="Release Notes">
|
||||||
<rect key="frame" x="0.0" y="50" width="343" height="75"/>
|
<rect key="frame" x="0.0" y="50" width="343" height="75"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Version Notes" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rNs-2O-k3V" customClass="CollapsingMarkdownView" customModule="SideStore" customModuleProvider="target">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Version Notes" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rNs-2O-k3V" customClass="CollapsingTextView" customModule="AltStore" customModuleProvider="target">
|
||||||
<rect key="frame" x="15" y="0.0" width="313" height="26"/>
|
<rect key="frame" x="15" y="0.0" width="313" height="26"/>
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
<color red="1" green="1" blue="1" alpha="0.30000001192092896" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="1" green="1" blue="1" alpha="0.30000001192092896" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</namedColor>
|
</namedColor>
|
||||||
<systemColor name="secondaryLabelColor">
|
<systemColor name="secondaryLabelColor">
|
||||||
<color red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</systemColor>
|
</systemColor>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@@ -360,6 +360,13 @@ extension FetchProvisioningProfilesOperation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FetchProvisioningProfilesRefreshOperation: FetchProvisioningProfilesOperation, @unchecked Sendable {
|
||||||
|
override init(context: AppOperationContext)
|
||||||
|
{
|
||||||
|
super.init(context: context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class FetchProvisioningProfilesInstallOperation: FetchProvisioningProfilesOperation, @unchecked Sendable{
|
class FetchProvisioningProfilesInstallOperation: FetchProvisioningProfilesOperation, @unchecked Sendable{
|
||||||
override init(context: AppOperationContext)
|
override init(context: AppOperationContext)
|
||||||
{
|
{
|
||||||
@@ -606,14 +613,3 @@ class FetchProvisioningProfilesInstallOperation: FetchProvisioningProfilesOperat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// <TEST> : users were reporting that refresh (though seemed like it refreshed the app becomes no longer available)
|
|
||||||
// possibly, this is caused since refesh was not updating appFeatures and AppGroups in the new profile? not sure.
|
|
||||||
// for now we are reverting by keeping same operation that happens during fetch in install path to see if it fixes issue #893
|
|
||||||
// class FetchProvisioningProfilesRefreshOperation: FetchProvisioningProfilesOperation, @unchecked Sendable {
|
|
||||||
class FetchProvisioningProfilesRefreshOperation: FetchProvisioningProfilesInstallOperation, @unchecked Sendable {
|
|
||||||
override init(context: AppOperationContext)
|
|
||||||
{
|
|
||||||
super.init(context: context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import CoreData
|
|||||||
|
|
||||||
import AltStoreCore
|
import AltStoreCore
|
||||||
import Roxas
|
import Roxas
|
||||||
import SemanticVersion
|
|
||||||
|
|
||||||
@objc(FetchSourceOperation)
|
@objc(FetchSourceOperation)
|
||||||
final class FetchSourceOperation: ResultOperation<Source>
|
final class FetchSourceOperation: ResultOperation<Source>
|
||||||
@@ -247,20 +246,9 @@ private extension FetchSourceOperation
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
let incomingSourceID = source.identifier
|
if let previousSourceID = self.$source.identifier
|
||||||
if let previousSourceID = self.$source.identifier,
|
|
||||||
incomingSourceID != previousSourceID
|
|
||||||
{
|
{
|
||||||
// if let version = BuildInfo().marketing_version,
|
guard source.identifier == previousSourceID else { throw SourceError.changedID(source.identifier, previousID: previousSourceID, source: source) }
|
||||||
// SemanticVersion(version)! <= SemanticVersion("0.6.1")!
|
|
||||||
// {
|
|
||||||
// // delete the source, so that incoming will be saved.
|
|
||||||
// self.source?.managedObjectContext?.delete(self.source!)
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
throw SourceError.changedID(source.identifier, previousID: self.$source.identifier ?? "nil", source: source)
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -263,6 +263,50 @@ final class SettingsViewController: UITableViewController
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class BuildInfo{
|
||||||
|
private static let MARKETING_VERSION_TAG = "CFBundleShortVersionString"
|
||||||
|
private static let CURRENT_PROJECT_VERSION_TAG = kCFBundleVersionKey as String
|
||||||
|
|
||||||
|
private static let XCODE_VERSION_TAG = "DTXcode"
|
||||||
|
private static let XCODE_REVISION_TAG = "DTXcodeBuild"
|
||||||
|
|
||||||
|
let bundle: Bundle
|
||||||
|
|
||||||
|
public init(){
|
||||||
|
bundle = Bundle.main
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BundleError: Swift.Error {
|
||||||
|
case invalidURL
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(url: URL) throws {
|
||||||
|
guard let bundle = Bundle(url: url) else {
|
||||||
|
throw BundleError.invalidURL
|
||||||
|
}
|
||||||
|
self.bundle = bundle
|
||||||
|
}
|
||||||
|
|
||||||
|
public lazy var project_version: String? = {
|
||||||
|
let version = bundle.object(forInfoDictionaryKey: Self.CURRENT_PROJECT_VERSION_TAG) as? String
|
||||||
|
return version
|
||||||
|
}()
|
||||||
|
|
||||||
|
public lazy var marketing_version: String? = {
|
||||||
|
let version = bundle.object(forInfoDictionaryKey: Self.MARKETING_VERSION_TAG) as? String
|
||||||
|
return version
|
||||||
|
}()
|
||||||
|
|
||||||
|
public lazy var xcode: String? = {
|
||||||
|
let xcode = bundle.object(forInfoDictionaryKey: Self.XCODE_VERSION_TAG) as? String
|
||||||
|
return xcode
|
||||||
|
}()
|
||||||
|
|
||||||
|
public lazy var xcode_revision: String? = {
|
||||||
|
let revision = bundle.object(forInfoDictionaryKey: Self.XCODE_REVISION_TAG) as? String
|
||||||
|
return revision
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
private extension SettingsViewController
|
private extension SettingsViewController
|
||||||
{
|
{
|
||||||
@@ -949,7 +993,7 @@ extension SettingsViewController
|
|||||||
|
|
||||||
// Option 2: Discord
|
// Option 2: Discord
|
||||||
alertController.addAction(UIAlertAction(title: "Discord", style: .default) { _ in
|
alertController.addAction(UIAlertAction(title: "Discord", style: .default) { _ in
|
||||||
if let discordURL = URL(string: "https://discord.gg/sidestore-949183273383395328") {
|
if let discordURL = URL(string: "https://discord.gg/sidestore") {
|
||||||
let safariViewController = SFSafariViewController(url: discordURL)
|
let safariViewController = SFSafariViewController(url: discordURL)
|
||||||
safariViewController.preferredControlTintColor = .altPrimary
|
safariViewController.preferredControlTintColor = .altPrimary
|
||||||
self.present(safariViewController, animated: true, completion: nil)
|
self.present(safariViewController, animated: true, completion: nil)
|
||||||
|
|||||||
@@ -514,27 +514,27 @@ extension SourcesViewController: NSFetchedResultsControllerDelegate
|
|||||||
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||||
context.performAndWait {
|
context.performAndWait {
|
||||||
_ = Source.make(name: "OatmealDome's AltStore Source",
|
_ = Source.make(name: "OatmealDome's AltStore Source",
|
||||||
groupID: "me.oatmealdome.altstore",
|
identifier: "me.oatmealdome.altstore",
|
||||||
sourceURL: URL(string: "https://altstore.oatmealdome.me")!,
|
sourceURL: URL(string: "https://altstore.oatmealdome.me")!,
|
||||||
context: context)
|
context: context)
|
||||||
|
|
||||||
_ = Source.make(name: "UTM Repository",
|
_ = Source.make(name: "UTM Repository",
|
||||||
groupID: "com.utmapp.repos.UTM",
|
identifier: "com.utmapp.repos.UTM",
|
||||||
sourceURL: URL(string: "https://alt.getutm.app")!,
|
sourceURL: URL(string: "https://alt.getutm.app")!,
|
||||||
context: context)
|
context: context)
|
||||||
|
|
||||||
_ = Source.make(name: "Flyinghead",
|
_ = Source.make(name: "Flyinghead",
|
||||||
groupID: "com.flyinghead.source",
|
identifier: "com.flyinghead.source",
|
||||||
sourceURL: URL(string: "https://flyinghead.github.io/flycast-builds/altstore.json")!,
|
sourceURL: URL(string: "https://flyinghead.github.io/flycast-builds/altstore.json")!,
|
||||||
context: context)
|
context: context)
|
||||||
|
|
||||||
_ = Source.make(name: "Provenance",
|
_ = Source.make(name: "Provenance",
|
||||||
groupID: "org.provenance-emu.AltStore",
|
identifier: "org.provenance-emu.AltStore",
|
||||||
sourceURL: URL(string: "https://provenance-emu.com/apps.json")!,
|
sourceURL: URL(string: "https://provenance-emu.com/apps.json")!,
|
||||||
context: context)
|
context: context)
|
||||||
|
|
||||||
_ = Source.make(name: "PojavLauncher Repository",
|
_ = Source.make(name: "PojavLauncher Repository",
|
||||||
groupID: "dev.crystall1ne.repos.PojavLauncher",
|
identifier: "dev.crystall1ne.repos.PojavLauncher",
|
||||||
sourceURL: URL(string: "http://alt.crystall1ne.dev")!,
|
sourceURL: URL(string: "http://alt.crystall1ne.dev")!,
|
||||||
context: context)
|
context: context)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>_XCCurrentVersionName</key>
|
<key>_XCCurrentVersionName</key>
|
||||||
<string>AltStore 17_1.xcdatamodel</string>
|
<string>AltStore 17.xcdatamodel</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,315 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="23605" systemVersion="24D70" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="v17.1">
|
|
||||||
<entity name="Account" representedClassName="Account" syncable="YES">
|
|
||||||
<attribute name="appleID" attributeType="String"/>
|
|
||||||
<attribute name="firstName" attributeType="String"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="isActiveAccount" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="lastName" attributeType="String"/>
|
|
||||||
<relationship name="teams" toMany="YES" deletionRule="Cascade" destinationEntity="Team" inverseName="account" inverseEntity="Team"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="AppID" representedClassName="AppID" syncable="YES">
|
|
||||||
<attribute name="bundleIdentifier" attributeType="String"/>
|
|
||||||
<attribute name="expirationDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="features" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<relationship name="team" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Team" inverseName="appIDs" inverseEntity="Team"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="AppPermission" representedClassName="AppPermission" syncable="YES">
|
|
||||||
<attribute name="appBundleID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="permission" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="sourceID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="type" attributeType="String"/>
|
|
||||||
<attribute name="usageDescription" attributeType="String"/>
|
|
||||||
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="permissions" inverseEntity="StoreApp"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="sourceID"/>
|
|
||||||
<constraint value="appBundleID"/>
|
|
||||||
<constraint value="type"/>
|
|
||||||
<constraint value="permission"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="AppScreenshot" representedClassName="AppScreenshot" syncable="YES">
|
|
||||||
<attribute name="appBundleID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="deviceType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="height" optional="YES" attributeType="Integer 16" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="imageURL" attributeType="URI"/>
|
|
||||||
<attribute name="sourceID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="width" optional="YES" attributeType="Integer 16" usesScalarValueType="NO"/>
|
|
||||||
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="screenshots" inverseEntity="StoreApp"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="sourceID"/>
|
|
||||||
<constraint value="appBundleID"/>
|
|
||||||
<constraint value="imageURL"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="AppVersion" representedClassName="AppVersion" syncable="YES">
|
|
||||||
<attribute name="appBundleID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="buildVersion" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="channel" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="downloadURL" attributeType="URI"/>
|
|
||||||
<attribute name="localizedDescription" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="maxOSVersion" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="minOSVersion" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="sha256" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="size" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="sourceID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="version" attributeType="String"/>
|
|
||||||
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="versions" inverseEntity="StoreApp"/>
|
|
||||||
<relationship name="latestVersionApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="latestVersion" inverseEntity="StoreApp"/>
|
|
||||||
<relationship name="releaseTrack" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="ReleaseTrack" inverseName="releases" inverseEntity="ReleaseTrack"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="sourceID"/>
|
|
||||||
<constraint value="appBundleID"/>
|
|
||||||
<constraint value="version"/>
|
|
||||||
<constraint value="buildVersion"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="InstalledApp" representedClassName="InstalledApp" syncable="YES">
|
|
||||||
<attribute name="buildVersion" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="bundleIdentifier" attributeType="String"/>
|
|
||||||
<attribute name="certificateSerialNumber" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="expirationDate" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="hasAlternateIcon" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="hasUpdate" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="installedDate" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="isActive" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="isRefreshing" transient="YES" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<attribute name="needsResign" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="refreshedDate" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="resignedBundleIdentifier" attributeType="String"/>
|
|
||||||
<attribute name="storeBuildVersion" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="version" attributeType="String"/>
|
|
||||||
<relationship name="appExtensions" toMany="YES" deletionRule="Cascade" destinationEntity="InstalledExtension" inverseName="parentApp" inverseEntity="InstalledExtension"/>
|
|
||||||
<relationship name="loggedErrors" toMany="YES" deletionRule="Nullify" destinationEntity="LoggedError" inverseName="installedApp" inverseEntity="LoggedError"/>
|
|
||||||
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="installedApp" inverseEntity="StoreApp"/>
|
|
||||||
<relationship name="team" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Team" inverseName="installedApps" inverseEntity="Team"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="bundleIdentifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="InstalledExtension" representedClassName="InstalledExtension" syncable="YES">
|
|
||||||
<attribute name="bundleIdentifier" attributeType="String"/>
|
|
||||||
<attribute name="expirationDate" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="installedDate" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<attribute name="refreshedDate" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="resignedBundleIdentifier" attributeType="String"/>
|
|
||||||
<attribute name="version" attributeType="String"/>
|
|
||||||
<relationship name="parentApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="appExtensions" inverseEntity="InstalledApp"/>
|
|
||||||
</entity>
|
|
||||||
<entity name="LoggedError" representedClassName="LoggedError" syncable="YES">
|
|
||||||
<attribute name="appBundleID" attributeType="String"/>
|
|
||||||
<attribute name="appName" attributeType="String"/>
|
|
||||||
<attribute name="code" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="domain" attributeType="String"/>
|
|
||||||
<attribute name="operation" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="userInfo" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
|
||||||
<relationship name="installedApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="loggedErrors" inverseEntity="InstalledApp"/>
|
|
||||||
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="loggedErrors" inverseEntity="StoreApp"/>
|
|
||||||
</entity>
|
|
||||||
<entity name="NewsItem" representedClassName="NewsItem" syncable="YES">
|
|
||||||
<attribute name="appID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="caption" attributeType="String"/>
|
|
||||||
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="externalURL" optional="YES" attributeType="URI"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="imageURL" optional="YES" attributeType="URI"/>
|
|
||||||
<attribute name="isSilent" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="sortIndex" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="sourceIdentifier" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="tintColor" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
|
||||||
<attribute name="title" attributeType="String"/>
|
|
||||||
<relationship name="source" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="newsItems" inverseEntity="Source"/>
|
|
||||||
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="newsItems" inverseEntity="StoreApp"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
<constraint value="sourceIdentifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="PatreonAccount" representedClassName="PatreonAccount" syncable="YES">
|
|
||||||
<attribute name="firstName" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="isPatron" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<relationship name="pledges" toMany="YES" deletionRule="Cascade" destinationEntity="Pledge" inverseName="account" inverseEntity="Pledge"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="Patron" representedClassName="ManagedPatron" syncable="YES">
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="Pledge" representedClassName="Pledge" syncable="YES">
|
|
||||||
<attribute name="amount" attributeType="Decimal" defaultValueString="0"/>
|
|
||||||
<attribute name="campaignURL" attributeType="URI"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PatreonAccount" inverseName="pledges" inverseEntity="PatreonAccount"/>
|
|
||||||
<relationship name="rewards" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="PledgeReward" inverseName="pledge" inverseEntity="PledgeReward"/>
|
|
||||||
<relationship name="tiers" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="PledgeTier" inverseName="pledge" inverseEntity="PledgeTier"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="PledgeReward" representedClassName="PledgeReward" syncable="YES">
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<relationship name="pledge" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Pledge" inverseName="rewards" inverseEntity="Pledge"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="PledgeTier" representedClassName="PledgeTier" syncable="YES">
|
|
||||||
<attribute name="amount" attributeType="Decimal" defaultValueString="0.0"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="name" optional="YES" attributeType="String"/>
|
|
||||||
<relationship name="pledge" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Pledge" inverseName="tiers" inverseEntity="Pledge"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="RefreshAttempt" representedClassName="RefreshAttempt" syncable="YES">
|
|
||||||
<attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="errorDescription" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="isSuccess" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="ReleaseTrack" representedClassName="ReleaseTrack" syncable="YES">
|
|
||||||
<attribute name="appBundleID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="sourceID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="track" attributeType="String"/>
|
|
||||||
<relationship name="releases" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="AppVersion" inverseName="releaseTrack" inverseEntity="AppVersion"/>
|
|
||||||
<relationship name="storeApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="StoreApp" inverseName="releaseTracks" inverseEntity="StoreApp"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="sourceID"/>
|
|
||||||
<constraint value="appBundleID"/>
|
|
||||||
<constraint value="track"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="Source" representedClassName="Source" syncable="YES">
|
|
||||||
<attribute name="error" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
|
||||||
<attribute name="featuredSortID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="groupID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="hasFeaturedApps" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="headerImageURL" optional="YES" attributeType="URI"/>
|
|
||||||
<attribute name="iconURL" optional="YES" attributeType="URI"/>
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="localizedDescription" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<attribute name="patreonURL" optional="YES" attributeType="URI"/>
|
|
||||||
<attribute name="sourceURL" attributeType="URI"/>
|
|
||||||
<attribute name="subtitle" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="tintColor" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
|
||||||
<attribute name="version" attributeType="Integer 64" defaultValueString="1" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="websiteURL" optional="YES" attributeType="URI"/>
|
|
||||||
<relationship name="apps" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="StoreApp" inverseName="source" inverseEntity="StoreApp"/>
|
|
||||||
<relationship name="featuredApps" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="StoreApp" inverseName="featuringSource" inverseEntity="StoreApp"/>
|
|
||||||
<relationship name="newsItems" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="NewsItem" inverseName="source" inverseEntity="NewsItem"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="StoreApp" representedClassName="StoreApp" syncable="YES">
|
|
||||||
<attribute name="bundleIdentifier" attributeType="String"/>
|
|
||||||
<attribute name="category" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="developerName" attributeType="String"/>
|
|
||||||
<attribute name="downloadURL" optional="YES" attributeType="URI"/>
|
|
||||||
<attribute name="featuredSortID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="iconURL" attributeType="URI"/>
|
|
||||||
<attribute name="isHiddenWithoutPledge" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="isPledged" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="isPledgeRequired" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="localizedDescription" attributeType="String"/>
|
|
||||||
<attribute name="marketplaceID" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<attribute name="pledgeAmount" optional="YES" attributeType="Decimal"/>
|
|
||||||
<attribute name="pledgeCurrency" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="prefersCustomPledge" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="screenshotURLs" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
|
||||||
<attribute name="size" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="sortIndex" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="sourceIdentifier" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="subtitle" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="tintColor" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
|
||||||
<attribute name="version" optional="YES" attributeType="String"/>
|
|
||||||
<attribute name="versionDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
|
|
||||||
<attribute name="versionDescription" optional="YES" attributeType="String"/>
|
|
||||||
<relationship name="featuringSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="featuredApps" inverseEntity="Source"/>
|
|
||||||
<relationship name="installedApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="storeApp" inverseEntity="InstalledApp"/>
|
|
||||||
<relationship name="latestVersion" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="AppVersion" inverseName="latestVersionApp" inverseEntity="AppVersion"/>
|
|
||||||
<relationship name="loggedErrors" toMany="YES" deletionRule="Nullify" destinationEntity="LoggedError" inverseName="storeApp" inverseEntity="LoggedError"/>
|
|
||||||
<relationship name="newsItems" toMany="YES" deletionRule="Nullify" destinationEntity="NewsItem" inverseName="storeApp" inverseEntity="NewsItem"/>
|
|
||||||
<relationship name="permissions" toMany="YES" deletionRule="Cascade" destinationEntity="AppPermission" inverseName="app" inverseEntity="AppPermission"/>
|
|
||||||
<relationship name="releaseTracks" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="ReleaseTrack" inverseName="storeApp" inverseEntity="ReleaseTrack"/>
|
|
||||||
<relationship name="screenshots" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="AppScreenshot" inverseName="app" inverseEntity="AppScreenshot"/>
|
|
||||||
<relationship name="source" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="apps" inverseEntity="Source"/>
|
|
||||||
<relationship name="versions" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="AppVersion" inverseName="app" inverseEntity="AppVersion"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="sourceIdentifier"/>
|
|
||||||
<constraint value="bundleIdentifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
<entity name="Team" representedClassName="Team" syncable="YES">
|
|
||||||
<attribute name="identifier" attributeType="String"/>
|
|
||||||
<attribute name="isActiveTeam" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
|
||||||
<attribute name="name" attributeType="String"/>
|
|
||||||
<attribute name="type" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
|
||||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="teams" inverseEntity="Account"/>
|
|
||||||
<relationship name="appIDs" toMany="YES" deletionRule="Cascade" destinationEntity="AppID" inverseName="team" inverseEntity="AppID"/>
|
|
||||||
<relationship name="installedApps" toMany="YES" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="team" inverseEntity="InstalledApp"/>
|
|
||||||
<uniquenessConstraints>
|
|
||||||
<uniquenessConstraint>
|
|
||||||
<constraint value="identifier"/>
|
|
||||||
</uniquenessConstraint>
|
|
||||||
</uniquenessConstraints>
|
|
||||||
</entity>
|
|
||||||
</model>
|
|
||||||
@@ -33,7 +33,7 @@ public class AppPermission: BaseEntity
|
|||||||
default: return UnknownAppPermission(rawValue: self._permission)
|
default: return UnknownAppPermission(rawValue: self._permission)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@NSManaged @objc(permission) private(set) public var _permission: String
|
@NSManaged @objc(permission) private var _permission: String
|
||||||
|
|
||||||
// Set by StoreApp.
|
// Set by StoreApp.
|
||||||
@NSManaged public var appBundleID: String
|
@NSManaged public var appBundleID: String
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ public class InstalledApp: BaseEntity, InstalledAppProtocol
|
|||||||
let latestVer = SemanticVersion("\(latestSemVer!.major).\(latestSemVer!.minor).\(latestSemVer!.patch)")
|
let latestVer = SemanticVersion("\(latestSemVer!.major).\(latestSemVer!.minor).\(latestSemVer!.patch)")
|
||||||
|
|
||||||
// Compare by major.minor.patch
|
// Compare by major.minor.patch
|
||||||
if latestVer! > currentVer! {
|
if latestVer! > latestVer! {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,6 @@ extension MergePolicy{
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// break
|
|
||||||
// Unknown context-level conflict.
|
// Unknown context-level conflict.
|
||||||
// assertionFailure("MergePolicy is only intended to work with database-level conflicts.")
|
// assertionFailure("MergePolicy is only intended to work with database-level conflicts.")
|
||||||
assertionFailure("Context Conflict Detected: is there ambigious data in your incoming sources?\nConflict:\(conflict)")
|
assertionFailure("Context Conflict Detected: is there ambigious data in your incoming sources?\nConflict:\(conflict)")
|
||||||
|
|||||||
@@ -505,14 +505,13 @@ cxIAAYagXxAPTlNLZXllZEFyY2hpdmVy0QAIAAlUcm9vdIABrxESLAALAAwAGQA1ADYANwBjAGQAZQBm
|
|||||||
<relationship name="relationshipmappings" type="0/0" destination="XDDEVRELATIONSHIPMAPPING" idrefs="z142 z119 z223 z116 z229 z139 z236 z136 z214 z216"></relationship>
|
<relationship name="relationshipmappings" type="0/0" destination="XDDEVRELATIONSHIPMAPPING" idrefs="z142 z119 z223 z116 z229 z139 z236 z136 z214 z216"></relationship>
|
||||||
</object>
|
</object>
|
||||||
<object type="XDDEVENTITYMAPPING" id="z198">
|
<object type="XDDEVENTITYMAPPING" id="z198">
|
||||||
<attribute name="migrationpolicyclassname" type="string"></attribute>
|
|
||||||
<attribute name="sourcename" type="string">AppPermission</attribute>
|
<attribute name="sourcename" type="string">AppPermission</attribute>
|
||||||
<attribute name="mappingtypename" type="string">Undefined</attribute>
|
<attribute name="mappingtypename" type="string">Undefined</attribute>
|
||||||
<attribute name="mappingnumber" type="int16">17</attribute>
|
<attribute name="mappingnumber" type="int16">17</attribute>
|
||||||
<attribute name="destinationname" type="string">AppPermission</attribute>
|
<attribute name="destinationname" type="string">AppPermission</attribute>
|
||||||
<attribute name="autogenerateexpression" type="bool">1</attribute>
|
<attribute name="autogenerateexpression" type="bool">1</attribute>
|
||||||
<relationship name="mappingmodel" type="1/1" destination="XDDEVMAPPINGMODEL" idrefs="z192"></relationship>
|
<relationship name="mappingmodel" type="1/1" destination="XDDEVMAPPINGMODEL" idrefs="z192"></relationship>
|
||||||
<relationship name="attributemappings" type="0/0" destination="XDDEVATTRIBUTEMAPPING" idrefs="z123 z213 z287 z240 z191"></relationship>
|
<relationship name="attributemappings" type="0/0" destination="XDDEVATTRIBUTEMAPPING" idrefs="z213 z240 z191 z123 z287"></relationship>
|
||||||
<relationship name="relationshipmappings" type="0/0" destination="XDDEVRELATIONSHIPMAPPING" idrefs="z288"></relationship>
|
<relationship name="relationshipmappings" type="0/0" destination="XDDEVRELATIONSHIPMAPPING" idrefs="z288"></relationship>
|
||||||
</object>
|
</object>
|
||||||
<object type="XDDEVENTITYMAPPING" id="z199">
|
<object type="XDDEVENTITYMAPPING" id="z199">
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Source11To17MigrationPolicy.swift
|
// SourceMigrationPolicy.swift
|
||||||
// AltStoreCore
|
// AltStoreCore
|
||||||
//
|
//
|
||||||
// Created by Riley Testut on 10/19/23.
|
// Created by Riley Testut on 10/19/23.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// StoreApp11To17MigrationPolicy.swift
|
// StoreAppMigration11To17Policy.swift
|
||||||
// AltStore
|
// AltStore
|
||||||
//
|
//
|
||||||
// Created by Magesh K on 25/02/25.
|
// Created by Magesh K on 25/02/25.
|
||||||
@@ -108,7 +108,7 @@ class StoreApp11To17MigrationPolicy: NSEntityMigrationPolicy {
|
|||||||
|
|
||||||
// Create a new ReleaseTrack entity
|
// Create a new ReleaseTrack entity
|
||||||
let context = dInstance.managedObjectContext!
|
let context = dInstance.managedObjectContext!
|
||||||
let releaseTrack = NSEntityDescription.insertNewObject(forEntityName: ReleaseTrack.entity().name ?? ReleaseTrack.description(), into: context)
|
let releaseTrack = NSEntityDescription.insertNewObject(forEntityName: ReleaseTrack.entity().name!, into: context)
|
||||||
releaseTrack.setValue(defaultChannel, forKey: #keyPath(ReleaseTrack._track))
|
releaseTrack.setValue(defaultChannel, forKey: #keyPath(ReleaseTrack._track))
|
||||||
|
|
||||||
// Connect the releaseTrack to the destination StoreApp
|
// Connect the releaseTrack to the destination StoreApp
|
||||||
@@ -116,7 +116,7 @@ class StoreApp11To17MigrationPolicy: NSEntityMigrationPolicy {
|
|||||||
|
|
||||||
|
|
||||||
// Find the mapping name for AppVersion (make sure this exactly matches your mapping model)
|
// Find the mapping name for AppVersion (make sure this exactly matches your mapping model)
|
||||||
let appVersionMappingName = findEntityMappingName(for: AppVersion.entity().name ?? AppVersion.description(), in: manager)
|
let appVersionMappingName = findEntityMappingName(for: AppVersion.entity().name!, in: manager)
|
||||||
|
|
||||||
// Create a mutable ordered set for the destination AppVersion objects
|
// Create a mutable ordered set for the destination AppVersion objects
|
||||||
let destinationVersionsSet = NSMutableOrderedSet()
|
let destinationVersionsSet = NSMutableOrderedSet()
|
||||||
@@ -145,5 +145,5 @@ class StoreApp11To17MigrationPolicy: NSEntityMigrationPolicy {
|
|||||||
// dInstance.setValue(NSOrderedSet(), forKey: #keyPath(StoreApp._versions))
|
// dInstance.setValue(NSOrderedSet(), forKey: #keyPath(StoreApp._versions))
|
||||||
dInstance.setValue(nil, forKey: #keyPath(StoreApp._versions))
|
dInstance.setValue(nil, forKey: #keyPath(StoreApp._versions))
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// AppPermission17to17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
@objc(AppPermission11To17_1MigrationPolicy)
|
|
||||||
class AppPermission11To17_1MigrationPolicy: AppPermission17To17_1MigrationPolicy {
|
|
||||||
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
// Let the default implementation create the basic destination AppPermission
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override func createRelationships(
|
|
||||||
forDestination dInstance: NSManagedObject,
|
|
||||||
in mapping: NSEntityMapping,
|
|
||||||
manager: NSMigrationManager
|
|
||||||
) throws {
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// ReleaseTrack11To17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
@objc(ReleaseTrack11To17_1MigrationPolicy)
|
|
||||||
class ReleaseTrack11To17_1MigrationPolicy: ReleaseTrack17To17_1MigrationPolicy {
|
|
||||||
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override func createRelationships(
|
|
||||||
forDestination dInstance: NSManagedObject,
|
|
||||||
in mapping: NSEntityMapping,
|
|
||||||
manager: NSMigrationManager
|
|
||||||
) throws {
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// Source11To17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
@objc(Source11To17_1MigrationPolicy)
|
|
||||||
class Source11To17_1MigrationPolicy: Source17To17_1MigrationPolicy
|
|
||||||
{
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
// Let the default implementation create the basic destination AppPermission
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
|
|
||||||
{
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
//
|
|
||||||
// StoreApp11To17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
fileprivate extension NSManagedObject
|
|
||||||
{
|
|
||||||
var storeAppReleaseTracks: NSOrderedSet? {
|
|
||||||
let tracks = self.value(forKey: #keyPath(StoreApp._releaseTracks)) as? NSOrderedSet
|
|
||||||
return tracks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc(StoreApp11To17_1MigrationPolicy)
|
|
||||||
class StoreApp11To17_1MigrationPolicy: StoreApp11To17MigrationPolicy
|
|
||||||
{
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
|
|
||||||
{
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
|
|
||||||
let appBundleID = dInstance.value(forKey: #keyPath(StoreApp.bundleIdentifier))
|
|
||||||
let sourceID = dInstance.value(forKey: #keyPath(StoreApp.sourceIdentifier))
|
|
||||||
|
|
||||||
for case let track as NSManagedObject in dInstance.storeAppReleaseTracks ?? []
|
|
||||||
{
|
|
||||||
track.setValue(appBundleID, forKey: #keyPath(ReleaseTrack._appBundleID))
|
|
||||||
track.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
|
|
||||||
|
|
||||||
guard let releases = track.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for case let version as NSManagedObject in releases {
|
|
||||||
version.setValue(appBundleID, forKey: #keyPath(AppVersion.appBundleID))
|
|
||||||
version.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let permissions = dInstance.value(forKey: #keyPath(StoreApp._permissions)) as? NSSet {
|
|
||||||
for case let permission as NSManagedObject in permissions {
|
|
||||||
permission.setValue(appBundleID, forKey: #keyPath(AppPermission.appBundleID))
|
|
||||||
permission.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let screenshots = dInstance.value(forKey: #keyPath(StoreApp._screenshots)) as? NSOrderedSet {
|
|
||||||
for case let screenshot as NSManagedObject in screenshots {
|
|
||||||
screenshot.setValue(appBundleID, forKey: #keyPath(AppScreenshot.appBundleID))
|
|
||||||
screenshot.setValue(sourceID, forKey: #keyPath(AppScreenshot.sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
//
|
|
||||||
// AppPermission17to17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
@objc(AppPermission17To17_1MigrationPolicy)
|
|
||||||
class AppPermission17To17_1MigrationPolicy: NSEntityMigrationPolicy {
|
|
||||||
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
// Let the default implementation create the basic destination AppPermission
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
|
|
||||||
// Get the destination AppPermission instance that was created
|
|
||||||
guard let destinationPermission = manager.destinationInstances(forEntityMappingName: mapping.name, sourceInstances: [sInstance]).first else {
|
|
||||||
print("Failed to locate destination AppPermission instance")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the type value from source
|
|
||||||
if let type = sInstance.value(forKey: #keyPath(AppPermission.type)) as? String {
|
|
||||||
// this is for backwards compatibility <0.5.10
|
|
||||||
// if older type was a valid permission, then consider it as "privacy" in the newer "type".
|
|
||||||
if let permission = self.derivePermissionFromType(type) {
|
|
||||||
destinationPermission.setValue(permission, forKey: #keyPath(AppPermission._permission))
|
|
||||||
destinationPermission.setValue("privacy", forKey: #keyPath(AppPermission.type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set initial values copied from source as-is
|
|
||||||
// (will be updated by StoreApp and Source migration policy in its createRelationship() method)
|
|
||||||
if let storeApp = sInstance.value(forKey: #keyPath(AppPermission.app)) as? NSManagedObject{
|
|
||||||
if let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String{
|
|
||||||
destinationPermission.setValue(appBundle, forKey: #keyPath(AppPermission.appBundleID))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String {
|
|
||||||
destinationPermission.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override func createRelationships(
|
|
||||||
forDestination dInstance: NSManagedObject,
|
|
||||||
in mapping: NSEntityMapping,
|
|
||||||
manager: NSMigrationManager
|
|
||||||
) throws {
|
|
||||||
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
|
|
||||||
// Retrieve the source storeApp from the source appPermission
|
|
||||||
guard let storeApp = dInstance.value(forKey: #keyPath(AppPermission.app)) as? NSManagedObject else {
|
|
||||||
print("Destination \(AppPermission.description()) has no storeApp")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// set initial values copied from source as-is to satisfy unique constraints
|
|
||||||
// (will be updated by StoreApp and Source migration policy in its createRelationship() method)
|
|
||||||
if let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String{
|
|
||||||
dInstance.setValue(appBundle, forKey: #keyPath(AppPermission.appBundleID))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String {
|
|
||||||
dInstance.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to derive permission string from type
|
|
||||||
private func derivePermissionFromType(_ type: String) -> String? {
|
|
||||||
|
|
||||||
// Based on the code in the documents, we need to map the ALTAppPermissionType to permission strings
|
|
||||||
switch type {
|
|
||||||
case "photos": return "NSPhotosUsageDescription"
|
|
||||||
case "camera": return "NSCameraUsageDescription"
|
|
||||||
case "location": return "NSLocationUsageDescription"
|
|
||||||
case "contacts": return "NSContactsUsageDescription"
|
|
||||||
case "reminders": return "NSRemindersUsageDescription"
|
|
||||||
case "music": return "NSAppleMusicUsageDescription"
|
|
||||||
case "microphone": return "NSMicrophoneUsageDescription"
|
|
||||||
case "speech-recognition": return "NSSpeechRecognitionUsageDescription"
|
|
||||||
case "background-audio": return "NSBackgroundAudioUsageDescription"
|
|
||||||
case "background-fetch": return "NSBackgroundFetchUsageDescription"
|
|
||||||
case "bluetooth": return "NSBluetoothUsageDescription"
|
|
||||||
case "network": return "NSNetworkUsageDescription"
|
|
||||||
case "calendars": return "NSCalendarsUsageDescription"
|
|
||||||
case "touchID": return "NSTouchIDUsageDescription"
|
|
||||||
case "faceID": return "NSFaceIDUsageDescription"
|
|
||||||
case "siri": return "NSSiriUsageDescription"
|
|
||||||
case "motion": return "NSMotionUsageDescription"
|
|
||||||
|
|
||||||
default: return nil // Default fallback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
//
|
|
||||||
// ReleaseTrack17To17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
@objc(ReleaseTrack17To17_1MigrationPolicy)
|
|
||||||
class ReleaseTrack17To17_1MigrationPolicy: NSEntityMigrationPolicy {
|
|
||||||
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override func createRelationships(
|
|
||||||
forDestination dInstance: NSManagedObject,
|
|
||||||
in mapping: NSEntityMapping,
|
|
||||||
manager: NSMigrationManager
|
|
||||||
) throws {
|
|
||||||
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
|
|
||||||
// Retrieve the source storeApp from the source ReleaseTrack
|
|
||||||
guard let storeApp = dInstance.value(forKey: #keyPath(ReleaseTrack.storeApp)) as? NSManagedObject else {
|
|
||||||
print("Destination \(ReleaseTrack.description()) has no storeApp")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// set initial values copied from source as-is to satisfy unique constraints
|
|
||||||
// (will be updated by StoreApp and Source migration policy in its createRelationship() method)
|
|
||||||
let appBundle = storeApp.value(forKey: #keyPath(StoreApp.bundleIdentifier)) as? String
|
|
||||||
let sourceID = storeApp.value(forKey: #keyPath(StoreApp.sourceIdentifier)) as? String
|
|
||||||
|
|
||||||
if let appBundle {
|
|
||||||
dInstance.setValue(appBundle, forKey: #keyPath(ReleaseTrack._appBundleID))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let sourceID {
|
|
||||||
dInstance.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let releases = dInstance.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet {
|
|
||||||
for case let version as NSManagedObject in releases {
|
|
||||||
if let appBundle {
|
|
||||||
version.setValue(appBundle, forKey: #keyPath(AppVersion.appBundleID))
|
|
||||||
}
|
|
||||||
if let sourceID {
|
|
||||||
version.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
//
|
|
||||||
// Source17To17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
fileprivate extension NSManagedObject
|
|
||||||
{
|
|
||||||
var sourceSourceURL: URL? {
|
|
||||||
let sourceURL = self.value(forKey: #keyPath(Source.sourceURL)) as? URL
|
|
||||||
return sourceURL
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceSourceId: String? {
|
|
||||||
let sourceId = self.value(forKey: #keyPath(Source.identifier)) as? String
|
|
||||||
return sourceId
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceApps: NSOrderedSet? {
|
|
||||||
let apps = self.value(forKey: #keyPath(Source._apps)) as? NSOrderedSet
|
|
||||||
return apps
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceNewsItems: NSOrderedSet? {
|
|
||||||
let newsItems = self.value(forKey: #keyPath(Source._newsItems)) as? NSOrderedSet
|
|
||||||
return newsItems
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate extension NSManagedObject
|
|
||||||
{
|
|
||||||
func setSourceId(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(Source.identifier))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setGroupId(_ groupID: String)
|
|
||||||
{
|
|
||||||
self.setValue(groupID, forKey: #keyPath(Source.groupID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setSourceSourceUrl(_ sourceURL: URL)
|
|
||||||
{
|
|
||||||
self.setValue(sourceURL, forKey: #keyPath(Source.sourceURL))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setSourceSourceID(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(Source.identifier))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setStoreAppSourceID(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(StoreApp.sourceIdentifier))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setNewsItemSourceID(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(NewsItem.sourceIdentifier))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setAppVersionSourceID(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setAppPermissionSourceID(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(AppPermission.sourceID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setAppScreenshotSourceID(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(AppScreenshot.sourceID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func setReleaseTracksSourceID(_ sourceID: String)
|
|
||||||
{
|
|
||||||
self.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fileprivate extension NSManagedObject
|
|
||||||
{
|
|
||||||
var storeAppVersions: NSOrderedSet? {
|
|
||||||
let versions = self.value(forKey: #keyPath(StoreApp._versions)) as? NSOrderedSet
|
|
||||||
return versions
|
|
||||||
}
|
|
||||||
|
|
||||||
var storeAppPermissions: NSSet? {
|
|
||||||
let permissions = self.value(forKey: #keyPath(StoreApp._permissions)) as? NSSet
|
|
||||||
return permissions
|
|
||||||
}
|
|
||||||
|
|
||||||
var storeAppScreenshots: NSOrderedSet? {
|
|
||||||
let screenshots = self.value(forKey: #keyPath(StoreApp._screenshots)) as? NSOrderedSet
|
|
||||||
return screenshots
|
|
||||||
}
|
|
||||||
|
|
||||||
var storeAppReleaseTracks: NSOrderedSet? {
|
|
||||||
let tracks = self.value(forKey: #keyPath(StoreApp._releaseTracks)) as? NSOrderedSet
|
|
||||||
return tracks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc(Source17To17_1MigrationPolicy)
|
|
||||||
class Source17To17_1MigrationPolicy: NSEntityMigrationPolicy
|
|
||||||
{
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
// Let the default implementation create the basic destination AppPermission
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
|
|
||||||
// Get the destination Source instance that was created
|
|
||||||
guard let dInstance = manager.destinationInstances(forEntityMappingName: mapping.name, sourceInstances: [sInstance]).first else {
|
|
||||||
print("Failed to locate destination Source instance")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// update new fields with initial values
|
|
||||||
if let sourceID = sInstance.value(forKey: #keyPath(Source.identifier)) as? String {
|
|
||||||
dInstance.setValue(sourceID, forKey: #keyPath(Source.groupID))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard var sourceURL = dInstance.sourceSourceURL else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// sidestore official source has been moved to sidestore.io/apps-v2.json
|
|
||||||
// if we don't switch, users will end up with 2 offical sources
|
|
||||||
let normalizedSourceURL = try? sourceURL.normalized()
|
|
||||||
if normalizedSourceURL == "apps.sidestore.io" // if using old source url (<0.5.9)
|
|
||||||
{
|
|
||||||
sourceURL = Source.altStoreSourceURL // switch to latest
|
|
||||||
dInstance.setSourceSourceUrl(sourceURL) // and use it for current
|
|
||||||
}
|
|
||||||
|
|
||||||
var sourceID = try Source.sourceID(from: sourceURL)
|
|
||||||
dInstance.setSourceId(sourceID)
|
|
||||||
|
|
||||||
// for older versions migrating to current (their sourceID is their groupID)
|
|
||||||
dInstance.setGroupId(sourceID)
|
|
||||||
|
|
||||||
if sourceID == "apps.sidestore.io" {
|
|
||||||
sourceID = Source.altStoreIdentifier
|
|
||||||
dInstance.setSourceId(sourceID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
|
|
||||||
{
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
|
|
||||||
guard let sourceID = dInstance.sourceSourceId else { return }
|
|
||||||
|
|
||||||
for case let newsItem as NSManagedObject in dInstance.sourceNewsItems ?? []
|
|
||||||
{
|
|
||||||
newsItem.setNewsItemSourceID(sourceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
for case let app as NSManagedObject in dInstance.sourceApps ?? []
|
|
||||||
{
|
|
||||||
app.setStoreAppSourceID(sourceID)
|
|
||||||
|
|
||||||
for case let screenshot as NSManagedObject in app.storeAppScreenshots ?? []
|
|
||||||
{
|
|
||||||
screenshot.setAppScreenshotSourceID(sourceID)
|
|
||||||
}
|
|
||||||
|
|
||||||
for case let track as NSManagedObject in app.storeAppReleaseTracks ?? []
|
|
||||||
{
|
|
||||||
// print("Source_17_1MigrationPolicy: processing track \(track.value(forKey: "track")!)")
|
|
||||||
track.setValue(sourceID, forKey: #keyPath(ReleaseTrack._sourceID))
|
|
||||||
|
|
||||||
guard let releases = track.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet else {
|
|
||||||
// print("Source_17_1MigrationPolicy: releases not found for track: \(track.value(forKey: "track")!)")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for case let version as NSManagedObject in releases {
|
|
||||||
// print("Source_17_1MigrationPolicy: updating sourceID for version: \(version.value(forKey: "version")!)")
|
|
||||||
version.setValue(sourceID, forKey: #keyPath(AppVersion.sourceID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
//
|
|
||||||
// StoreApp17To17_1MigrationPolicy.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 15/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
fileprivate extension NSManagedObject
|
|
||||||
{
|
|
||||||
var storeAppReleaseTracks: NSOrderedSet? {
|
|
||||||
let tracks = self.value(forKey: #keyPath(StoreApp._releaseTracks)) as? NSOrderedSet
|
|
||||||
return tracks
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc(StoreApp17To17_1MigrationPolicy)
|
|
||||||
class StoreApp17To17_1MigrationPolicy: NSEntityMigrationPolicy
|
|
||||||
{
|
|
||||||
override func createDestinationInstances(forSource sInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
|
|
||||||
try super.createDestinationInstances(forSource: sInstance, in: mapping, manager: manager)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func createRelationships(forDestination dInstance: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws
|
|
||||||
{
|
|
||||||
try super.createRelationships(forDestination: dInstance, in: mapping, manager: manager)
|
|
||||||
|
|
||||||
let appBundleID = dInstance.value(forKey: #keyPath(StoreApp.bundleIdentifier))
|
|
||||||
|
|
||||||
for case let track as NSManagedObject in dInstance.storeAppReleaseTracks ?? []
|
|
||||||
{
|
|
||||||
track.setValue(appBundleID, forKey: #keyPath(ReleaseTrack._appBundleID))
|
|
||||||
|
|
||||||
guard let releases = track.value(forKey: #keyPath(ReleaseTrack._releases)) as? NSOrderedSet else {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for case let version as NSManagedObject in releases {
|
|
||||||
version.setValue(appBundleID, forKey: #keyPath(AppVersion.appBundleID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let permissions = dInstance.value(forKey: #keyPath(StoreApp._permissions)) as? NSSet {
|
|
||||||
for case let permission as NSManagedObject in permissions {
|
|
||||||
permission.setValue(appBundleID, forKey: #keyPath(AppPermission.appBundleID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let screenshots = dInstance.value(forKey: #keyPath(StoreApp._screenshots)) as? NSOrderedSet {
|
|
||||||
for case let screenshot as NSManagedObject in screenshots {
|
|
||||||
screenshot.setValue(appBundleID, forKey: #keyPath(AppScreenshot.appBundleID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -75,9 +75,6 @@ public class NewsItem: BaseEntity, Decodable
|
|||||||
self.imageURL = try container.decodeIfPresent(URL.self, forKey: .imageURL)
|
self.imageURL = try container.decodeIfPresent(URL.self, forKey: .imageURL)
|
||||||
self.externalURL = try container.decodeIfPresent(URL.self, forKey: .externalURL)
|
self.externalURL = try container.decodeIfPresent(URL.self, forKey: .externalURL)
|
||||||
|
|
||||||
// TODO: app specific news should be moved to appEntity (via refactoring)
|
|
||||||
// This can be done by: 1. having two newsItem schema, one for source and one for StoreApp
|
|
||||||
// 2. move this appID field into the newItem schema which is under StoreApp.
|
|
||||||
self.appID = try container.decodeIfPresent(String.self, forKey: .appID)
|
self.appID = try container.decodeIfPresent(String.self, forKey: .appID)
|
||||||
|
|
||||||
let notify = try container.decodeIfPresent(Bool.self, forKey: .notify) ?? false
|
let notify = try container.decodeIfPresent(Bool.self, forKey: .notify) ?? false
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ public class ReleaseTrack: BaseEntity, Decodable
|
|||||||
{
|
{
|
||||||
// attributes
|
// attributes
|
||||||
@NSManaged @objc(track) public private(set) var _track: String?
|
@NSManaged @objc(track) public private(set) var _track: String?
|
||||||
@NSManaged @objc(appBundleID) public private(set) var _appBundleID: String?
|
|
||||||
@NSManaged @objc(sourceID) public private(set) var _sourceID: String?
|
|
||||||
|
|
||||||
// RelationShips
|
// RelationShips
|
||||||
@NSManaged @objc(releases) public private(set) var _releases: NSOrderedSet?
|
@NSManaged @objc(releases) public private(set) var _releases: NSOrderedSet?
|
||||||
@@ -91,7 +89,7 @@ public extension ReleaseTrack{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update it into the appVersion
|
// update it into the appVersion
|
||||||
_ = version.mutateForData(channel: track, appBundleID: storeApp.bundleIdentifier, sourceID: storeApp.sourceIdentifier)
|
_ = version.mutateForData(channel: track, appBundleID: storeApp.bundleIdentifier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,10 +109,6 @@ public extension ReleaseTrack{
|
|||||||
if key == NSExpression(forKeyPath: #keyPath(ReleaseTrack.storeApp)).keyPath
|
if key == NSExpression(forKeyPath: #keyPath(ReleaseTrack.storeApp)).keyPath
|
||||||
{
|
{
|
||||||
updateVersions(for: storeApp)
|
updateVersions(for: storeApp)
|
||||||
|
|
||||||
// update unique constraint attribs
|
|
||||||
self._appBundleID = storeApp?.bundleIdentifier
|
|
||||||
self._sourceID = storeApp?.sourceIdentifier
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,7 @@ import UIKit
|
|||||||
|
|
||||||
public extension Source
|
public extension Source
|
||||||
{
|
{
|
||||||
#if ALPHA
|
static let altStoreIdentifier = try! Source.sourceID(from: Source.altStoreSourceURL)
|
||||||
static let altStoreGroupIdentifier = Bundle.Info.appbundleIdentifier
|
|
||||||
#else
|
|
||||||
static let altStoreGroupIdentifier = Bundle.Info.appbundleIdentifier
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if STAGING
|
#if STAGING
|
||||||
|
|
||||||
@@ -28,15 +24,13 @@ public extension Source
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
#if ALPHA
|
#if ALPHA
|
||||||
static let altStoreSourceURL = URL(string: "https://sidestore.io/apps-v2.json/")!
|
static let altStoreSourceURL = URL(string: "https://apps.sidestore.io/")!
|
||||||
#else
|
#else
|
||||||
static let altStoreSourceURL = URL(string: "https://sidestore.io/apps-v2.json/")!
|
// static let altStoreSourceURL = URL(string: "https://apps.sidestore.io/")!
|
||||||
|
static let altStoreSourceURL = URL(string: "https://sidestore.io/apps-v2.json/")! // using v2 for alpha releases
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// normalized url is the source identifier (or) p-key!
|
|
||||||
static let altStoreIdentifier = try! Source.sourceID(from: altStoreSourceURL)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// public struct AppPermissionFeed: Codable {
|
// public struct AppPermissionFeed: Codable {
|
||||||
@@ -211,8 +205,7 @@ public class Source: BaseEntity, Decodable
|
|||||||
/* Properties */
|
/* Properties */
|
||||||
@NSManaged public var version: Int
|
@NSManaged public var version: Int
|
||||||
@NSManaged public var name: String
|
@NSManaged public var name: String
|
||||||
@NSManaged public private(set) var identifier: String // NOTE: sourceID is just normalized sourceURL
|
@NSManaged public private(set) var identifier: String
|
||||||
@NSManaged public private(set) var groupID: String?
|
|
||||||
@NSManaged public var sourceURL: URL
|
@NSManaged public var sourceURL: URL
|
||||||
|
|
||||||
/* Source Detail */
|
/* Source Detail */
|
||||||
@@ -287,9 +280,6 @@ public class Source: BaseEntity, Decodable
|
|||||||
case news
|
case news
|
||||||
case featuredApps
|
case featuredApps
|
||||||
case userInfo
|
case userInfo
|
||||||
|
|
||||||
// case identifier
|
|
||||||
case groupID = "identifier"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
||||||
@@ -369,24 +359,6 @@ public class Source: BaseEntity, Decodable
|
|||||||
|
|
||||||
// Updates identifier + apps & newsItems
|
// Updates identifier + apps & newsItems
|
||||||
try self.setSourceURL(sourceURL)
|
try self.setSourceURL(sourceURL)
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Source ID is just normalized sourceURL. coz normalized url is the primary key which needs to be unique
|
|
||||||
// Hence if a source's URL changed, then it means it is a different source now.
|
|
||||||
// This also means that the identifier field in the source is irrelevant (if any)
|
|
||||||
|
|
||||||
// if we want grouping of sources from same author or something like that then we should have used groupID (a new field)
|
|
||||||
// shouldn't use the existing "identifier" field, hence the following is commented out
|
|
||||||
|
|
||||||
// // if an explicit identifier is present, then use it
|
|
||||||
// if let identifier = try container.decodeIfPresent(String.self, forKey: .identifier)
|
|
||||||
// {
|
|
||||||
// self.identifier = identifier
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if an explicit (group)identifier is present, then use it as groupID else use sourceID as groupID too
|
|
||||||
self.groupID = try container.decodeIfPresent(String.self, forKey: .groupID) ?? self.identifier
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -433,7 +405,7 @@ public extension Source
|
|||||||
|
|
||||||
// TODO: Support alternate URLs
|
// TODO: Support alternate URLs
|
||||||
let isRecommended = recommendedSources.contains { source in
|
let isRecommended = recommendedSources.contains { source in
|
||||||
return source.identifier == self.identifier || source.sourceURL?.absoluteString.lowercased() == self.sourceURL.absoluteString.lowercased()
|
return source.identifier == self.identifier || source.sourceURL?.absoluteString.lowercased() == self.sourceURL.absoluteString
|
||||||
}
|
}
|
||||||
return isRecommended
|
return isRecommended
|
||||||
}
|
}
|
||||||
@@ -446,17 +418,14 @@ public extension Source
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Source
|
internal extension Source
|
||||||
{
|
{
|
||||||
class func sourceID(from sourceURL: URL) throws -> String
|
class func sourceID(from sourceURL: URL) throws -> String
|
||||||
{
|
{
|
||||||
let sourceID = try sourceURL.normalized()
|
let sourceID = try sourceURL.normalized()
|
||||||
return sourceID
|
return sourceID
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal extension Source
|
|
||||||
{
|
|
||||||
func setFeaturedApps(_ featuredApps: [StoreApp]?)
|
func setFeaturedApps(_ featuredApps: [StoreApp]?)
|
||||||
{
|
{
|
||||||
// Explicitly update relationships for all apps to ensure featuredApps merges correctly.
|
// Explicitly update relationships for all apps to ensure featuredApps merges correctly.
|
||||||
@@ -482,16 +451,10 @@ public extension Source
|
|||||||
{
|
{
|
||||||
func setSourceURL(_ sourceURL: URL) throws
|
func setSourceURL(_ sourceURL: URL) throws
|
||||||
{
|
{
|
||||||
self.sourceURL = sourceURL
|
|
||||||
|
|
||||||
// update the normalized sourceURL as the identifier
|
|
||||||
let identifier = try Source.sourceID(from: sourceURL)
|
let identifier = try Source.sourceID(from: sourceURL)
|
||||||
try self.setSourceID(identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setSourceID(_ identifier: String) throws
|
|
||||||
{
|
|
||||||
self.identifier = identifier
|
self.identifier = identifier
|
||||||
|
self.sourceURL = sourceURL
|
||||||
|
|
||||||
for app in self.apps
|
for app in self.apps
|
||||||
{
|
{
|
||||||
@@ -516,7 +479,6 @@ public extension Source
|
|||||||
{
|
{
|
||||||
let source = Source(context: context)
|
let source = Source(context: context)
|
||||||
source.name = "SideStore Offical"
|
source.name = "SideStore Offical"
|
||||||
source.groupID = Source.altStoreGroupIdentifier
|
|
||||||
source.identifier = Source.altStoreIdentifier
|
source.identifier = Source.altStoreIdentifier
|
||||||
try! source.setSourceURL(Source.altStoreSourceURL)
|
try! source.setSourceURL(Source.altStoreSourceURL)
|
||||||
|
|
||||||
@@ -529,13 +491,12 @@ public extension Source
|
|||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|
||||||
class func make(name: String, groupID: String, sourceURL: URL, context: NSManagedObjectContext) -> Source
|
class func make(name: String, identifier: String, sourceURL: URL, context: NSManagedObjectContext) -> Source
|
||||||
{
|
{
|
||||||
let source = Source(context: context)
|
let source = Source(context: context)
|
||||||
source.name = name
|
source.name = name
|
||||||
|
source.identifier = identifier
|
||||||
source.sourceURL = sourceURL
|
source.sourceURL = sourceURL
|
||||||
source.sourceURL = sourceURL
|
|
||||||
source.identifier = try! Source.sourceID(from: sourceURL)
|
|
||||||
|
|
||||||
return source
|
return source
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// Configuration settings file format documentation can be found at:
|
// Configuration settings file format documentation can be found at:
|
||||||
// https://help.apple.com/xcode/#/dev745c5c974
|
// https://help.apple.com/xcode/#/dev745c5c974
|
||||||
|
|
||||||
MARKETING_VERSION = 0.6.1
|
MARKETING_VERSION = 0.6.0
|
||||||
CURRENT_PROJECT_VERSION = 0601
|
CURRENT_PROJECT_VERSION = 6000
|
||||||
|
|
||||||
// Vars to be overwritten by `CodeSigning.xcconfig` if exists
|
// Vars to be overwritten by `CodeSigning.xcconfig` if exists
|
||||||
DEVELOPMENT_TEAM = S32Z3HMYVQ
|
DEVELOPMENT_TEAM = S32Z3HMYVQ
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
DEVELOPMENT_TEAM = XYZ0123456
|
DEVELOPMENT_TEAM = XYZ0123456
|
||||||
|
|
||||||
// Set this for dev-local xcode builds
|
// Set this for dev-local xcode builds
|
||||||
//MARKETING_VERSION_SUFFIX = -local
|
MARKETING_VERSION_SUFFIX = -local
|
||||||
|
|
||||||
// Prefix of unique bundle IDs registered to you in Apple Developer Portal.
|
// Prefix of unique bundle IDs registered to you in Apple Developer Portal.
|
||||||
// You need to register:
|
// You need to register:
|
||||||
|
|||||||
1
Makefile
1
Makefile
@@ -213,7 +213,6 @@ build-tests:
|
|||||||
@echo "Performing a build-for-testing..."
|
@echo "Performing a build-for-testing..."
|
||||||
@xcodebuild build-for-testing \
|
@xcodebuild build-for-testing \
|
||||||
-enableCodeCoverage YES \
|
-enableCodeCoverage YES \
|
||||||
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2' \
|
|
||||||
$(COMMON_BUILD_SETTINGS)
|
$(COMMON_BUILD_SETTINGS)
|
||||||
|
|
||||||
run-tests:
|
run-tests:
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Why iOS 15? Targeting such a recent version of iOS allows us to accelerate devel
|
|||||||
SideStore is a just regular, sandboxed iOS application. The AltStore app target contains the vast majority of SideStore's functionality, including all the logic for downloading and updating apps through SideStore. SideStore makes heavy use of standard iOS frameworks and technologies most iOS developers are familiar with.
|
SideStore is a just regular, sandboxed iOS application. The AltStore app target contains the vast majority of SideStore's functionality, including all the logic for downloading and updating apps through SideStore. SideStore makes heavy use of standard iOS frameworks and technologies most iOS developers are familiar with.
|
||||||
|
|
||||||
### EM Proxy
|
### EM Proxy
|
||||||
[SideServer mobile](https://github.com/jkcoxson/em_proxy) powers the defining feature of SideStore: untethered app installation. By leveraging an App Store app with additional entitlements (WireGuard or StosVPN) to create the VPN tunnel for us, it allows SideStore to take advantage of [Jitterbug](https://github.com/osy/Jitterbug)'s loopback method without requiring a paid developer account.
|
[SideServer mobile](https://github.com/jkcoxson/em_proxy) powers the defining feature of SideStore: untethered app installation. By levaraging an App Store app with additional entitlements (WireGuard) to create the VPN tunnel for us, it allows SideStore to take advantage of [Jitterbug](https://github.com/osy/Jitterbug)'s loopback method without requiring a paid developer account.
|
||||||
|
|
||||||
### Minimuxer
|
### Minimuxer
|
||||||
[Minimuxer](https://github.com/jkcoxson/minimuxer) is a lockdown muxer that can run inside iOS’s sandbox. It replicates Apple’s usbmuxd protocol on MacOS to “discover” devices to interface with wireguard On-Device.
|
[Minimuxer](https://github.com/jkcoxson/minimuxer) is a lockdown muxer that can run inside iOS’s sandbox. It replicates Apple’s usbmuxd protocol on MacOS to “discover” devices to interface with wireguard On-Device.
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// BuildInfo.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 23/03/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public class BuildInfo{
|
|
||||||
private static let MARKETING_VERSION_TAG = "CFBundleShortVersionString"
|
|
||||||
private static let CURRENT_PROJECT_VERSION_TAG = kCFBundleVersionKey as String
|
|
||||||
|
|
||||||
private static let XCODE_VERSION_TAG = "DTXcode"
|
|
||||||
private static let XCODE_REVISION_TAG = "DTXcodeBuild"
|
|
||||||
|
|
||||||
let bundle: Bundle
|
|
||||||
|
|
||||||
public init(){
|
|
||||||
bundle = Bundle.main
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BundleError: Swift.Error {
|
|
||||||
case invalidURL
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(url: URL) throws {
|
|
||||||
guard let bundle = Bundle(url: url) else {
|
|
||||||
throw BundleError.invalidURL
|
|
||||||
}
|
|
||||||
self.bundle = bundle
|
|
||||||
}
|
|
||||||
|
|
||||||
public lazy var project_version: String? = {
|
|
||||||
let version = bundle.object(forInfoDictionaryKey: Self.CURRENT_PROJECT_VERSION_TAG) as? String
|
|
||||||
return version
|
|
||||||
}()
|
|
||||||
|
|
||||||
public lazy var marketing_version: String? = {
|
|
||||||
let version = bundle.object(forInfoDictionaryKey: Self.MARKETING_VERSION_TAG) as? String
|
|
||||||
return version
|
|
||||||
}()
|
|
||||||
|
|
||||||
public lazy var xcode: String? = {
|
|
||||||
let xcode = bundle.object(forInfoDictionaryKey: Self.XCODE_VERSION_TAG) as? String
|
|
||||||
return xcode
|
|
||||||
}()
|
|
||||||
|
|
||||||
public lazy var xcode_revision: String? = {
|
|
||||||
let revision = bundle.object(forInfoDictionaryKey: Self.XCODE_REVISION_TAG) as? String
|
|
||||||
return revision
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
//
|
|
||||||
// CollapsingMarkdownView.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Magesh K on 27/02/25.
|
|
||||||
// Copyright © 2025 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import MarkdownKit
|
|
||||||
|
|
||||||
struct MarkdownManager
|
|
||||||
{
|
|
||||||
struct Fonts{
|
|
||||||
static let body: UIFont = .systemFont(ofSize: UIFont.systemFontSize)
|
|
||||||
// static let body: UIFont = .systemFont(ofSize: UIFont.labelFontSize)
|
|
||||||
|
|
||||||
static let header: UIFont = .boldSystemFont(ofSize: 14)
|
|
||||||
static let list: UIFont = .systemFont(ofSize: 14)
|
|
||||||
static let bold: UIFont = .boldSystemFont(ofSize: 14)
|
|
||||||
static let italic: UIFont = .italicSystemFont(ofSize: 14)
|
|
||||||
static let quote: UIFont = .italicSystemFont(ofSize: 14)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Color{
|
|
||||||
static let header = UIColor { traitCollection in
|
|
||||||
traitCollection.userInterfaceStyle == .dark ? UIColor.white : UIColor.black
|
|
||||||
}
|
|
||||||
static let bold = UIColor { traitCollection in
|
|
||||||
traitCollection.userInterfaceStyle == .dark ? UIColor.lightText : UIColor.darkText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var enabledElements: MarkdownParser.EnabledElements {
|
|
||||||
[
|
|
||||||
.header,
|
|
||||||
.list,
|
|
||||||
.quote,
|
|
||||||
.code,
|
|
||||||
.link,
|
|
||||||
.bold,
|
|
||||||
.italic,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
var markdownParser: MarkdownParser {
|
|
||||||
MarkdownParser(
|
|
||||||
font: Self.Fonts.body,
|
|
||||||
color: Self.Color.bold
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final class CollapsingMarkdownView: UIView {
|
|
||||||
/// Called when the collapse state toggles.
|
|
||||||
var didToggleCollapse: (() -> Void)?
|
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
var isCollapsed = true {
|
|
||||||
didSet {
|
|
||||||
guard self.isCollapsed != oldValue else { return }
|
|
||||||
self.updateCollapsedState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var maximumNumberOfLines = 3 {
|
|
||||||
didSet {
|
|
||||||
self.checkIfNeedsCollapsing()
|
|
||||||
self.updateCollapsedState()
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var text: String = "" {
|
|
||||||
didSet {
|
|
||||||
self.updateMarkdownContent()
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lineSpacing: Double = 2 {
|
|
||||||
didSet {
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let toggleButton = UIButton(type: .system)
|
|
||||||
|
|
||||||
private let textView = UITextView()
|
|
||||||
private let markdownParser = MarkdownManager().markdownParser
|
|
||||||
|
|
||||||
private var previousSize: CGSize?
|
|
||||||
private var actualLineCount: Int = 0
|
|
||||||
private var needsCollapsing = false
|
|
||||||
|
|
||||||
// MARK: - Initialization
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
super.init(coder: coder)
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func awakeFromNib() {
|
|
||||||
super.awakeFromNib()
|
|
||||||
initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func checkIfNeedsCollapsing() {
|
|
||||||
guard bounds.width > 0, let font = textView.font, font.lineHeight > 0 else {
|
|
||||||
needsCollapsing = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the number of lines in the text
|
|
||||||
let textSize = textView.sizeThatFits(CGSize(width: bounds.width, height: .greatestFiniteMagnitude))
|
|
||||||
let lineHeight = font.lineHeight
|
|
||||||
|
|
||||||
// Safely calculate actual line count
|
|
||||||
actualLineCount = max(1, Int(ceil(textSize.height / lineHeight)))
|
|
||||||
|
|
||||||
// Only needs collapsing if actual lines exceed the maximum
|
|
||||||
needsCollapsing = actualLineCount > maximumNumberOfLines
|
|
||||||
|
|
||||||
// Update button visibility
|
|
||||||
toggleButton.isHidden = !needsCollapsing
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateCollapsedState() {
|
|
||||||
// Disable animations for this update
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
// Update the button title
|
|
||||||
let title = isCollapsed ? NSLocalizedString("More", comment: "") : NSLocalizedString("Less", comment: "")
|
|
||||||
toggleButton.setTitle(title, for: .normal)
|
|
||||||
|
|
||||||
// Set max lines based on collapsed state
|
|
||||||
if isCollapsed && needsCollapsing {
|
|
||||||
textView.textContainer.maximumNumberOfLines = maximumNumberOfLines
|
|
||||||
} else {
|
|
||||||
textView.textContainer.maximumNumberOfLines = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Button is only visible if content needs collapsing
|
|
||||||
toggleButton.isHidden = !needsCollapsing
|
|
||||||
|
|
||||||
// Force layout updates
|
|
||||||
textView.layoutIfNeeded()
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
self.invalidateIntrinsicContentSize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func initialize() {
|
|
||||||
// Configure text view
|
|
||||||
textView.isEditable = false
|
|
||||||
textView.isScrollEnabled = false
|
|
||||||
textView.textContainerInset = .zero
|
|
||||||
textView.textContainer.lineFragmentPadding = 0
|
|
||||||
textView.textContainer.lineBreakMode = .byTruncatingTail
|
|
||||||
textView.backgroundColor = .clear
|
|
||||||
|
|
||||||
// Make textView selectable to enable link interactions
|
|
||||||
textView.isSelectable = true
|
|
||||||
textView.delegate = self
|
|
||||||
|
|
||||||
// Important: This prevents selection handles from appearing
|
|
||||||
textView.dataDetectorTypes = .link
|
|
||||||
|
|
||||||
// Configure markdown parser
|
|
||||||
configureMarkdownParser()
|
|
||||||
|
|
||||||
// Add subviews
|
|
||||||
addSubview(textView)
|
|
||||||
|
|
||||||
// Configure toggle button
|
|
||||||
toggleButton.addTarget(self, action: #selector(toggleCollapsed(_:)), for: .primaryActionTriggered)
|
|
||||||
addSubview(toggleButton)
|
|
||||||
|
|
||||||
setNeedsLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func configureMarkdownParser() {
|
|
||||||
// Configure markdown parser with desired settings
|
|
||||||
markdownParser.enabledElements = MarkdownManager.enabledElements
|
|
||||||
|
|
||||||
// You can also customize the styling if needed
|
|
||||||
markdownParser.header.font = MarkdownManager.Fonts.header
|
|
||||||
markdownParser.list.font = MarkdownManager.Fonts.list
|
|
||||||
markdownParser.bold.font = MarkdownManager.Fonts.bold
|
|
||||||
markdownParser.italic.font = MarkdownManager.Fonts.italic
|
|
||||||
markdownParser.quote.font = MarkdownManager.Fonts.quote
|
|
||||||
|
|
||||||
markdownParser.header.color = MarkdownManager.Color.header
|
|
||||||
markdownParser.bold.color = MarkdownManager.Color.bold
|
|
||||||
markdownParser.list.color = MarkdownManager.Color.bold
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Layout
|
|
||||||
override func layoutSubviews() {
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
// Calculate button height (for spacing)
|
|
||||||
let buttonHeight = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000)).height
|
|
||||||
|
|
||||||
// Set textView frame to leave space for button
|
|
||||||
textView.frame = CGRect(
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: bounds.width,
|
|
||||||
height: bounds.height - buttonHeight
|
|
||||||
)
|
|
||||||
|
|
||||||
// Check if layout changed
|
|
||||||
if previousSize?.width != bounds.width {
|
|
||||||
checkIfNeedsCollapsing()
|
|
||||||
updateCollapsedState()
|
|
||||||
previousSize = bounds.size
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position toggle button at bottom right
|
|
||||||
let buttonSize = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
|
||||||
toggleButton.frame = CGRect(
|
|
||||||
x: bounds.width - buttonSize.width,
|
|
||||||
y: textView.frame.maxY,
|
|
||||||
width: buttonSize.width,
|
|
||||||
height: buttonHeight
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func toggleCollapsed(_ sender: UIButton) {
|
|
||||||
isCollapsed.toggle()
|
|
||||||
didToggleCollapse?()
|
|
||||||
}
|
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
|
||||||
guard bounds.width > 0, let font = textView.font, font.lineHeight > 0 else {
|
|
||||||
return CGSize(width: UIView.noIntrinsicMetric, height: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
let lineHeight = font.lineHeight
|
|
||||||
let buttonHeight = toggleButton.sizeThatFits(CGSize(width: 1000, height: 1000)).height
|
|
||||||
|
|
||||||
// Always add button height to reserve space for it
|
|
||||||
if isCollapsed && needsCollapsing {
|
|
||||||
// When collapsed and needs collapsing, use maximumNumberOfLines
|
|
||||||
let collapsedHeight = lineHeight * CGFloat(maximumNumberOfLines) +
|
|
||||||
lineSpacing * CGFloat(max(0, maximumNumberOfLines - 1))
|
|
||||||
return CGSize(width: UIView.noIntrinsicMetric, height: collapsedHeight + buttonHeight)
|
|
||||||
} else if !needsCollapsing {
|
|
||||||
// Text is shorter than max lines - use actual text height
|
|
||||||
let textSize = textView.sizeThatFits(CGSize(width: bounds.width, height: .greatestFiniteMagnitude))
|
|
||||||
return CGSize(width: UIView.noIntrinsicMetric, height: textSize.height + buttonHeight)
|
|
||||||
} else {
|
|
||||||
// When expanded and needs collapsing, use full text height plus button
|
|
||||||
let textSize = textView.sizeThatFits(CGSize(width: bounds.width, height: .greatestFiniteMagnitude))
|
|
||||||
return CGSize(width: UIView.noIntrinsicMetric, height: textSize.height + buttonHeight)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Markdown Processing
|
|
||||||
private func updateMarkdownContent() {
|
|
||||||
let attributedString = markdownParser.parse(text)
|
|
||||||
|
|
||||||
// Apply line spacing
|
|
||||||
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
|
|
||||||
let paragraphStyle = NSMutableParagraphStyle()
|
|
||||||
paragraphStyle.lineSpacing = lineSpacing
|
|
||||||
|
|
||||||
mutableAttributedString.addAttribute(
|
|
||||||
.paragraphStyle,
|
|
||||||
value: paragraphStyle,
|
|
||||||
range: NSRange(location: 0, length: mutableAttributedString.length)
|
|
||||||
)
|
|
||||||
|
|
||||||
textView.attributedText = mutableAttributedString
|
|
||||||
|
|
||||||
// Check if content needs collapsing after setting text
|
|
||||||
checkIfNeedsCollapsing()
|
|
||||||
updateCollapsedState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CollapsingMarkdownView: UITextViewDelegate {
|
|
||||||
// This enables tapping on links while preventing text selection
|
|
||||||
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
|
|
||||||
// Open the URL using UIApplication
|
|
||||||
UIApplication.shared.open(URL)
|
|
||||||
return false // Return false to prevent the default behavior
|
|
||||||
}
|
|
||||||
|
|
||||||
// This prevents text selection
|
|
||||||
func textViewDidChangeSelection(_ textView: UITextView) {
|
|
||||||
textView.selectedTextRange = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
IGNORED_AUTHORS = [
|
IGNORED_AUTHORS = [
|
||||||
|
"=", # probably someone used an equalTo ?! # anyway we are ignoring it!
|
||||||
]
|
]
|
||||||
|
|
||||||
TAG_MARKER = "###"
|
TAG_MARKER = "###"
|
||||||
@@ -82,54 +82,10 @@ def format_commit_message(msg):
|
|||||||
msg_clean = msg_clean[1:].strip() # remove leading '-' and spaces
|
msg_clean = msg_clean[1:].strip() # remove leading '-' and spaces
|
||||||
return f"- {msg_clean}"
|
return f"- {msg_clean}"
|
||||||
|
|
||||||
# def generate_release_notes(last_successful, tag, branch):
|
|
||||||
"""Generate release notes for the given tag."""
|
|
||||||
current_commit = get_head_commit()
|
|
||||||
messages = get_commit_messages(last_successful, current_commit)
|
|
||||||
|
|
||||||
# Start with the tag header
|
|
||||||
new_section = f"{TAG_MARKER} {tag}\n"
|
|
||||||
|
|
||||||
# What's Changed section (always present)
|
|
||||||
new_section += f"{HEADER_MARKER} What's Changed\n"
|
|
||||||
|
|
||||||
if not messages or last_successful == current_commit:
|
|
||||||
new_section += "- Nothing...\n"
|
|
||||||
else:
|
|
||||||
for msg in messages:
|
|
||||||
new_section += f"{format_commit_message(msg)}\n"
|
|
||||||
|
|
||||||
# New Contributors section (only if there are new contributors)
|
|
||||||
all_previous_authors = get_authors_in_range(f"{branch}")
|
|
||||||
recent_authors = get_authors_in_range(f"{last_successful}..{current_commit}")
|
|
||||||
new_contributors = recent_authors - all_previous_authors
|
|
||||||
|
|
||||||
if new_contributors:
|
|
||||||
new_section += f"\n{HEADER_MARKER} New Contributors\n"
|
|
||||||
for author in sorted(new_contributors):
|
|
||||||
new_section += f"- {format_contributor(author)} made their first contribution\n"
|
|
||||||
|
|
||||||
# Full Changelog section (only if there are changes)
|
|
||||||
if messages and last_successful != current_commit:
|
|
||||||
repo_url = get_repo_url()
|
|
||||||
changelog_link = f"{repo_url}/compare/{last_successful}...{current_commit}"
|
|
||||||
new_section += f"\n{HEADER_MARKER} Full Changelog: [{last_successful[:8]}...{current_commit[:8]}]({changelog_link})\n"
|
|
||||||
|
|
||||||
return new_section
|
|
||||||
|
|
||||||
def generate_release_notes(last_successful, tag, branch):
|
def generate_release_notes(last_successful, tag, branch):
|
||||||
"""Generate release notes for the given tag."""
|
"""Generate release notes for the given tag."""
|
||||||
current_commit = get_head_commit()
|
current_commit = get_head_commit()
|
||||||
try:
|
|
||||||
# Try to get commit messages using the provided last_successful commit
|
|
||||||
messages = get_commit_messages(last_successful, current_commit)
|
messages = get_commit_messages(last_successful, current_commit)
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
# If the range is invalid (e.g. force push made last_successful obsolete),
|
|
||||||
# fall back to using the last 10 commits in the current branch.
|
|
||||||
print("\nInvalid revision range error, using last 10 commits as fallback.\n")
|
|
||||||
fallback_commit = run_command("git rev-parse HEAD~5")
|
|
||||||
messages = get_commit_messages(fallback_commit, current_commit)
|
|
||||||
last_successful = fallback_commit
|
|
||||||
|
|
||||||
# Start with the tag header
|
# Start with the tag header
|
||||||
new_section = f"{TAG_MARKER} {tag}\n"
|
new_section = f"{TAG_MARKER} {tag}\n"
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#include "../Build.xcconfig"
|
|
||||||
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = $(PRODUCT_BUNDLE_IDENTIFIER).DataStructureTests
|
|
||||||
Reference in New Issue
Block a user