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: 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 current build as BETA run: | echo "RELEASE_CHANNEL=beta" >> $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: Checkout SideStore/beta-build-num uses: actions/checkout@v4 with: 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: 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-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 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 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 # 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 nightly release uses: IsaacShelton/update-existing-release@v1.3.1 with: token: ${{ secrets.GITHUB_TOKEN }} release: "Nightly" tag: "nightly" prerelease: true files: SideStore.ipa SideStore.dSYMs.zip encrypted-build_log.zip body: | This is an ⚠️ **EXPERIMENTAL** ⚠️ nightly build for commit [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}). Nightly 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 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: 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 # Check if PUBLISH_BETA_UPDATES secret is set to non-zero - name: Check if PUBLISH_BETA_UPDATES is set id: check_publish run: | echo "PUBLISH_BETA_UPDATES=${{ vars.PUBLISH_BETA_UPDATES }}" if [[ "${{ vars.PUBLISH_BETA_UPDATES }}" == "__YES__" ]]; then echo "PUBLISH_BETA_UPDATES is not set. Skipping deployment." echo "should_deploy=true" >> $GITHUB_OUTPUT else echo "PUBLISH_BETA_UPDATES is set. Proceeding with deployment." echo "should_deploy=false" >> $GITHUB_OUTPUT fi - name: Get formatted date 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.outputs.should_deploy == 'true' 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 if: steps.check_publish.outputs.should_deploy == 'true' 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 environment variables dynamically if: steps.check_publish.outputs.should_deploy == 'true' run: | LOCALIZED_DESCRIPTION=$(cat <> $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/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.outputs.should_deploy == 'true' uses: actions/checkout@v4 with: # Repository name with owner. For example, actions/checkout # Default: ${{ github.repository }} repository: 'SideStore/apps-v2.json' 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.CROSS_REPO_PUSH_KEY }} path: 'SideStore/apps-v2.json' - name: Publish to SideStore/apps-v2.json 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 ./_includes/source.json git commit -m " - updated for $SHORT_COMMIT deployment" || echo "No changes to commit" 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