diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 4809cb27..a4591c9e 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -2,283 +2,25 @@ name: Alpha SideStore build on: push: branches: - # - alpha - - rebase-2.0-wip + - develop-alpha + +# cancel duplicate run if from same branch +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true jobs: - build: - name: Build and upload SideStore Alpha releases - concurrency: - group: ${{ github.ref }} - cancel-in-progress: true - strategy: - fail-fast: false - matrix: - include: - - os: 'macos-14' - version: '16.1' - - runs-on: ${{ matrix.os }} - steps: - - - name: Set current build as ALPHA - run: echo "IS_ALPHA=1" >> $GITHUB_ENV - - - name: Checkout code - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install dependencies - run: brew install ldid - - - name: Install xcbeautify - run: brew install xcbeautify - - - name: Cache .alpha-build-num - uses: actions/cache@v4 - with: - path: .alpha-build-num - key: alpha-build-num - - - name: Get version - id: version-marketing - run: echo "VERSION_IPA=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_ENV - - - name: Increase alpha build number and set as version - run: bash .github/workflows/increase-alpha-build-num.sh - - - name: Get version - id: version - run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT - - - name: Echo version - run: echo "${{ steps.version.outputs.version }}" - - - name: Setup Xcode - uses: maxim-lobanov/setup-xcode@v1.6.0 - with: - xcode-version: ${{ matrix.version }} - - - name: Cache Build - uses: irgaly/xcode-cache@v1 - with: - key: xcode-cache-deriveddata-${{ github.sha }} - restore-keys: xcode-cache-deriveddata- - swiftpm-cache-key: xcode-cache-sourcedata-${{ github.sha }} - swiftpm-cache-restore-keys: | - xcode-cache-sourcedata- - - - - name: Restore Pods from Cache (Exact match) - id: pods-restore - uses: actions/cache/restore@v3 - with: - path: | - ./Podfile.lock - ./Pods/ - ./AltStore.xcworkspace/ - key: pods-cache-${{ hashFiles('Podfile') }} - # restore-keys: | # commented out to strictly check cache for this particular podfile - # pods-cache- - - - name: 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- - - - name: Install CocoaPods - # if: ${{ steps.pods-restore.outputs.cache-hit != 'true'}} - id: pods-install - run: | - pod install - - - name: 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-${{ hashFiles('Podfile') }} - - - name: List Files and derived data - 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 - run: NSUnbufferedIO=YES make build 2>&1 | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]} - - - name: Fakesign app - run: make fakesign - - - name: Convert to IPA - run: make ipa - - - name: Get current date - id: date - run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT - - - name: Get current date in AltStore date form - id: date_altstore - run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - - name: Create dSYMs zip - run: zip -r -9 ./SideStore.dSYMs.zip ./SideStore.xcarchive/dSYMs/* - - - name: Upload to alpha release - uses: IsaacShelton/update-existing-release@v1.3.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - release: "Alpha" - tag: "alpha" - prerelease: true - files: SideStore.ipa SideStore.dSYMs.zip - body: | - This is an ⚠️ **EXPERIMENTAL** ⚠️ alpha build for commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}). - - Alpha builds are **extremely experimental builds only meant to be used by developers and alpha 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 Stable](https://github.com/${{ github.repository }}/releases?q=stable). - - ## Build Info - - Built at (UTC): `${{ steps.date.outputs.date }}` - Built at (UTC date): `${{ steps.date_altstore.outputs.date }}` - Commit SHA: `${{ github.sha }}` - Version: `${{ steps.version.outputs.version }}` - - - name: Add version to IPA file name - run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa - - - name: Upload SideStore.ipa Artifact - uses: actions/upload-artifact@v4 - with: - name: SideStore-${{ steps.version.outputs.version }}.ipa - path: SideStore-${{ steps.version.outputs.version }}.ipa - - - name: Upload *.dSYM Artifact - uses: actions/upload-artifact@v4 - with: - name: SideStore-${{ steps.version.outputs.version }}-dSYM - path: ./SideStore.xcarchive/dSYMs/* - - # Check if PUBLISH_ALPHA_UPDATES secret is set to non-zero - - name: Check if PUBLISH_ALPHA_UPDATES is set - id: check_publish - run: | - if [[ "${{ secrets.PUBLISH_ALPHA_UPDATES }}" != "__YES__" ]]; then - echo "PUBLISH_ALPHA_UPDATES is not set. Skipping deployment." - exit 1 # Exit with 1 to indicate no deployment - else - echo "PUBLISH_ALPHA_UPDATES is set. Proceeding with deployment." - exit 0 # Exit with 0 to indicate deployment should proceed - fi - continue-on-error: true # Continue even if exit code is 1 - - - name: Get short commit hash - if: ${{ steps.check_publish.outcome == 'success' }} - run: | - # SHORT_COMMIT="${{ github.sha }}" - SHORT_COMMIT=${GITHUB_SHA:0:7} - echo "Short commit hash: $SHORT_COMMIT" - echo "SHORT_COMMIT=$SHORT_COMMIT" >> $GITHUB_ENV - - - name: Get formatted date - if: ${{ steps.check_publish.outcome == 'success' }} - run: | - FORMATTED_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") - echo "Formatted date: $FORMATTED_DATE" - echo "FORMATTED_DATE=$FORMATTED_DATE" >> $GITHUB_ENV - - - name: Get size of IPA in bytes (macOS/Linux) - if: ${{ steps.check_publish.outcome == 'success' }} - run: | - if [[ "$(uname)" == "Darwin" ]]; then - # macOS - IPA_SIZE=$(stat -f %z SideStore-${{ steps.version.outputs.version }}.ipa) - else - # Linux - IPA_SIZE=$(stat -c %s SideStore-${{ steps.version.outputs.version }}.ipa) - fi - echo "IPA size in bytes: $IPA_SIZE" - echo "IPA_SIZE=$IPA_SIZE" >> $GITHUB_ENV - - - name: Compute SHA-256 of IPA - if: ${{ steps.check_publish.outcome == 'success' }} - run: | - SHA256_HASH=$(shasum -a 256 SideStore-${{ steps.version.outputs.version }}.ipa | awk '{ print $1 }') - echo "SHA-256 Hash: $SHA256_HASH" - echo "SHA256_HASH=$SHA256_HASH" >> $GITHUB_ENV - - - name: Set environment variables dynamically - if: ${{ steps.check_publish.outcome == 'success' }} - run: | - echo "VERSION_IPA=$VERSION_IPA" >> $GITHUB_ENV - echo "VERSION_DATE=$FORMATTED_DATE" >> $GITHUB_ENV - echo "BETA=true" >> $GITHUB_ENV - echo "COMMIT_ID=$SHORT_COMMIT" >> $GITHUB_ENV - echo "SIZE=$IPA_SIZE" >> $GITHUB_ENV - echo "SHA256=$SHA256_HASH" >> $GITHUB_ENV - echo "LOCALIZED_DESCRIPTION=This is alpha release for revision: ${{ github.sha }}" >> $GITHUB_ENV - echo "DOWNLOAD_URL=https://github.com/SideStore/SideStore/releases/download/alpha/SideStore.ipa" >> $GITHUB_ENV - - - name: Checkout SideStore/apps-v2.json - if: ${{ steps.check_publish.outcome == 'success' }} - uses: actions/checkout@v4 - with: - # Repository name with owner. For example, actions/checkout - # Default: ${{ github.repository }} - repository: 'SideStore/apps-v2.json' - ref: 'main' - # token: ${{ github.token }} - token: ${{ secrets.APPS_DEPLOY_KEY }} - path: 'SideStore/apps-v2.json' - - - name: Publish to SideStore/apps-v2.json - if: ${{ steps.check_publish.outcome == 'success' }} - 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" - - # Make the update script executable and run it - python3 ../../update_apps.py "./_includes/source.json" - - # Commit changes and push using SSH - git add ./_includes/source.json - git commit -m " - updated for $SHORT_COMMIT deployment" || echo "No changes to commit" - - git status - git push origin HEAD:main - popd + Reuseable-build: + uses: ./.github/workflows/reusable-build-workflow.yml + with: + bundle_id: "com.SideStore.SideStore.Alpha" + is_beta: true + publish: true + is_shared_build_num: false + release_tag: "alpha" + release_name: "Alpha" + upstream_tag: "nightly" + upstream_name: "Nightly" + secrets: + CROSS_REPO_PUSH_KEY: ${{ secrets.CROSS_REPO_PUSH_KEY }} + BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }} \ No newline at end of file diff --git a/.github/workflows/increase-alpha-build-num.sh b/.github/workflows/increase-alpha-build-num.sh deleted file mode 100644 index 46963873..00000000 --- a/.github/workflows/increase-alpha-build-num.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -# Ensure we are in root directory -cd "$(dirname "$0")/../.." - -DATE=`date -u +'%Y.%m.%d'` -BUILD_NUM=1 - -write() { - sed -e "/MARKETING_VERSION = .*/s/$/-alpha.$DATE.$BUILD_NUM+$(git rev-parse --short HEAD)/" -i '' Build.xcconfig - echo "$DATE,$BUILD_NUM" > .alpha-build-num -} - -if [ ! -f ".alpha-build-num" ]; then - write - exit 0 -fi - -LAST_DATE=`cat .alpha-build-num | perl -n -e '/([^,]*),([^ ]*)$/ && print $1'` -LAST_BUILD_NUM=`cat .alpha-build-num | perl -n -e '/([^,]*),([^ ]*)$/ && print $2'` - -if [[ "$DATE" != "$LAST_DATE" ]]; then - write -else - BUILD_NUM=`expr $LAST_BUILD_NUM + 1` - write -fi - diff --git a/.github/workflows/increase-beta-build-num.sh b/.github/workflows/increase-beta-build-num.sh new file mode 100755 index 00000000..2e24ef20 --- /dev/null +++ b/.github/workflows/increase-beta-build-num.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +# Ensure we are in root directory +cd "$(dirname "$0")/../.." + +DATE=`date -u +'%Y.%m.%d'` +BUILD_NUM=1 + +# Use RELEASE_CHANNEL from the environment variable or default to "beta" +RELEASE_CHANNEL=${RELEASE_CHANNEL:-"beta"} + +write() { + sed -e "/MARKETING_VERSION = .*/s/$/-$RELEASE_CHANNEL.$DATE.$BUILD_NUM+$(git rev-parse --short HEAD)/" -i '' Build.xcconfig + echo "$DATE,$BUILD_NUM" > build_number.txt +} + +if [ ! -f "build_number.txt" ]; then + write + exit 0 +fi + +LAST_DATE=`cat build_number.txt | perl -n -e '/([^,]*),([^ ]*)$/ && print $1'` +LAST_BUILD_NUM=`cat build_number.txt | perl -n -e '/([^,]*),([^ ]*)$/ && print $2'` + +# if [[ "$DATE" != "$LAST_DATE" ]]; then +# write +# else +# BUILD_NUM=`expr $LAST_BUILD_NUM + 1` +# write +# fi + +# Build number is always incremental +BUILD_NUM=`expr $LAST_BUILD_NUM + 1` +write diff --git a/.github/workflows/increase-nightly-build-num.sh b/.github/workflows/increase-nightly-build-num.sh deleted file mode 100644 index 4d152d07..00000000 --- a/.github/workflows/increase-nightly-build-num.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -# Ensure we are in root directory -cd "$(dirname "$0")/../.." - -DATE=`date -u +'%Y.%m.%d'` -BUILD_NUM=1 - -write() { - sed -e "/MARKETING_VERSION = .*/s/$/-nightly.$DATE.$BUILD_NUM+$(git rev-parse --short HEAD)/" -i '' Build.xcconfig - echo "$DATE,$BUILD_NUM" > .nightly-build-num -} - -if [ ! -f ".nightly-build-num" ]; then - write - exit 0 -fi - -LAST_DATE=`cat .nightly-build-num | perl -n -e '/([^,]*),([^ ]*)$/ && print $1'` -LAST_BUILD_NUM=`cat .nightly-build-num | perl -n -e '/([^,]*),([^ ]*)$/ && print $2'` - -if [[ "$DATE" != "$LAST_DATE" ]]; then - write -else - BUILD_NUM=`expr $LAST_BUILD_NUM + 1` - write -fi - diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index fee5ac40..9db3387c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,20 +1,71 @@ -name: Nightly SideStore build +name: Nightly SideStore Build on: push: branches: - develop + schedule: + - cron: '0 0 * * *' # Runs every night at midnight UTC + workflow_dispatch: # Allows manual trigger + +# cancel duplicate run if from same branch +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true jobs: + check-changes: + if: github.event_name == 'schedule' + runs-on: ubuntu-latest + outputs: + has_changes: ${{ steps.check.outputs.has_changes }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Ensure full history + + - name: Get last successful workflow run + id: get_last_success + run: | + LAST_SUCCESS=$(gh run list --workflow "Nightly SideStore Build" --json createdAt,conclusion \ + --jq '[.[] | select(.conclusion=="success")][0].createdAt' || echo "") + echo "Last successful run: $LAST_SUCCESS" + echo "last_success=$LAST_SUCCESS" >> $GITHUB_ENV + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Check for new commits since last successful build + id: check + run: | + if [ -n "$LAST_SUCCESS" ]; then + NEW_COMMITS=$(git rev-list --count --since="$LAST_SUCCESS" origin/develop) + else + NEW_COMMITS=1 + fi + echo "Has changes: $NEW_COMMITS" + if [ "$NEW_COMMITS" -gt 0 ]; then + echo "has_changes=true" >> $GITHUB_OUTPUT + else + echo "has_changes=false" >> $GITHUB_OUTPUT + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + LAST_SUCCESS: ${{ env.last_success }} + build: name: Build and upload SideStore Nightly releases + needs: check-changes + if: | + always() && + (github.event_name == 'push' || + (github.event_name == 'schedule' && needs.check-changes.result == 'success' && needs.check-changes.outputs.has_changes == 'true')) concurrency: - group: ${{ github.ref }} - cancel-in-progress: true + group: build-number-increment # serialize for build num cache access strategy: fail-fast: false matrix: include: - - os: 'macos-14' + - os: 'macos-15' version: '16.1' runs-on: ${{ matrix.os }} @@ -22,7 +73,6 @@ jobs: - name: Set current build as BETA run: | - echo "IS_BETA=1" >> $GITHUB_ENV echo "RELEASE_CHANNEL=beta" >> $GITHUB_ENV - name: Checkout code @@ -36,25 +86,60 @@ jobs: - name: Install xcbeautify run: brew install xcbeautify - - name: Cache .nightly-build-num - uses: actions/cache@v4 + - name: Checkout SideStore/beta-build-num + uses: actions/checkout@v4 with: - path: .nightly-build-num - key: nightly-build-num + repository: 'SideStore/beta-build-num' + # ref: 'main' # use this when you want to share the build num with other beta workflows + ref: 'nightly' + token: ${{ secrets.CROSS_REPO_PUSH_KEY }} + path: 'SideStore/beta-build-num' - - name: Get version - id: version-marketing - run: echo "VERSION_IPA=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_ENV + - name: Copy build_number.txt to repo root + run: | + cp SideStore/beta-build-num/build_number.txt . + + - name: Echo Build.xcconfig, build_number.txt + run: | + cat Build.xcconfig + cat build_number.txt - name: Increase nightly build number and set as version - run: bash .github/workflows/increase-nightly-build-num.sh + run: bash .github/workflows/increase-beta-build-num.sh - - name: Get version + - name: Extract MARKETING_VERSION from Build.xcconfig id: version - run: echo "version=$(grep MARKETING_VERSION Build.xcconfig | sed -e "s/MARKETING_VERSION = //g")" >> $GITHUB_OUTPUT + run: | + version=$(grep MARKETING_VERSION Build.xcconfig | sed -e 's/MARKETING_VERSION = //g') + echo "version=$version" >> $GITHUB_OUTPUT + echo "version=$version" - - name: Echo version - run: echo "${{ steps.version.outputs.version }}" + - name: Get short commit hash + run: | + # SHORT_COMMIT="${{ github.sha }}" + SHORT_COMMIT=${GITHUB_SHA:0:7} + echo "Short commit hash: $SHORT_COMMIT" + echo "SHORT_COMMIT=$SHORT_COMMIT" >> $GITHUB_ENV + + - name: Set 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}+${SHORT_COMMIT}" + + echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV + echo "MARKETING_VERSION=$MARKETING_VERSION" + + - name: Echo Updated Build.xcconfig, build_number.txt + run: | + cat Build.xcconfig + cat build_number.txt - name: Setup Xcode uses: maxim-lobanov/setup-xcode@v1.6.0 @@ -203,7 +288,7 @@ jobs: Version: `${{ steps.version.outputs.version }}` - name: Add version to IPA file name - run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa + run: cp SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa - name: Upload SideStore.ipa Artifact uses: actions/upload-artifact@v4 @@ -227,63 +312,68 @@ jobs: - name: Check if PUBLISH_BETA_UPDATES is set id: check_publish run: | - if [[ "${{ secrets.PUBLISH_BETA_UPDATES }}" != "__YES__" ]]; then + echo "PUBLISH_BETA_UPDATES=${{ vars.PUBLISH_BETA_UPDATES }}" + if [[ "${{ vars.PUBLISH_BETA_UPDATES }}" == "__YES__" ]]; then echo "PUBLISH_BETA_UPDATES is not set. Skipping deployment." - exit 1 # Exit with 1 to indicate no deployment + echo "should_deploy=true" >> $GITHUB_OUTPUT else echo "PUBLISH_BETA_UPDATES is set. Proceeding with deployment." - exit 0 # Exit with 0 to indicate deployment should proceed + echo "should_deploy=false" >> $GITHUB_OUTPUT fi - continue-on-error: true # Continue even if exit code is 1 - - - name: Get short commit hash - if: ${{ steps.check_publish.outcome == 'success' }} - run: | - # SHORT_COMMIT="${{ github.sha }}" - SHORT_COMMIT=${GITHUB_SHA:0:7} - echo "Short commit hash: $SHORT_COMMIT" - echo "SHORT_COMMIT=$SHORT_COMMIT" >> $GITHUB_ENV - name: Get formatted date - if: ${{ steps.check_publish.outcome == 'success' }} + if: steps.check_publish.outputs.should_deploy == 'true' run: | FORMATTED_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") echo "Formatted date: $FORMATTED_DATE" echo "FORMATTED_DATE=$FORMATTED_DATE" >> $GITHUB_ENV - name: Get size of IPA in bytes (macOS/Linux) - if: ${{ steps.check_publish.outcome == 'success' }} + if: steps.check_publish.outputs.should_deploy == 'true' run: | if [[ "$(uname)" == "Darwin" ]]; then # macOS - IPA_SIZE=$(stat -f %z SideStore-${{ steps.version.outputs.version }}.ipa) + IPA_SIZE=$(stat -f %z SideStore.ipa) else # Linux - IPA_SIZE=$(stat -c %s SideStore-${{ steps.version.outputs.version }}.ipa) + IPA_SIZE=$(stat -c %s SideStore.ipa) fi echo "IPA size in bytes: $IPA_SIZE" echo "IPA_SIZE=$IPA_SIZE" >> $GITHUB_ENV - name: Compute SHA-256 of IPA - if: ${{ steps.check_publish.outcome == 'success' }} + if: steps.check_publish.outputs.should_deploy == 'true' run: | - SHA256_HASH=$(shasum -a 256 SideStore-${{ steps.version.outputs.version }}.ipa | awk '{ print $1 }') + SHA256_HASH=$(shasum -a 256 SideStore.ipa | awk '{ print $1 }') echo "SHA-256 Hash: $SHA256_HASH" echo "SHA256_HASH=$SHA256_HASH" >> $GITHUB_ENV - name: Set environment variables dynamically - if: ${{ steps.check_publish.outcome == 'success' }} + if: steps.check_publish.outputs.should_deploy == 'true' run: | - echo "VERSION_IPA=$VERSION_IPA" >> $GITHUB_ENV + + LOCALIZED_DESCRIPTION=$(cat <> $GITHUB_ENV echo "VERSION_DATE=$FORMATTED_DATE" >> $GITHUB_ENV - echo "COMMIT_ID=$SHORT_COMMIT" >> $GITHUB_ENV echo "SIZE=$IPA_SIZE" >> $GITHUB_ENV echo "SHA256=$SHA256_HASH" >> $GITHUB_ENV - echo "LOCALIZED_DESCRIPTION=This is nightly release for revision: ${{ github.sha }}" >> $GITHUB_ENV echo "DOWNLOAD_URL=https://github.com/SideStore/SideStore/releases/download/nightly/SideStore.ipa" >> $GITHUB_ENV + # multiline strings + echo "LOCALIZED_DESCRIPTION<> $GITHUB_ENV + echo "$LOCALIZED_DESCRIPTION" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + - name: Checkout SideStore/apps-v2.json - if: ${{ steps.check_publish.outcome == 'success' }} + if: steps.check_publish.outputs.should_deploy == 'true' uses: actions/checkout@v4 with: # Repository name with owner. For example, actions/checkout @@ -292,11 +382,11 @@ jobs: ref: 'main' # TODO: use branches for alpha and beta tracks? so as to avoid push collision? # ref: 'nightly' # TODO: use branches for alpha and beta tracks? so as to avoid push collision? # token: ${{ github.token }} - token: ${{ secrets.APPS_DEPLOY_KEY }} + token: ${{ secrets.CROSS_REPO_PUSH_KEY }} path: 'SideStore/apps-v2.json' - name: Publish to SideStore/apps-v2.json - if: ${{ steps.check_publish.outcome == 'success' }} + id: publish-release run: | # Copy and execute the update script pushd SideStore/apps-v2.json/ @@ -312,6 +402,32 @@ jobs: git add ./_includes/source.json git commit -m " - updated for $SHORT_COMMIT deployment" || echo "No changes to commit" - git status - git push origin HEAD:main + git push --verbose + popd + + - name: Echo Updated Build.xcconfig, build_number.txt + run: | + cat Build.xcconfig + cat build_number.txt + + # save it + - name: Publish to SideStore/beta-build-num + run: | + rm SideStore/beta-build-num/build_number.txt + mv build_number.txt SideStore/beta-build-num/build_number.txt + 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 $RELEASE_CHANNEL - $SHORT_COMMIT deployment" || echo "No changes to commit" + + echo "Performing git pull, to see if any extenal change has been made within our current run duration" + git pull + + echo "Pushing to remote repo" + git push --verbose popd diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4b55503d..535da38d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,10 +1,13 @@ name: Pull Request SideStore build on: pull_request: + # types: [opened, synchronize, reopened, ready_for_review, converted_to_draft] + types: [opened, synchronize, reopened, ready_for_review] jobs: build: name: Build and upload SideStore + if: ${{ github.event.pull_request.draft == false }} strategy: fail-fast: false matrix: diff --git a/.github/workflows/reusable-build-workflow.yml b/.github/workflows/reusable-build-workflow.yml new file mode 100644 index 00000000..ac7901ca --- /dev/null +++ b/.github/workflows/reusable-build-workflow.yml @@ -0,0 +1,407 @@ +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 + + secrets: + # GITHUB_TOKEN: + # required: true + CROSS_REPO_PUSH_KEY: + required: true + BUILD_LOG_ZIP_PASSWORD: + required: false + +jobs: + build: + name: Build and upload SideStore ${{ inputs.release_tag }} releases + concurrency: + group: build-number-increment # serialize for build num cache access + strategy: + fail-fast: false + matrix: + include: + - os: 'macos-15' + version: '16.1' + + runs-on: ${{ matrix.os }} + steps: + + - name: Set beta status + run: echo "IS_BETA=${{ inputs.is_beta }}" >> $GITHUB_ENV + + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - 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 + + - 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 + + - name: Echo Build.xcconfig + run: | + echo "cat Build.xcconfig" + cat Build.xcconfig + + + - name: Increase build number for beta builds + if: ${{ inputs.is_beta }} + run: | + echo "RELEASE_CHANNEL=${{ inputs.release_tag }}" >> $GITHUB_OUTPUT + bash .github/workflows/increase-beta-build-num.sh + + - 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" + + - name: Get short commit hash + run: | + # SHORT_COMMIT="${{ github.sha }}" + SHORT_COMMIT=${GITHUB_SHA:0:7} + echo "Short commit hash: $SHORT_COMMIT" + echo "SHORT_COMMIT=$SHORT_COMMIT" >> $GITHUB_ENV + + - name: Set MARKETING_VERSION + if: ${{ inputs.is_beta }} + 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}+${SHORT_COMMIT}" + + echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV + echo "MARKETING_VERSION=$MARKETING_VERSION" + + - name: Echo Updated Build.xcconfig, build_number.txt + if: ${{ inputs.is_beta }} + run: | + cat Build.xcconfig + cat build_number.txt + + - name: Setup Xcode + uses: maxim-lobanov/setup-xcode@v1.6.0 + with: + xcode-version: ${{ matrix.version }} + + - name: Cache Build + uses: irgaly/xcode-cache@v1 + with: + key: xcode-cache-deriveddata-${{ github.sha }} + restore-keys: xcode-cache-deriveddata- + swiftpm-cache-key: xcode-cache-sourcedata-${{ github.sha }} + swiftpm-cache-restore-keys: | + xcode-cache-sourcedata- + + - name: Restore Pods from Cache (Exact match) + id: pods-restore + uses: actions/cache/restore@v3 + with: + path: | + ./Podfile.lock + ./Pods/ + ./AltStore.xcworkspace/ + key: pods-cache-${{ hashFiles('Podfile') }} + # restore-keys: | # commented out to strictly check cache for this particular podfile + # pods-cache- + + - name: 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- + + + - name: Install CocoaPods + run: pod install + + - name: 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-${{ hashFiles('Podfile') }} + + - name: List Files and derived data + 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 + # using 'tee' to intercept stdout and log for detailed build-log + run: | + NSUnbufferedIO=YES make build 2>&1 | tee build.log | xcbeautify --renderer github-actions && exit ${PIPESTATUS[0]} + + - name: Fakesign app + run: make fakesign | tee -a build.log + + - name: Convert to IPA + run: make ipa | tee -a build.log + + - name: Encrypt build.log generated from SideStore build for upload + 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 + + if [ ! -f build.log ]; then + echo "Warning: build.log is missing, creating a dummy log..." + echo "Error: build.log was missing, This is a dummy placeholder file..." > build.log + fi + + zip -e -P "$BUILD_LOG_ZIP_PASSWORD" encrypted-build_log.zip build.log + + - name: List Files after SideStore build + run: | + echo ">>>>>>>>> Workdir <<<<<<<<<<" + ls -la . + echo "" + + - name: Get current date + id: date + run: echo "date=$(date -u +'%c')" >> $GITHUB_OUTPUT + + - name: Get current date in AltStore date form + id: date_altstore + run: echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT + + - name: Create dSYMs zip + run: zip -r -9 ./SideStore.dSYMs.zip ./SideStore.xcarchive/dSYMs/* + + - 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_log.zip + 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 alpha 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.upstrea_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: `${{ steps.version.outputs.version }}` + + # save it + - name: Publish to SideStore/beta-build-num + if: ${{ inputs.is_beta }} + run: | + rm SideStore/beta-build-num/build_number.txt + mv build_number.txt SideStore/beta-build-num/build_number.txt + 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 }} - $SHORT_COMMIT deployment" || echo "No changes to commit" + + echo "Pushing to remote repo" + git push --verbose + popd + + - name: Add version to IPA file name + run: cp 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: ./SideStore.xcarchive/dSYMs/* + + - name: Upload encrypted-build_log.zip + uses: actions/upload-artifact@v4 + with: + name: encrypted-build_log.zip + path: encrypted-build_log.zip + + - 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 + + - 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 + + - 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 + + - name: Set Release Info variables + run: | + # Format localized description + LOCALIZED_DESCRIPTION=$(cat <> $GITHUB_ENV + echo "VERSION_IPA=$MARKETING_VERSION" >> $GITHUB_ENV + echo "VERSION_DATE=$FORMATTED_DATE" >> $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<> $GITHUB_ENV + echo "$LOCALIZED_DESCRIPTION" >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Check if Publish updates is set + id: check_publish + run: | + echo "Publish updates to source.json = ${{ inputs.publish }}" + + - 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 + 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 $SHORT_COMMIT deployment" || echo "No changes to commit" + + git push --verbose + popd \ No newline at end of file diff --git a/AltBackup/Info.plist b/AltBackup/Info.plist index f3345af1..c169189d 100644 --- a/AltBackup/Info.plist +++ b/AltBackup/Info.plist @@ -35,10 +35,6 @@ - BuildRevision - $(BUILD_REVISION) - BuildChannel - $(BUILD_CHANNEL) CFBundleVersion $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS diff --git a/AltStore.xcodeproj/project.pbxproj b/AltStore.xcodeproj/project.pbxproj index b2cab4ba..9c8125ab 100644 --- a/AltStore.xcodeproj/project.pbxproj +++ b/AltStore.xcodeproj/project.pbxproj @@ -65,8 +65,6 @@ A86315DF2D3EB2DE0048FA40 /* ErrorProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */; }; A868CFE42D31999A002F1201 /* SingletonGenericMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = A868CFE32D319988002F1201 /* SingletonGenericMap.swift */; }; A8696EE42D34512C00E96389 /* RemoveAppExtensionsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */; }; - A888EAD52D401D8F0026F7E3 /* BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888EAD42D401D8A0026F7E3 /* BuildInfo.swift */; }; - A888EAD62D4020770026F7E3 /* BuildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = A888EAD42D401D8A0026F7E3 /* BuildInfo.swift */; }; A88B8C492D35AD3200F53F9D /* OperationsLoggingContolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */; }; A88B8C552D35F1EC00F53F9D /* OperationsLoggingControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */; }; A8945AA62D059B6100D86CBE /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8945AA52D059B6100D86CBE /* Roxas.framework */; }; @@ -90,6 +88,7 @@ A8C6D5182D1EE95B00DF01F1 /* OpenSSL.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A859ED5B2D1EE80D003DCC58 /* OpenSSL.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; A8D484D82D0CD306002C691D /* AltBackup.ipa in Resources */ = {isa = PBXBuildFile; fileRef = A8D484D72D0CD306002C691D /* AltBackup.ipa */; }; A8D49F532D3D2F9400844B92 /* ProcessInfo+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */; }; + A8EA195F2D4982D600DC6322 /* BaseEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EA195E2D4982D600DC6322 /* BaseEntity.swift */; }; A8F838922D048E8F00ED425D /* libEmotionalDamage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19104DB22909C06C00C49C7B /* libEmotionalDamage.a */; }; A8F838932D048E8F00ED425D /* libminimuxer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 191E5FAB290A5D92001A3B7C /* libminimuxer.a */; }; A8F838942D048ECE00ED425D /* libimobiledevice.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BF45872B2298D31600BD7491 /* libimobiledevice.a */; }; @@ -651,7 +650,6 @@ A86315DE2D3EB2D80048FA40 /* ErrorProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorProcessing.swift; sourceTree = ""; }; A868CFE32D319988002F1201 /* SingletonGenericMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingletonGenericMap.swift; sourceTree = ""; }; A8696EE32D34512C00E96389 /* RemoveAppExtensionsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveAppExtensionsOperation.swift; sourceTree = ""; }; - A888EAD42D401D8A0026F7E3 /* BuildInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildInfo.swift; sourceTree = ""; }; A88B8C482D35AD3200F53F9D /* OperationsLoggingContolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingContolView.swift; sourceTree = ""; }; A88B8C542D35F1EC00F53F9D /* OperationsLoggingControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationsLoggingControl.swift; sourceTree = ""; }; A8945AA52D059B6100D86CBE /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -667,6 +665,7 @@ A8C38C372D2084D000E83DBD /* ConsoleLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLogView.swift; sourceTree = ""; }; A8D484D72D0CD306002C691D /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; path = AltBackup.ipa; sourceTree = ""; }; A8D49F522D3D2F9400844B92 /* ProcessInfo+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+AltStore.swift"; sourceTree = ""; }; + A8EA195E2D4982D600DC6322 /* BaseEntity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseEntity.swift; sourceTree = ""; }; A8F66C3C2D04D433009689E6 /* em_proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = em_proxy.h; sourceTree = ""; }; A8F66C602D04D464009689E6 /* minimuxer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = minimuxer.xcodeproj; sourceTree = ""; }; A8FD915B2D046EF100322782 /* ProcessError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessError.swift; sourceTree = ""; }; @@ -1227,14 +1226,6 @@ path = errors; sourceTree = ""; }; - A888EAD32D401D7C0026F7E3 /* buildinfo */ = { - isa = PBXGroup; - children = ( - A888EAD42D401D8A0026F7E3 /* BuildInfo.swift */, - ); - path = buildinfo; - sourceTree = ""; - }; A88B8C532D35F1E800F53F9D /* operations */ = { isa = PBXGroup; children = ( @@ -1299,7 +1290,6 @@ A8C38C1C2D2068D100E83DBD /* Utils */ = { isa = PBXGroup; children = ( - A888EAD32D401D7C0026F7E3 /* buildinfo */, A8AD35572D31BEB2003A28B4 /* datastructures */, A8A853AD2D3050CC00995795 /* pagination */, A8087E712D2D291B002DB21B /* importexport */, @@ -1330,6 +1320,31 @@ path = common; sourceTree = ""; }; + A8EA19602D4982E300DC6322 /* DatabaseManager */ = { + isa = PBXGroup; + children = ( + BF66EECA2501AECA007EE018 /* DatabaseManager.swift */, + D5FD4ECA2A9532960097BEE8 /* DatabaseManager+Async.swift */, + ); + path = DatabaseManager; + sourceTree = ""; + }; + A8EA19612D4982E300DC6322 /* MergePolicies */ = { + isa = PBXGroup; + children = ( + BF66EEC52501AECA007EE018 /* MergePolicy.swift */, + ); + path = MergePolicies; + sourceTree = ""; + }; + A8EA19622D4982FC00DC6322 /* Transformers */ = { + isa = PBXGroup; + children = ( + BF66EEC12501AECA007EE018 /* SecureValueTransformer.swift */, + ); + path = Transformers; + sourceTree = ""; + }; A8F66C072D04C025009689E6 /* SideStore */ = { isa = PBXGroup; children = ( @@ -1665,27 +1680,26 @@ BF66EEAA2501AECA007EE018 /* Model */ = { isa = PBXGroup; children = ( + A8EA19602D4982E300DC6322 /* DatabaseManager */, + A8EA19612D4982E300DC6322 /* MergePolicies */, + A8EA19622D4982FC00DC6322 /* Transformers */, + D557A4862AE88232007D0DCF /* Patreon */, + BF66EEAC2501AECA007EE018 /* Migrations */, + A8EA195E2D4982D600DC6322 /* BaseEntity.swift */, BF66EEB72501AECA007EE018 /* AltStore.xcdatamodeld */, BF66EEC92501AECA007EE018 /* Account.swift */, BF66EEC72501AECA007EE018 /* AppID.swift */, BF66EEC62501AECA007EE018 /* AppPermission.swift */, D5F9821C2AB900060045751F /* AppScreenshot.swift */, D52C08ED28AEC37A006C4AE5 /* AppVersion.swift */, - BF66EECA2501AECA007EE018 /* DatabaseManager.swift */, - D5FD4ECA2A9532960097BEE8 /* DatabaseManager+Async.swift */, BF66EEC02501AECA007EE018 /* InstalledApp.swift */, BF66EECB2501AECA007EE018 /* InstalledExtension.swift */, D58916FD28C7C55C00E39C8B /* LoggedError.swift */, - BF66EEC52501AECA007EE018 /* MergePolicy.swift */, BF66EEBF2501AECA007EE018 /* NewsItem.swift */, - D5CA0C4A280E141900469595 /* ManagedPatron.swift */, BF66EEC32501AECA007EE018 /* RefreshAttempt.swift */, - BF66EEC12501AECA007EE018 /* SecureValueTransformer.swift */, BF66EEAB2501AECA007EE018 /* Source.swift */, BF66EEC42501AECA007EE018 /* StoreApp.swift */, BF66EEC22501AECA007EE018 /* Team.swift */, - D557A4862AE88232007D0DCF /* Patreon */, - BF66EEAC2501AECA007EE018 /* Migrations */, ); path = Model; sourceTree = ""; @@ -1713,21 +1727,21 @@ BF66EEB02501AECA007EE018 /* Mapping Models */ = { isa = PBXGroup; children = ( - BF66EEB12501AECA007EE018 /* AltStoreToAltStore2.xcmappingmodel */, - BF66EEB22501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel */, - BF66EEB32501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel */, - BF66EEB42501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel */, - BF66EEB52501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel */, - BF66EEB62501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel */, - BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */, - D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */, - D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */, - D5927D6829DCE28700D6898E /* AltStore11ToAltStore12.xcmappingmodel */, - D5177B0C2A26944600270065 /* AltStore12ToAltStore13.xcmappingmodel */, - D5185B7F2AE1E51B00646E33 /* AltStore13ToAltStore14.xcmappingmodel */, - D5753A612B279F1900090456 /* AltStore14ToAltStore15.xcmappingmodel */, - D5CE309B2B4C946300DB8151 /* AltStore15ToAltStore16.xcmappingmodel */, D51E83812B8692DF0092FC61 /* AltStore16ToAltStore17.xcmappingmodel */, + D5CE309B2B4C946300DB8151 /* AltStore15ToAltStore16.xcmappingmodel */, + D5753A612B279F1900090456 /* AltStore14ToAltStore15.xcmappingmodel */, + D5185B7F2AE1E51B00646E33 /* AltStore13ToAltStore14.xcmappingmodel */, + D5177B0C2A26944600270065 /* AltStore12ToAltStore13.xcmappingmodel */, + D5927D6829DCE28700D6898E /* AltStore11ToAltStore12.xcmappingmodel */, + D5F99A1728D11DB500476A16 /* AltStore10ToAltStore11.xcmappingmodel */, + D5CA0C4D280E249E00469595 /* AltStore9ToAltStore10.xcmappingmodel */, + BFBF331A2526762200B7B8C9 /* AltStore8ToAltStore9.xcmappingmodel */, + BF66EEB22501AECA007EE018 /* AltStore6ToAltStore7.xcmappingmodel */, + BF66EEB62501AECA007EE018 /* AltStore5ToAltStore6.xcmappingmodel */, + BF66EEB42501AECA007EE018 /* AltStore4ToAltStore5.xcmappingmodel */, + BF66EEB32501AECA007EE018 /* AltStore3ToAltStore4.xcmappingmodel */, + BF66EEB52501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel */, + BF66EEB12501AECA007EE018 /* AltStoreToAltStore2.xcmappingmodel */, ); path = "Mapping Models"; sourceTree = ""; @@ -2234,6 +2248,7 @@ D557A4802AE85BB0007D0DCF /* Pledge.swift */, D557A4842AE88227007D0DCF /* PledgeTier.swift */, D557A4822AE85DB7007D0DCF /* PledgeReward.swift */, + D5CA0C4A280E141900469595 /* ManagedPatron.swift */, ); path = Patreon; sourceTree = ""; @@ -2889,6 +2904,7 @@ buildActionMask = 2147483647; files = ( A8D49F532D3D2F9400844B92 /* ProcessInfo+AltStore.swift in Sources */, + A8EA195F2D4982D600DC6322 /* BaseEntity.swift in Sources */, A82067842D03DC0600645C0D /* OperatingSystemVersion+Comparable.swift in Sources */, D5FB28EE2ADDF89800A1C337 /* KnownSource.swift in Sources */, BF66EED32501AECA007EE018 /* AltStore2ToAltStore3.xcmappingmodel in Sources */, @@ -2922,7 +2938,6 @@ 0E05025C2BEC947000879B5C /* String+SideStore.swift in Sources */, 0EE7FDCB2BE8D12B00D1E390 /* ALTLocalizedError.swift in Sources */, BF66EEA92501AEC5007EE018 /* Tier.swift in Sources */, - A888EAD62D4020770026F7E3 /* BuildInfo.swift in Sources */, BF66EEDB2501AECA007EE018 /* StoreApp.swift in Sources */, D5CE309C2B4C946300DB8151 /* AltStore15ToAltStore16.xcmappingmodel in Sources */, BF66EEDE2501AECA007EE018 /* AppID.swift in Sources */, @@ -3067,7 +3082,6 @@ BFF00D302501BD7D00746320 /* Intents.intentdefinition in Sources */, D5B6F6AB2AD76541007EED5A /* PreviewAppScreenshotsViewController.swift in Sources */, BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */, - A888EAD52D401D8F0026F7E3 /* BuildInfo.swift in Sources */, BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */, BFE00A202503097F00EB4D0C /* INInteraction+AltStore.swift in Sources */, BFDB6A0B22AAEDB7007EA6D6 /* Operation.swift in Sources */, diff --git a/AltStore/App Detail/AppViewController.swift b/AltStore/App Detail/AppViewController.swift index e0e1c930..97200e19 100644 --- a/AltStore/App Detail/AppViewController.swift +++ b/AltStore/App Detail/AppViewController.swift @@ -387,7 +387,8 @@ private extension AppViewController { var buttonAction: AppBannerView.AppAction? - if let installedApp = self.app.installedApp, let latestVersion = self.app.latestAvailableVersion, !installedApp.matches(latestVersion), !self.app.isPledgeRequired || self.app.isPledged + // if let installedApp = self.app.installedApp, let latestVersion = self.app.latestAvailableVersion, !installedApp.matches(latestVersion), !self.app.isPledgeRequired || self.app.isPledged + if let installedApp = self.app.installedApp, installedApp.hasUpdate { // Explicitly set button action to .update if there is an update available, even if it's not supported. buttonAction = .update @@ -537,7 +538,8 @@ extension AppViewController { if let installedApp = self.app.installedApp { - if let latestVersion = self.app.latestAvailableVersion, !installedApp.matches(latestVersion), !self.app.isPledgeRequired || self.app.isPledged + // if let latestVersion = self.app.latestAvailableVersion, !installedApp.matches(latestVersion), !self.app.isPledgeRequired || self.app.isPledged + if let latestVersion = self.app.latestAvailableVersion, installedApp.hasUpdate { self.updateApp(installedApp, to: latestVersion) } diff --git a/AltStore/AppDelegate.swift b/AltStore/AppDelegate.swift index b436b686..107569e3 100644 --- a/AltStore/AppDelegate.swift +++ b/AltStore/AppDelegate.swift @@ -68,7 +68,17 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { // Register default settings before doing anything else. UserDefaults.registerDefaults() - + + // Recreate Database if requested + // NOTE: Userdefaults are local to the SideStore.app sandbox and are not shared + if UserDefaults.standard.recreateDatabaseOnNextStart{ + // reset the state + UserDefaults.standard.recreateDatabaseOnNextStart = false + + // re-create database + DatabaseManager.recreateDatabase() + } + DatabaseManager.shared.start { (error) in if let error = error @@ -99,7 +109,7 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { UserDefaults.standard.preferredServerID = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.serverID) as? String - #if DEBUG && (targetEnvironment(simulator) || BETA) + #if DEBUG && targetEnvironment(simulator) UserDefaults.standard.isDebugModeEnabled = true #endif @@ -424,6 +434,8 @@ private extension AppDelegate try context.save() + + let updatesFetchRequest = InstalledApp.supportedUpdatesFetchRequest() let newsItemsFetchRequest = NewsItem.fetchRequest() as NSFetchRequest diff --git a/AltStore/Browse/BrowseViewController.swift b/AltStore/Browse/BrowseViewController.swift index 2efdaf1e..433a93dd 100644 --- a/AltStore/Browse/BrowseViewController.swift +++ b/AltStore/Browse/BrowseViewController.swift @@ -538,7 +538,8 @@ private extension BrowseViewController let app = self.dataSource.item(at: indexPath) - if let installedApp = app.installedApp, !installedApp.isUpdateAvailable + // if let installedApp = app.installedApp, !installedApp.isUpdateAvailable + if let installedApp = app.installedApp, !installedApp.hasUpdate { self.open(installedApp) } @@ -563,7 +564,8 @@ private extension BrowseViewController } Task(priority: .userInitiated) { @MainActor in - if let installedApp = app.installedApp, installedApp.isUpdateAvailable + // if let installedApp = app.installedApp, installedApp.isUpdateAvailable + if let installedApp = app.installedApp, installedApp.hasUpdate { AppManager.shared.update(installedApp, presentingViewController: self, completionHandler: finish(_:)) } diff --git a/AltStore/Browse/FeaturedViewController.swift b/AltStore/Browse/FeaturedViewController.swift index 015324e5..56db1de3 100644 --- a/AltStore/Browse/FeaturedViewController.swift +++ b/AltStore/Browse/FeaturedViewController.swift @@ -482,7 +482,8 @@ private extension FeaturedViewController let storeApp = self.dataSource.item(at: indexPath) - if let installedApp = storeApp.installedApp, !installedApp.isUpdateAvailable + // if let installedApp = storeApp.installedApp, !installedApp.isUpdateAvailable + if let installedApp = storeApp.installedApp, !installedApp.hasUpdate { self.open(installedApp) } @@ -500,7 +501,8 @@ private extension FeaturedViewController return } - if let installedApp = storeApp.installedApp, installedApp.isUpdateAvailable + // if let installedApp = storeApp.installedApp, installedApp.isUpdateAvailable + if let installedApp = storeApp.installedApp, installedApp.hasUpdate { AppManager.shared.update(installedApp, presentingViewController: self, completionHandler: finish(_:)) } diff --git a/AltStore/Components/AppBannerView.swift b/AltStore/Components/AppBannerView.swift index d37bbd23..04fecb27 100644 --- a/AltStore/Components/AppBannerView.swift +++ b/AltStore/Components/AppBannerView.swift @@ -233,7 +233,8 @@ extension AppBannerView { // App is installed - if installedApp.isUpdateAvailable + // if installedApp.isUpdateAvailable + if installedApp.hasUpdate { buttonAction = .update } diff --git a/AltStore/Info.plist b/AltStore/Info.plist index 031b11d9..59ad9e90 100644 --- a/AltStore/Info.plist +++ b/AltStore/Info.plist @@ -81,10 +81,6 @@ CFBundleVersion $(CURRENT_PROJECT_VERSION) - BuildRevision - $(BUILD_REVISION) - BuildChannel - $(BUILD_CHANNEL) INIntentsSupported RefreshAllIntent diff --git a/AltStore/LaunchViewController.swift b/AltStore/LaunchViewController.swift index d06242c6..a953ddc3 100644 --- a/AltStore/LaunchViewController.swift +++ b/AltStore/LaunchViewController.swift @@ -316,7 +316,12 @@ extension LaunchViewController let errorDesc = ErrorProcessing(.fullError).getDescription(error: error as NSError) print("Failed to update sources on launch. \(errorDesc)") - let toastView = ToastView(error: error, mode: .fullError) + var mode: ToastView.InfoMode = .fullError + if String(describing: error).contains("The Internet connection appears to be offline"){ + mode = .localizedDescription // dont make noise! + } + + let toastView = ToastView(error: error, mode: mode) toastView.addTarget(self.destinationViewController, action: #selector(TabBarController.presentSources), for: .touchUpInside) toastView.show(in: self.destinationViewController.selectedViewController ?? self.destinationViewController) } diff --git a/AltStore/Managing Apps/AppManager.swift b/AltStore/Managing Apps/AppManager.swift index 8081bc26..3728e8c8 100644 --- a/AltStore/Managing Apps/AppManager.swift +++ b/AltStore/Managing Apps/AppManager.swift @@ -1232,16 +1232,12 @@ private extension AppManager else { // Disable the idleTimeout - if !UIApplication.shared.isIdleTimerDisabled { // accept only once if concurrent - DispatchQueue.main.schedule { + DispatchQueue.main.schedule { + if !UIApplication.shared.isIdleTimerDisabled { // accept only once if concurrent UIApplication.shared.isIdleTimerDisabled = UserDefaults.standard.isIdleTimeoutDisableEnabled } } performAppOperations() - // Moved to self.finish() -// DispatchQueue.main.schedule { -// UIApplication.shared.isIdleTimerDisabled = false -// } } return group @@ -2106,8 +2102,8 @@ private extension AppManager // TODO: This should disable for the last finish() request not the first though for batches // probably if we are in batch mode, we can count expected no of finishes() to arrive // and schedule disabling only on last request by matching it with count. - if UIApplication.shared.isIdleTimerDisabled { // accept only once if concurrent - DispatchQueue.main.schedule { + DispatchQueue.main.schedule { + if UIApplication.shared.isIdleTimerDisabled { // accept only once if concurrent UIApplication.shared.isIdleTimerDisabled = false } } diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift index 24dd159c..6f24e3d0 100644 --- a/AltStore/My Apps/MyAppsViewController.swift +++ b/AltStore/My Apps/MyAppsViewController.swift @@ -16,6 +16,7 @@ import AltStoreCore import AltSign import Roxas import minimuxer +import SemanticVersion import Nuke @@ -241,7 +242,18 @@ private extension MyAppsViewController cell.bannerView.button.isIndicatingActivity = false cell.bannerView.configure(for: app, action: .update) - cell.bannerView.subtitleLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), latestSupportedVersion.localizedVersion) + + var versionText = latestSupportedVersion.localizedVersion + + // If the app is SideStore itself, remove the build number to save space + if app.bundleIdentifier == Bundle.Info.appbundleIdentifier, + let version = SemanticVersion(latestSupportedVersion.version) + { + // leave out the build so that it doesnt take up much space + versionText = SemanticVersion(version.major, version.minor, version.patch, version.preRelease).description + } + + cell.bannerView.subtitleLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), versionText) let appName: String @@ -1044,57 +1056,6 @@ private extension MyAppsViewController cell.bannerView.iconImageView.isIndicatingActivity = false } - func removeAppExtensions(from application: ALTApplication, completion: @escaping (Result) -> Void) - { - guard !application.appExtensions.isEmpty else { return completion(.success(())) } - - func removeAppExtensions() throws - { - for appExtension in application.appExtensions - { - try FileManager.default.removeItem(at: appExtension.fileURL) - } - - let scInfoURL = application.fileURL.appendingPathComponent("SC_Info") - let manifestPlistURL = scInfoURL.appendingPathComponent("Manifest.plist") - - if let manifestPlist = NSMutableDictionary(contentsOf: manifestPlistURL), - let sinfReplicationPaths = manifestPlist["SinfReplicationPaths"] as? [String] - { - let replacementPaths = sinfReplicationPaths.filter { !$0.starts(with: "PlugIns/") } // Filter out app extension paths. - manifestPlist["SinfReplicationPaths"] = replacementPaths - try manifestPlist.write(to: manifestPlistURL) - } - } - - let firstSentence: String - - if UserDefaults.standard.activeAppLimitIncludesExtensions - { - firstSentence = NSLocalizedString("Non-developer Apple IDs are limited to 3 active apps and app extensions.", comment: "") - } - else - { - firstSentence = NSLocalizedString("Non-developer Apple IDs are limited to creating 10 App IDs per week.", comment: "") - } - - let message = firstSentence + " " + NSLocalizedString("Would you like to remove this app's extensions so they don't count towards your limit?", comment: "") - - let alertController = UIAlertController(title: NSLocalizedString("App Contains Extensions", comment: ""), message: message, preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: UIAlertAction.cancel.title, style: UIAlertAction.cancel.style, handler: { (action) in - completion(.failure(OperationError.cancelled)) - })) - alertController.addAction(UIAlertAction(title: NSLocalizedString("Keep App Extensions", comment: ""), style: .default) { (action) in - completion(.success(())) - }) - alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove App Extensions", comment: ""), style: .destructive) { (action) in - let result = Result { try removeAppExtensions() } - completion(result) - }) - - self.present(alertController, animated: true, completion: nil) - } - @objc func showHiddenUpdatesAlert(_ sender: UIButton) { guard !self.unsupportedUpdates.isEmpty else { return } @@ -1602,6 +1563,7 @@ private extension MyAppsViewController } catch let error as AppManager.FetchSourcesError { + print(error) try await error.managedObjectContext?.performAsync { try error.managedObjectContext?.save() } @@ -1633,6 +1595,7 @@ private extension MyAppsViewController } catch let error as NSError { + print(error) let toastView = ToastView(error: error.withLocalizedTitle(NSLocalizedString("Unable to Check for Updates", comment: ""))) toastView.addTarget(nil, action: #selector(TabBarController.presentSources), for: .touchUpInside) toastView.show(in: self) diff --git a/AltStore/News/NewsViewController.swift b/AltStore/News/NewsViewController.swift index d3a34f84..b091975e 100644 --- a/AltStore/News/NewsViewController.swift +++ b/AltStore/News/NewsViewController.swift @@ -319,7 +319,8 @@ private extension NewsViewController let app = self.dataSource.item(at: indexPath) guard let storeApp = app.storeApp else { return } - if let installedApp = storeApp.installedApp, !installedApp.isUpdateAvailable + // if let installedApp = storeApp.installedApp, !installedApp.isUpdateAvailable + if let installedApp = storeApp.installedApp, !installedApp.hasUpdate { self.open(installedApp) } @@ -338,7 +339,8 @@ private extension NewsViewController } Task(priority: .userInitiated) { @MainActor in - if let installedApp = storeApp.installedApp, installedApp.isUpdateAvailable + // if let installedApp = storeApp.installedApp, installedApp.isUpdateAvailable + if let installedApp = storeApp.installedApp, installedApp.hasUpdate { AppManager.shared.update(installedApp, presentingViewController: self, completionHandler: finish(_:)) } diff --git a/AltStore/Operations/DownloadAppOperation.swift b/AltStore/Operations/DownloadAppOperation.swift index 45687c5d..7a1890a3 100644 --- a/AltStore/Operations/DownloadAppOperation.swift +++ b/AltStore/Operations/DownloadAppOperation.swift @@ -99,7 +99,8 @@ final class DownloadAppOperation: ResultOperation if let installedApp = storeApp.installedApp { - guard !installedApp.matches(latestSupportedVersion) else { return self.finish(.failure(error)) } + // guard !installedApp.matches(latestSupportedVersion) else { return self.finish(.failure(error)) } + guard installedApp.hasUpdate else { return self.finish(.failure(error)) } } let title = NSLocalizedString("Unsupported iOS Version", comment: "") diff --git a/AltStore/Operations/Errors/VerificationError.swift b/AltStore/Operations/Errors/VerificationError.swift index 635a728d..6b81e664 100644 --- a/AltStore/Operations/Errors/VerificationError.swift +++ b/AltStore/Operations/Errors/VerificationError.swift @@ -45,8 +45,14 @@ extension VerificationError VerificationError(code: .mismatchedHash, app: app, hash: hash, expectedHash: expectedHash) } - static func mismatchedVersion(_ version: String, expectedVersion: String, app: AppProtocol) -> VerificationError { - VerificationError(code: .mismatchedVersion, app: app, version: version, expectedVersion: expectedVersion) + static func mismatchedVersion(version: String, + expectedVersion: String, + app: AppProtocol) -> VerificationError + { + VerificationError(code: .mismatchedVersion, app: app, + version: version, + expectedVersion: expectedVersion + ) } static func mismatchedBuildVersion(_ version: String, expectedVersion: String, app: AppProtocol) -> VerificationError { diff --git a/AltStore/Operations/RemoveAppExtensionsOperation.swift b/AltStore/Operations/RemoveAppExtensionsOperation.swift index f60ce233..7190d216 100644 --- a/AltStore/Operations/RemoveAppExtensionsOperation.swift +++ b/AltStore/Operations/RemoveAppExtensionsOperation.swift @@ -55,8 +55,23 @@ final class RemoveAppExtensionsOperation: ResultOperation try FileManager.default.removeItem(at: appExtension.fileURL) } } - - + + private func updateManifest() throws { + guard let app = context.app else { + return + } + + let scInfoURL = app.fileURL.appendingPathComponent("SC_Info") + let manifestPlistURL = scInfoURL.appendingPathComponent("Manifest.plist") + + if let manifestPlist = NSMutableDictionary(contentsOf: manifestPlistURL), + let sinfReplicationPaths = manifestPlist["SinfReplicationPaths"] as? [String] + { + let replacementPaths = sinfReplicationPaths.filter { !$0.starts(with: "PlugIns/") } // Filter out app extension paths. + manifestPlist["SinfReplicationPaths"] = replacementPaths + try manifestPlist.write(to: manifestPlistURL) + } + } private func removeAppExtensions(from targetAppBundle: ALTApplication, localAppExtensions: Set?, @@ -127,6 +142,7 @@ final class RemoveAppExtensionsOperation: ResultOperation alertController.addAction(UIAlertAction(title: NSLocalizedString("Remove App Extensions", comment: ""), style: .destructive) { (action) in do { try Self.removeExtensions(from: targetAppBundle.appExtensions) + try self.updateManifest() return self.finish(.success(())) } catch { return self.finish(.failure(error)) diff --git a/AltStore/Operations/VerifyAppOperation.swift b/AltStore/Operations/VerifyAppOperation.swift index 969083c2..cf741287 100644 --- a/AltStore/Operations/VerifyAppOperation.swift +++ b/AltStore/Operations/VerifyAppOperation.swift @@ -82,17 +82,16 @@ final class VerifyAppOperation: ResultOperation do { guard let ipaURL = self.context.ipaURL else { throw OperationError.appNotFound(name: app.name) } - - - // TODO: @mahee96: appVersion is instantiated source info as AppVersion incoming from source json - // app is the instantiated ipa downloaded from the specified in the source json in temp dir - // - // For alpha and beta/nightly releases, the CFBundleShortVersionString which is the - // $(MARKETING_VERSION) will be overriden with the commit id before invoking xcode build - // + try await self.verifyHash(of: app, at: ipaURL, matches: appVersion) try await self.verifyDownloadedVersion(of: app, matches: appVersion) - try await self.verifyPermissions(of: app, match: appVersion) + + // process missing permissions check only if the source is V2 or later + if let source = appVersion.app?.source, + source.isSourceAtLeastV2 + { + try await self.verifyPermissions(of: app, match: appVersion) + } self.finish(.success(())) } @@ -129,24 +128,17 @@ private extension VerifyAppOperation { let (version, buildVersion) = await $appVersion.perform { ($0.version, $0.buildVersion) } - let downloadedIpaRevision = Bundle(url: app.fileURL)!.object(forInfoDictionaryKey: "BuildRevision") as? String ?? "" - let sourceJsonIpaRevision = appVersion.revision - - // if not beta but version matches, then accept it, else compare revisions between source and downloaded + // marketplace buildVersion validation + if let buildVersion + { + guard buildVersion == app.buildVersion else { + throw VerificationError.mismatchedBuildVersion(app.buildVersion, expectedVersion: buildVersion, app: app) + } + } + if version != app.version { - throw VerificationError.mismatchedVersion(app.version, expectedVersion: version, app: app) + throw VerificationError.mismatchedVersion(version: app.version, expectedVersion: version, app: app) } - if (appVersion.isBeta && downloadedIpaRevision != sourceJsonIpaRevision) { - let sourceJsonIpaRevision = sourceJsonIpaRevision ?? "nil" - throw VerificationError.mismatchedVersion(app.version + " - " + downloadedIpaRevision, - expectedVersion: version + " - " + sourceJsonIpaRevision, app: app) - } - -// if let buildVersion -// { -// // TODO: @mahee96: requires altsign-marketplace branch release or equivalent -// guard buildVersion == app.buildVersion else { throw VerificationError.mismatchedBuildVersion(app.buildVersion, expectedVersion: buildVersion, app: app) } -// } } func verifyPermissions(of app: ALTApplication, @AsyncManaged match appVersion: AppVersion) async throws diff --git a/AltStore/Settings/OperationsLoggingContolView.swift b/AltStore/Settings/OperationsLoggingContolView.swift index 84680809..2f85516b 100644 --- a/AltStore/Settings/OperationsLoggingContolView.swift +++ b/AltStore/Settings/OperationsLoggingContolView.swift @@ -228,7 +228,7 @@ struct OperationsLoggingControlView: View { CustomToggle("1. Anisette Internal Logging", isOn: Binding( // enable anisette internal logging by default since it was already printing before get: { OperationsLoggingControl.getUpdatedFromDatabase( - for: ANISETTE_VERBOSITY.self, defaultVal: true + for: ANISETTE_VERBOSITY.self, defaultVal: false )}, set: { value in self.viewModel.updateDatabase(for: ANISETTE_VERBOSITY.self, value: value) diff --git a/AltStore/Settings/PatreonComponents.swift b/AltStore/Settings/PatreonComponents.swift index 6a058c14..87bb4d11 100644 --- a/AltStore/Settings/PatreonComponents.swift +++ b/AltStore/Settings/PatreonComponents.swift @@ -76,13 +76,13 @@ final class AboutPatreonHeaderView: UICollectionReusableView self.textView.layer.cornerRadius = 20 self.textView.textContainer.lineFragmentPadding = 0 - for imageView in [self.rileyImageView!, self.shaneImageView!] + for imageView in [self.rileyImageView, self.shaneImageView].compactMap({$0}) { imageView.clipsToBounds = true imageView.layer.cornerRadius = imageView.bounds.midY } - for button in [self.supportButton!, self.accountButton!] + for button in [self.supportButton, self.accountButton].compactMap({$0}) { button.clipsToBounds = true button.layer.cornerRadius = 16 diff --git a/AltStore/Settings/PatreonViewController.swift b/AltStore/Settings/PatreonViewController.swift index dd945161..2a0dbe0a 100644 --- a/AltStore/Settings/PatreonViewController.swift +++ b/AltStore/Settings/PatreonViewController.swift @@ -24,7 +24,7 @@ extension PatreonViewController final class PatreonViewController: UICollectionViewController { - private lazy var dataSource = self.makeDataSource() +// private lazy var dataSource = self.makeDataSource() private lazy var patronsDataSource = self.makePatronsDataSource() private var prototypeAboutHeader: AboutPatreonHeaderView! @@ -40,13 +40,13 @@ final class PatreonViewController: UICollectionViewController let aboutHeaderNib = UINib(nibName: "AboutPatreonHeaderView", bundle: nil) self.prototypeAboutHeader = aboutHeaderNib.instantiate(withOwner: nil, options: nil)[0] as? AboutPatreonHeaderView - self.collectionView.dataSource = self.dataSource +// self.collectionView.dataSource = self.dataSource self.collectionView.register(aboutHeaderNib, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "AboutHeader") self.collectionView.register(PatronsHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "PatronsHeader") - //self.collectionView.register(PatronsFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "PatronsFooter") + self.collectionView.register(PatronsFooterView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "PatronsFooter") - //NotificationCenter.default.addObserver(self, selector: #selector(PatreonViewController.didUpdatePatrons(_:)), name: AppManager.didUpdatePatronsNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PatreonViewController.didUpdatePatrons(_:)), name: AppManager.didUpdatePatronsNotification, object: nil) self.update() } diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard index 7a7baddf..7aec62f4 100644 --- a/AltStore/Settings/Settings.storyboard +++ b/AltStore/Settings/Settings.storyboard @@ -22,7 +22,7 @@ - + - - + + + @@ -286,8 +287,9 @@ - - + + + @@ -463,8 +465,9 @@ - - + + + @@ -497,14 +500,15 @@ - @@ -533,7 +537,7 @@ -