mirror of
https://github.com/SideStore/SideStore.git
synced 2026-03-27 12:55:40 +01:00
Compare commits
29 Commits
cb93168516
...
85ff9b09ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85ff9b09ca | ||
|
|
4100e8b1b9 | ||
|
|
42fae569ca | ||
|
|
aa224f68c7 | ||
|
|
a02d1c49e8 | ||
|
|
226f0dcc6b | ||
|
|
bce38c8743 | ||
|
|
0e72a33af8 | ||
|
|
0677cc287e | ||
|
|
b0bfbf5513 | ||
|
|
ea86b98674 | ||
|
|
3d47d486ef | ||
|
|
3a05485c40 | ||
|
|
31d07534d0 | ||
|
|
99712f0020 | ||
|
|
c5394be883 | ||
|
|
a07657261d | ||
|
|
db00202b37 | ||
|
|
b16dda5590 | ||
|
|
f8c4c558f6 | ||
|
|
ae1bd49a99 | ||
|
|
97b04094eb | ||
|
|
675bdc63ae | ||
|
|
8be9de3b11 | ||
|
|
0403dc3278 | ||
|
|
c546ff6642 | ||
|
|
dc058938ef | ||
|
|
4984e5119f | ||
|
|
bcadc92057 |
194
.github/workflows/alpha.yml
vendored
194
.github/workflows/alpha.yml
vendored
@@ -2,7 +2,8 @@ name: Alpha SideStore Build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [develop-alpha]
|
branches: [alpha]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.ref }}
|
group: ${{ github.ref }}
|
||||||
@@ -10,7 +11,11 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-15
|
runs-on: macos-26
|
||||||
|
env:
|
||||||
|
RELEASE_NAME: Alpha
|
||||||
|
CHANNEL: alpha
|
||||||
|
UPSTREAM_CHANNEL: "nightly"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -20,28 +25,181 @@ jobs:
|
|||||||
|
|
||||||
- run: brew install ldid xcbeautify
|
- run: brew install ldid xcbeautify
|
||||||
|
|
||||||
- name: Shared
|
# --------------------------------------------------
|
||||||
id: shared
|
# runtime env setup
|
||||||
run: python3 scripts/ci/workflow.py shared
|
# --------------------------------------------------
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: "SideStore/beta-build-num"
|
||||||
|
ref: ${{ env.CHANNEL }}
|
||||||
|
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||||
|
path: "Dependencies/beta-build-num"
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Beta bump
|
- name: Setup Env
|
||||||
env:
|
run: |
|
||||||
RELEASE_CHANNEL: alpha
|
BUILD_NUM=$(python3 scripts/ci/workflow.py reserve_build_number 'Dependencies/beta-build-num')
|
||||||
run: python3 scripts/ci/workflow.py bump-beta
|
MARKETING_VERSION=$(python3 scripts/ci/workflow.py get-marketing-version)
|
||||||
|
SHORT_COMMIT=$(python3 scripts/ci/workflow.py commid-id)
|
||||||
|
|
||||||
- name: Version
|
QUALIFIED_VERSION=$(python3 scripts/ci/workflow.py compute-qualified \
|
||||||
id: version
|
"$MARKETING_VERSION" \
|
||||||
run: python3 scripts/ci/workflow.py version
|
"$BUILD_NUM" \
|
||||||
|
"${{ env.CHANNEL }}" \
|
||||||
|
"$SHORT_COMMIT")
|
||||||
|
|
||||||
|
python3 scripts/ci/workflow.py set-marketing-version "$QUALIFIED_VERSION"
|
||||||
|
|
||||||
|
echo "BUILD_NUM=$BUILD_NUM" | tee -a $GITHUB_ENV
|
||||||
|
echo "SHORT_COMMIT=$SHORT_COMMIT" | tee -a $GITHUB_ENV
|
||||||
|
echo "MARKETING_VERSION=$QUALIFIED_VERSION" | tee -a $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup Xcode
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1.6.0
|
||||||
|
with:
|
||||||
|
xcode-version: "26.2"
|
||||||
|
|
||||||
|
- name: Restore Cache (exact)
|
||||||
|
id: xcode-cache-exact
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Developer/Xcode/DerivedData
|
||||||
|
~/Library/Caches/org.swift.swiftpm
|
||||||
|
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Restore Cache (last)
|
||||||
|
if: steps.xcode-cache-exact.outputs.cache-hit != 'true'
|
||||||
|
id: xcode-cache-fallback
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Developer/Xcode/DerivedData
|
||||||
|
~/Library/Caches/org.swift.swiftpm
|
||||||
|
key: xcode-build-cache-${{ github.ref_name }}-
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# build and test
|
||||||
|
# --------------------------------------------------
|
||||||
|
- name: Clean
|
||||||
|
if: contains(github.event.head_commit.message, '[--clean-build]')
|
||||||
|
run: |
|
||||||
|
python3 scripts/ci/workflow.py clean
|
||||||
|
python3 scripts/ci/workflow.py clean-derived-data
|
||||||
|
python3 scripts/ci/workflow.py clean-spm-cache
|
||||||
|
|
||||||
|
- name: Boot simulator (async)
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||||
|
run: |
|
||||||
|
mkdir -p build/logs
|
||||||
|
python3 scripts/ci/workflow.py boot-sim-async "iPhone 17 Pro"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: python3 scripts/ci/workflow.py build
|
id: build
|
||||||
|
|
||||||
- name: Encrypt logs
|
|
||||||
env:
|
env:
|
||||||
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
run: python3 scripts/ci/workflow.py encrypt-build
|
run: |
|
||||||
|
python3 scripts/ci/workflow.py build; STATUS=$?
|
||||||
|
python3 scripts/ci/workflow.py encrypt-build
|
||||||
|
echo "encrypted=true" >> $GITHUB_OUTPUT
|
||||||
|
exit $STATUS
|
||||||
|
|
||||||
|
- name: Tests Build
|
||||||
|
id: test-build
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||||
|
env:
|
||||||
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
python3 scripts/ci/workflow.py tests-build; STATUS=$?
|
||||||
|
python3 scripts/ci/workflow.py encrypt-tests-build
|
||||||
|
exit $STATUS
|
||||||
|
|
||||||
|
- name: Save Cache
|
||||||
|
if: ${{ steps.xcode-cache-fallback.outputs.cache-hit != 'true' }}
|
||||||
|
uses: actions/cache/save@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Developer/Xcode/DerivedData
|
||||||
|
~/Library/Caches/org.swift.swiftpm
|
||||||
|
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
||||||
|
|
||||||
|
- name: Tests Run
|
||||||
|
id: test-run
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||||
|
env:
|
||||||
|
BUILD_LOG_ZIP_PASSWORD: ${{ secrets.BUILD_LOG_ZIP_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
python3 scripts/ci/workflow.py tests-run "iPhone 17 Pro"; STATUS=$?
|
||||||
|
python3 scripts/ci/workflow.py encrypt-tests-run
|
||||||
|
exit $STATUS
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# artifacts
|
||||||
|
# --------------------------------------------------
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: encrypted-build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||||
|
path: encrypted-build-logs.zip
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_BUILD == '1' }}
|
||||||
|
with:
|
||||||
|
name: encrypted-tests-build-logs-${{ env.SHORT_COMMIT }}.zip
|
||||||
|
path: encrypted-tests-build-logs.zip
|
||||||
|
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ vars.ENABLE_TESTS == '1' && vars.ENABLE_TESTS_RUN == '1' }}
|
||||||
|
with:
|
||||||
|
name: encrypted-tests-run-logs-${{ env.SHORT_COMMIT }}.zip
|
||||||
|
path: encrypted-tests-run-logs.zip
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: SideStore-${{ steps.version.outputs.version }}.ipa
|
name: SideStore-${{ env.MARKETING_VERSION }}.ipa
|
||||||
path: SideStore.ipa
|
path: SideStore.ipa
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: SideStore-${{ env.MARKETING_VERSION }}-dSYMs.zip
|
||||||
|
path: SideStore.dSYMs.zip
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: "SideStore/apps-v2.json"
|
||||||
|
ref: "main"
|
||||||
|
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||||
|
path: "SideStore/apps-v2.json"
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# deploy
|
||||||
|
# --------------------------------------------------
|
||||||
|
- name: Deploy
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
PRODUCT_NAME=$(python3 scripts/ci/workflow.py get-product-name)
|
||||||
|
BUNDLE_ID=$(python3 scripts/ci/workflow.py get-bundle-id)
|
||||||
|
SOURCE_JSON="_includes/source.json"
|
||||||
|
IPA_NAME="$PRODUCT_NAME.ipa"
|
||||||
|
|
||||||
|
LAST_SUCCESSFUL_COMMIT=$(python3 scripts/ci/workflow.py last-successful-commit \
|
||||||
|
"${{ github.workflow }}" "$CHANNEL")
|
||||||
|
|
||||||
|
python3 scripts/ci/workflow.py deploy \
|
||||||
|
SideStore/apps-v2.json \
|
||||||
|
"$SOURCE_JSON" \
|
||||||
|
"$CHANNEL" \
|
||||||
|
"$SHORT_COMMIT" \
|
||||||
|
"$MARKETING_VERSION" \
|
||||||
|
"$CHANNEL" \
|
||||||
|
"$BUNDLE_ID" \
|
||||||
|
"$IPA_NAME" \
|
||||||
|
"$LAST_SUCCESSFUL_COMMIT"
|
||||||
|
|
||||||
|
RELEASE_NOTES=$(python3 scripts/ci/workflow.py retrieve-release-notes "$CHANNEL")
|
||||||
|
|
||||||
|
python3 scripts/ci/workflow.py upload-release \
|
||||||
|
"$RELEASE_NAME" \
|
||||||
|
"$CHANNEL" \
|
||||||
|
"$GITHUB_SHA" \
|
||||||
|
"$GITHUB_REPOSITORY" \
|
||||||
|
"$UPSTREAM_CHANNEL"
|
||||||
|
|||||||
97
.github/workflows/nightly.yml
vendored
97
.github/workflows/nightly.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [develop]
|
branches: [develop]
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * *'
|
- cron: "0 0 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
@@ -14,6 +14,10 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-26
|
runs-on: macos-26
|
||||||
|
env:
|
||||||
|
RELEASE_NAME: Nightly
|
||||||
|
CHANNEL: nightly
|
||||||
|
UPSTREAM_CHANNEL: ""
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -28,13 +32,13 @@ jobs:
|
|||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: 'SideStore/beta-build-num'
|
repository: "SideStore/beta-build-num"
|
||||||
ref: ${{ env.ref }}
|
ref: ${{ env.CHANNEL }}
|
||||||
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||||
path: 'Dependencies/beta-build-num'
|
path: "Dependencies/beta-build-num"
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup Env
|
||||||
run: |
|
run: |
|
||||||
BUILD_NUM=$(python3 scripts/ci/workflow.py reserve_build_number 'Dependencies/beta-build-num')
|
BUILD_NUM=$(python3 scripts/ci/workflow.py reserve_build_number 'Dependencies/beta-build-num')
|
||||||
MARKETING_VERSION=$(python3 scripts/ci/workflow.py get-marketing-version)
|
MARKETING_VERSION=$(python3 scripts/ci/workflow.py get-marketing-version)
|
||||||
@@ -43,31 +47,40 @@ jobs:
|
|||||||
QUALIFIED_VERSION=$(python3 scripts/ci/workflow.py compute-qualified \
|
QUALIFIED_VERSION=$(python3 scripts/ci/workflow.py compute-qualified \
|
||||||
"$MARKETING_VERSION" \
|
"$MARKETING_VERSION" \
|
||||||
"$BUILD_NUM" \
|
"$BUILD_NUM" \
|
||||||
"${{ env.ref }}" \
|
"${{ env.CHANNEL }}" \
|
||||||
"$SHORT_COMMIT")
|
"$SHORT_COMMIT")
|
||||||
|
|
||||||
echo "BUILD_NUM=$BUILD_NUM" >> $GITHUB_ENV
|
python3 scripts/ci/workflow.py set-marketing-version "$QUALIFIED_VERSION"
|
||||||
echo "MARKETING_VERSION=$MARKETING_VERSION" >> $GITHUB_ENV
|
|
||||||
echo "SHORT_COMMIT=$SHORT_COMMIT" >> $GITHUB_ENV
|
echo "BUILD_NUM=$BUILD_NUM" | tee -a $GITHUB_ENV
|
||||||
echo "VERSION=$QUALIFIED_VERSION" >> $GITHUB_ENV
|
echo "SHORT_COMMIT=$SHORT_COMMIT" | tee -a $GITHUB_ENV
|
||||||
|
echo "MARKETING_VERSION=$QUALIFIED_VERSION" | tee -a $GITHUB_ENV
|
||||||
|
|
||||||
- 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: '26.2'
|
xcode-version: "26.2"
|
||||||
|
|
||||||
- name: Restore Cache
|
- name: Restore Cache (exact)
|
||||||
id: xcode-cache
|
id: xcode-cache-exact
|
||||||
uses: actions/cache/restore@v3
|
uses: actions/cache/restore@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/Library/Developer/Xcode/DerivedData
|
~/Library/Developer/Xcode/DerivedData
|
||||||
~/Library/Caches/org.swift.swiftpm
|
~/Library/Caches/org.swift.swiftpm
|
||||||
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
key: xcode-build-cache-${{ github.ref_name }}-${{ github.sha }}
|
||||||
restore-keys: |
|
|
||||||
xcode-build-cache-${{ github.ref_name }}-
|
|
||||||
|
|
||||||
# --------------------------------------------------
|
- name: Restore Cache (last)
|
||||||
|
if: steps.xcode-cache-exact.outputs.cache-hit != 'true'
|
||||||
|
id: xcode-cache-fallback
|
||||||
|
uses: actions/cache/restore@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Developer/Xcode/DerivedData
|
||||||
|
~/Library/Caches/org.swift.swiftpm
|
||||||
|
key: xcode-build-cache-${{ github.ref_name }}-
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
# build and test
|
# build and test
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
- name: Clean
|
- name: Clean
|
||||||
@@ -104,7 +117,7 @@ jobs:
|
|||||||
exit $STATUS
|
exit $STATUS
|
||||||
|
|
||||||
- name: Save Cache
|
- name: Save Cache
|
||||||
if: ${{ steps.xcode-cache.outputs.cache-hit != 'true' }}
|
if: ${{ steps.xcode-cache-fallback.outputs.cache-hit != 'true' }}
|
||||||
uses: actions/cache/save@v3
|
uses: actions/cache/save@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@@ -123,11 +136,11 @@ jobs:
|
|||||||
exit $STATUS
|
exit $STATUS
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
# artifacts
|
# artifacts
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: encrypted-build-logs-${{ env.VERSION }}.zip
|
name: encrypted-build-logs-${{ env.MARKETING_VERSION }}.zip
|
||||||
path: encrypted-build-logs.zip
|
path: encrypted-build-logs.zip
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
@@ -144,25 +157,51 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: SideStore-${{ env.VERSION }}.ipa
|
name: SideStore-${{ env.MARKETING_VERSION }}.ipa
|
||||||
path: SideStore.ipa
|
path: SideStore.ipa
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: SideStore-${{ env.VERSION }}-dSYMs.zip
|
name: SideStore-${{ env.MARKETING_VERSION }}-dSYMs.zip
|
||||||
path: SideStore.dSYMs.zip
|
path: SideStore.dSYMs.zip
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: "SideStore/apps-v2.json"
|
||||||
|
ref: "main"
|
||||||
|
token: ${{ secrets.CROSS_REPO_PUSH_KEY }}
|
||||||
|
path: "SideStore/apps-v2.json"
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
# deploy
|
# deploy
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
- name: Deploy
|
- name: Deploy
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
|
PRODUCT_NAME=$(python3 scripts/ci/workflow.py get-product-name)
|
||||||
|
BUNDLE_ID=$(python3 scripts/ci/workflow.py get-bundle-id)
|
||||||
|
SOURCE_JSON="_includes/source.json"
|
||||||
|
IPA_NAME="$PRODUCT_NAME.ipa"
|
||||||
|
|
||||||
|
LAST_SUCCESSFUL_COMMIT=$(python3 scripts/ci/workflow.py last-successful-commit \
|
||||||
|
"${{ github.workflow }}" "$CHANNEL")
|
||||||
|
|
||||||
python3 scripts/ci/workflow.py deploy \
|
python3 scripts/ci/workflow.py deploy \
|
||||||
Dependencies/apps-v2.json \
|
SideStore/apps-v2.json \
|
||||||
"_includes/source.json" \
|
"$SOURCE_JSON" \
|
||||||
"${{ env.ref_name }}" \
|
"$CHANNEL" \
|
||||||
"$SHORT_COMMIT" \
|
"$SHORT_COMMIT" \
|
||||||
"$MARKETING_VERSION" \
|
"$MARKETING_VERSION" \
|
||||||
"$VERSION" \
|
"$CHANNEL" \
|
||||||
"${{ env.ref_name }}" \
|
"$BUNDLE_ID" \
|
||||||
"com.SideStore.SideStore" \
|
"$IPA_NAME" \
|
||||||
"SideStore.ipa"
|
"$LAST_SUCCESSFUL_COMMIT"
|
||||||
|
|
||||||
|
RELEASE_NOTES=$(python3 scripts/ci/workflow.py retrieve-release-notes "$CHANNEL")
|
||||||
|
|
||||||
|
python3 scripts/ci/workflow.py upload-release \
|
||||||
|
"$RELEASE_NAME" \
|
||||||
|
"$CHANNEL" \
|
||||||
|
"$GITHUB_SHA" \
|
||||||
|
"$GITHUB_REPOSITORY" \
|
||||||
|
"$UPSTREAM_CHANNEL"
|
||||||
|
|||||||
@@ -881,6 +881,90 @@
|
|||||||
remoteGlobalIDString = 191E5FAA290A5D92001A3B7C;
|
remoteGlobalIDString = 191E5FAA290A5D92001A3B7C;
|
||||||
remoteInfo = minimuxer;
|
remoteInfo = minimuxer;
|
||||||
};
|
};
|
||||||
|
A86372AE2F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A8636E922F4CF74D00E66784 /* libfragmentzip.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = 87B8C3401E0E9C37002F817D;
|
||||||
|
remoteInfo = "fragmentzip-cli-macOS";
|
||||||
|
};
|
||||||
|
A86372B02F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A8636E922F4CF74D00E66784 /* libfragmentzip.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = B315FDB02866CCF8002E243C;
|
||||||
|
remoteInfo = "fragmentzip-cli-iOS";
|
||||||
|
};
|
||||||
|
A86372B22F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A8636E922F4CF74D00E66784 /* libfragmentzip.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = B315FDB52866CD91002E243C;
|
||||||
|
remoteInfo = "fragmentzip-macOS";
|
||||||
|
};
|
||||||
|
A86372B42F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A8636E922F4CF74D00E66784 /* libfragmentzip.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = B315FDCE2866CDD3002E243C;
|
||||||
|
remoteInfo = "fragmentzip-iOS";
|
||||||
|
};
|
||||||
|
A86372B72F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A8636E6E2F4CF74D00E66784 /* libgeneral.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = 87977F6F227C4B71004F31DA;
|
||||||
|
remoteInfo = libgeneral;
|
||||||
|
};
|
||||||
|
A86372BC2F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A86372112F4CF74D00E66784 /* Roxas.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = BFADAFF819AE7BB70050CF31;
|
||||||
|
remoteInfo = Roxas;
|
||||||
|
};
|
||||||
|
A86372BE2F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A86372112F4CF74D00E66784 /* Roxas.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = BF8624801BB742E700C12EEE;
|
||||||
|
remoteInfo = RoxasTV;
|
||||||
|
};
|
||||||
|
A86372C02F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A86372112F4CF74D00E66784 /* Roxas.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = BFADB00319AE7BB80050CF31;
|
||||||
|
remoteInfo = RoxasTests;
|
||||||
|
};
|
||||||
|
A86372C32F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A86371872F4CF74D00E66784 /* SampleApp.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = 44E8FA8923D90632009E1D13;
|
||||||
|
remoteInfo = SampleApp;
|
||||||
|
};
|
||||||
|
A86372C72F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A863716E2F4CF74D00E66784 /* SampleApp.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = 44B1EE7C23DB90D5004E2E29;
|
||||||
|
remoteInfo = SampleApp;
|
||||||
|
};
|
||||||
|
A86372C92F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A863716E2F4CF74D00E66784 /* SampleApp.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = 445A906A2400612800B487B4;
|
||||||
|
remoteInfo = "NSAttributedString+MarkdownTests";
|
||||||
|
};
|
||||||
|
A86372CC2F4D1E4400E66784 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = A86371932F4CF74D00E66784 /* SwiftSampleApp.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = 44962FDA23E7A54A00E2A598;
|
||||||
|
remoteInfo = SwiftSampleApp;
|
||||||
|
};
|
||||||
A8A5AC022F4C2CFC00572B4A /* PBXContainerItemProxy */ = {
|
A8A5AC022F4C2CFC00572B4A /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = A8A5AB6D2F4C2CFC00572B4A /* minimuxer.xcodeproj */;
|
containerPortal = A8A5AB6D2F4C2CFC00572B4A /* minimuxer.xcodeproj */;
|
||||||
@@ -2168,6 +2252,12 @@
|
|||||||
A85AEC662F4B22F6002E2E11 /* em_proxy.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = em_proxy.xcodeproj; sourceTree = "<group>"; };
|
A85AEC662F4B22F6002E2E11 /* em_proxy.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = em_proxy.xcodeproj; sourceTree = "<group>"; };
|
||||||
A85AEC682F4B22F6002E2E11 /* minimuxer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = minimuxer.xcodeproj; sourceTree = "<group>"; };
|
A85AEC682F4B22F6002E2E11 /* minimuxer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = minimuxer.xcodeproj; sourceTree = "<group>"; };
|
||||||
A8635D052F4CF16D00E66784 /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = Dependencies/AltSign/Dependencies/OpenSSL.xcframework; sourceTree = "<group>"; };
|
A8635D052F4CF16D00E66784 /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = Dependencies/AltSign/Dependencies/OpenSSL.xcframework; sourceTree = "<group>"; };
|
||||||
|
A8636E6E2F4CF74D00E66784 /* libgeneral.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = libgeneral.xcodeproj; sourceTree = "<group>"; };
|
||||||
|
A8636E922F4CF74D00E66784 /* libfragmentzip.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = libfragmentzip.xcodeproj; sourceTree = "<group>"; };
|
||||||
|
A863716E2F4CF74D00E66784 /* SampleApp.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = SampleApp.xcodeproj; sourceTree = "<group>"; };
|
||||||
|
A86371872F4CF74D00E66784 /* SampleApp.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = SampleApp.xcodeproj; sourceTree = "<group>"; };
|
||||||
|
A86371932F4CF74D00E66784 /* SwiftSampleApp.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = SwiftSampleApp.xcodeproj; sourceTree = "<group>"; };
|
||||||
|
A86372112F4CF74D00E66784 /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = Roxas.xcodeproj; 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; };
|
||||||
A8A5A7E82F4C2CFC00572B4A /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
|
A8A5A7E82F4C2CFC00572B4A /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
|
||||||
A8A5A7E92F4C2CFC00572B4A /* build.rs */ = {isa = PBXFileReference; lastKnownFileType = text; path = build.rs; sourceTree = "<group>"; };
|
A8A5A7E92F4C2CFC00572B4A /* build.rs */ = {isa = PBXFileReference; lastKnownFileType = text; path = build.rs; sourceTree = "<group>"; };
|
||||||
@@ -3231,6 +3321,60 @@
|
|||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
A8636E992F4CF74D00E66784 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A86372B82F4D1E4400E66784 /* libgeneral */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A8636E9B2F4CF74D00E66784 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A86372AF2F4D1E4400E66784 /* libfragmentzip */,
|
||||||
|
A86372B12F4D1E4400E66784 /* libfragmentzip */,
|
||||||
|
A86372B32F4D1E4400E66784 /* libfragmentzip.a */,
|
||||||
|
A86372B52F4D1E4400E66784 /* libfragmentzip.a */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A863719B2F4CF74D00E66784 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A86372C82F4D1E4400E66784 /* SampleApp.app */,
|
||||||
|
A86372CA2F4D1E4400E66784 /* NSAttributedString+MarkdownTests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A863719D2F4CF74D00E66784 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A86372CD2F4D1E4400E66784 /* SwiftSampleApp.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A863719F2F4CF74D00E66784 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A86372C42F4D1E4400E66784 /* SampleApp.app */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
A86372122F4CF74D00E66784 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A86372BD2F4D1E4400E66784 /* Roxas.framework */,
|
||||||
|
A86372BF2F4D1E4400E66784 /* Roxas.framework */,
|
||||||
|
A86372C12F4D1E4400E66784 /* RoxasTests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
A8A5A7F42F4C2CFC00572B4A /* em_proxy */ = {
|
A8A5A7F42F4C2CFC00572B4A /* em_proxy */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -4497,6 +4641,10 @@
|
|||||||
ProductGroup = A8A5B06A2F4C347700572B4A /* Products */;
|
ProductGroup = A8A5B06A2F4C347700572B4A /* Products */;
|
||||||
ProjectRef = A8A5B0622F4C347700572B4A /* libfragmentzip.xcodeproj */;
|
ProjectRef = A8A5B0622F4C347700572B4A /* libfragmentzip.xcodeproj */;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProductGroup = A8636E9B2F4CF74D00E66784 /* Products */;
|
||||||
|
ProjectRef = A8636E922F4CF74D00E66784 /* libfragmentzip.xcodeproj */;
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ProductGroup = A8FAC0612F4B50D10061A851 /* Products */;
|
ProductGroup = A8FAC0612F4B50D10061A851 /* Products */;
|
||||||
ProjectRef = A8FABCE72F4B50D10061A851 /* libfragmentzip.xcodeproj */;
|
ProjectRef = A8FABCE72F4B50D10061A851 /* libfragmentzip.xcodeproj */;
|
||||||
@@ -4609,6 +4757,10 @@
|
|||||||
ProductGroup = A8EEDA212F4B19B000F2436D /* Products */;
|
ProductGroup = A8EEDA212F4B19B000F2436D /* Products */;
|
||||||
ProjectRef = A8EED1D72F4B19B000F2436D /* libgeneral.xcodeproj */;
|
ProjectRef = A8EED1D72F4B19B000F2436D /* libgeneral.xcodeproj */;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProductGroup = A8636E992F4CF74D00E66784 /* Products */;
|
||||||
|
ProjectRef = A8636E6E2F4CF74D00E66784 /* libgeneral.xcodeproj */;
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ProductGroup = A81197412F4C1C710013ABD0 /* Products */;
|
ProductGroup = A81197412F4C1C710013ABD0 /* Products */;
|
||||||
ProjectRef = A81193E22F4C1C710013ABD0 /* libgeneral.xcodeproj */;
|
ProjectRef = A81193E22F4C1C710013ABD0 /* libgeneral.xcodeproj */;
|
||||||
@@ -4773,6 +4925,10 @@
|
|||||||
ProductGroup = A8A5B7332F4C4C8600572B4A /* Products */;
|
ProductGroup = A8A5B7332F4C4C8600572B4A /* Products */;
|
||||||
ProjectRef = A8A5B7322F4C4C8600572B4A /* Roxas.xcodeproj */;
|
ProjectRef = A8A5B7322F4C4C8600572B4A /* Roxas.xcodeproj */;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProductGroup = A86372122F4CF74D00E66784 /* Products */;
|
||||||
|
ProjectRef = A86372112F4CF74D00E66784 /* Roxas.xcodeproj */;
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ProductGroup = A8EEDA272F4B19B000F2436D /* Products */;
|
ProductGroup = A8EEDA272F4B19B000F2436D /* Products */;
|
||||||
ProjectRef = A8EEDA1E2F4B19B000F2436D /* Roxas.xcodeproj */;
|
ProjectRef = A8EEDA1E2F4B19B000F2436D /* Roxas.xcodeproj */;
|
||||||
@@ -4817,6 +4973,10 @@
|
|||||||
ProductGroup = A81180DC2F4C1B230013ABD0 /* Products */;
|
ProductGroup = A81180DC2F4C1B230013ABD0 /* Products */;
|
||||||
ProjectRef = A81180302F4C1B230013ABD0 /* SampleApp.xcodeproj */;
|
ProjectRef = A81180302F4C1B230013ABD0 /* SampleApp.xcodeproj */;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProductGroup = A863719F2F4CF74D00E66784 /* Products */;
|
||||||
|
ProjectRef = A86371872F4CF74D00E66784 /* SampleApp.xcodeproj */;
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ProductGroup = A81197372F4C1C710013ABD0 /* Products */;
|
ProductGroup = A81197372F4C1C710013ABD0 /* Products */;
|
||||||
ProjectRef = A81196912F4C1C710013ABD0 /* SampleApp.xcodeproj */;
|
ProjectRef = A81196912F4C1C710013ABD0 /* SampleApp.xcodeproj */;
|
||||||
@@ -4865,6 +5025,10 @@
|
|||||||
ProductGroup = A8A5ACE92F4C339400572B4A /* Products */;
|
ProductGroup = A8A5ACE92F4C339400572B4A /* Products */;
|
||||||
ProjectRef = A8A5ACD02F4C339400572B4A /* SampleApp.xcodeproj */;
|
ProjectRef = A8A5ACD02F4C339400572B4A /* SampleApp.xcodeproj */;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProductGroup = A863719B2F4CF74D00E66784 /* Products */;
|
||||||
|
ProjectRef = A863716E2F4CF74D00E66784 /* SampleApp.xcodeproj */;
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ProductGroup = A8A5ACE72F4C339400572B4A /* Products */;
|
ProductGroup = A8A5ACE72F4C339400572B4A /* Products */;
|
||||||
ProjectRef = A8A5ACB72F4C339400572B4A /* SampleApp.xcodeproj */;
|
ProjectRef = A8A5ACB72F4C339400572B4A /* SampleApp.xcodeproj */;
|
||||||
@@ -4973,6 +5137,10 @@
|
|||||||
ProductGroup = A81197392F4C1C710013ABD0 /* Products */;
|
ProductGroup = A81197392F4C1C710013ABD0 /* Products */;
|
||||||
ProjectRef = A811969D2F4C1C710013ABD0 /* SwiftSampleApp.xcodeproj */;
|
ProjectRef = A811969D2F4C1C710013ABD0 /* SwiftSampleApp.xcodeproj */;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProductGroup = A863719D2F4CF74D00E66784 /* Products */;
|
||||||
|
ProjectRef = A86371932F4CF74D00E66784 /* SwiftSampleApp.xcodeproj */;
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ProductGroup = A8FAC0592F4B50D10061A851 /* Products */;
|
ProductGroup = A8FAC0592F4B50D10061A851 /* Products */;
|
||||||
ProjectRef = A8FABF7E2F4B50D10061A851 /* SwiftSampleApp.xcodeproj */;
|
ProjectRef = A8FABF7E2F4B50D10061A851 /* SwiftSampleApp.xcodeproj */;
|
||||||
@@ -5860,6 +6028,90 @@
|
|||||||
remoteRef = A85AED342F4B2315002E2E11 /* PBXContainerItemProxy */;
|
remoteRef = A85AED342F4B2315002E2E11 /* PBXContainerItemProxy */;
|
||||||
sourceTree = BUILT_PRODUCTS_DIR;
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
};
|
};
|
||||||
|
A86372AF2F4D1E4400E66784 /* libfragmentzip */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = "compiled.mach-o.executable";
|
||||||
|
path = libfragmentzip;
|
||||||
|
remoteRef = A86372AE2F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372B12F4D1E4400E66784 /* libfragmentzip */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = "compiled.mach-o.executable";
|
||||||
|
path = libfragmentzip;
|
||||||
|
remoteRef = A86372B02F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372B32F4D1E4400E66784 /* libfragmentzip.a */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = archive.ar;
|
||||||
|
path = libfragmentzip.a;
|
||||||
|
remoteRef = A86372B22F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372B52F4D1E4400E66784 /* libfragmentzip.a */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = archive.ar;
|
||||||
|
path = libfragmentzip.a;
|
||||||
|
remoteRef = A86372B42F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372B82F4D1E4400E66784 /* libgeneral */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = "compiled.mach-o.executable";
|
||||||
|
path = libgeneral;
|
||||||
|
remoteRef = A86372B72F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372BD2F4D1E4400E66784 /* Roxas.framework */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = wrapper.framework;
|
||||||
|
path = Roxas.framework;
|
||||||
|
remoteRef = A86372BC2F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372BF2F4D1E4400E66784 /* Roxas.framework */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = wrapper.framework;
|
||||||
|
path = Roxas.framework;
|
||||||
|
remoteRef = A86372BE2F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372C12F4D1E4400E66784 /* RoxasTests.xctest */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = wrapper.cfbundle;
|
||||||
|
path = RoxasTests.xctest;
|
||||||
|
remoteRef = A86372C02F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372C42F4D1E4400E66784 /* SampleApp.app */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = wrapper.application;
|
||||||
|
path = SampleApp.app;
|
||||||
|
remoteRef = A86372C32F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372C82F4D1E4400E66784 /* SampleApp.app */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = wrapper.application;
|
||||||
|
path = SampleApp.app;
|
||||||
|
remoteRef = A86372C72F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372CA2F4D1E4400E66784 /* NSAttributedString+MarkdownTests.xctest */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = wrapper.cfbundle;
|
||||||
|
path = "NSAttributedString+MarkdownTests.xctest";
|
||||||
|
remoteRef = A86372C92F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
|
A86372CD2F4D1E4400E66784 /* SwiftSampleApp.app */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = wrapper.application;
|
||||||
|
path = SwiftSampleApp.app;
|
||||||
|
remoteRef = A86372CC2F4D1E4400E66784 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
A8A5AC032F4C2CFC00572B4A /* libminimuxer_static.a */ = {
|
A8A5AC032F4C2CFC00572B4A /* libminimuxer_static.a */ = {
|
||||||
isa = PBXReferenceProxy;
|
isa = PBXReferenceProxy;
|
||||||
fileType = archive.ar;
|
fileType = archive.ar;
|
||||||
|
|||||||
2
Dependencies/AltSign
vendored
2
Dependencies/AltSign
vendored
Submodule Dependencies/AltSign updated: 4cffa3cf45...4819a7984f
2
Dependencies/apps-v2.json
vendored
2
Dependencies/apps-v2.json
vendored
Submodule Dependencies/apps-v2.json updated: e0914d3463...9724b1c56d
37
release-notes.md
Normal file
37
release-notes.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
|
||||||
|
### nightly
|
||||||
|
#### What's Changed
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- altsign updated to latest
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- re added openSSL from new path
|
||||||
|
- updated altsign to use xcframework for openSSL which was causing huge download of 1.2 GB each time
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: improve more ci worflow
|
||||||
|
- CI: full rewrite - moved logic into ci.py and kept workflow scripts mostly dummy
|
||||||
|
|
||||||
|
#### Full Changelog: [38715283...99712f00](https://github.com/SideStore/SideStore/compare/38715283073ea37949a462b889ce3cad403ea499...99712f0020a4f2ae57d8d781514fa735f893c23a)
|
||||||
@@ -19,6 +19,20 @@ def run(cmd: str) -> str:
|
|||||||
return subprocess.check_output(cmd, shell=True, text=True).strip()
|
return subprocess.check_output(cmd, shell=True, text=True).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def commit_exists(rev: str) -> bool:
|
||||||
|
if not rev:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
subprocess.check_output(
|
||||||
|
f"git rev-parse --verify {rev}^{{commit}}",
|
||||||
|
shell=True,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def head_commit():
|
def head_commit():
|
||||||
return run("git rev-parse HEAD")
|
return run("git rev-parse HEAD")
|
||||||
|
|
||||||
@@ -35,12 +49,8 @@ def repo_url():
|
|||||||
|
|
||||||
|
|
||||||
def commit_messages(start, end="HEAD"):
|
def commit_messages(start, end="HEAD"):
|
||||||
try:
|
out = run(f"git log {start}..{end} --pretty=format:%s")
|
||||||
out = run(f"git log {start}..{end} --pretty=format:%s")
|
return out.splitlines() if out else []
|
||||||
return out.splitlines() if out else []
|
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
fallback = run("git rev-parse HEAD~5")
|
|
||||||
return run(f"git log {fallback}..{end} --pretty=format:%s").splitlines()
|
|
||||||
|
|
||||||
|
|
||||||
def authors(range_expr, fmt="%an"):
|
def authors(range_expr, fmt="%an"):
|
||||||
@@ -76,10 +86,35 @@ def fmt_author(author):
|
|||||||
# release note generation
|
# release note generation
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def resolve_start_commit(last_successful: str):
|
||||||
|
if commit_exists(last_successful):
|
||||||
|
return last_successful
|
||||||
|
|
||||||
|
try:
|
||||||
|
return run("git rev-parse HEAD~10")
|
||||||
|
except Exception:
|
||||||
|
return first_commit()
|
||||||
|
|
||||||
def generate_release_notes(last_successful, tag, branch):
|
def generate_release_notes(last_successful, tag, branch):
|
||||||
current = head_commit()
|
current = head_commit()
|
||||||
|
|
||||||
|
# fallback if missing/invalid
|
||||||
|
if not last_successful or not commit_exists(last_successful):
|
||||||
|
try:
|
||||||
|
last_successful = run("git rev-parse HEAD~10")
|
||||||
|
except Exception:
|
||||||
|
last_successful = first_commit()
|
||||||
|
|
||||||
messages = commit_messages(last_successful, current)
|
messages = commit_messages(last_successful, current)
|
||||||
|
|
||||||
|
# fallback if empty range
|
||||||
|
if not messages:
|
||||||
|
try:
|
||||||
|
last_successful = run("git rev-parse HEAD~10")
|
||||||
|
except Exception:
|
||||||
|
last_successful = first_commit()
|
||||||
|
messages = commit_messages(last_successful, current)
|
||||||
|
|
||||||
section = f"{TAG_MARKER} {tag}\n"
|
section = f"{TAG_MARKER} {tag}\n"
|
||||||
section += f"{HEADER_MARKER} What's Changed\n"
|
section += f"{HEADER_MARKER} What's Changed\n"
|
||||||
|
|
||||||
@@ -90,7 +125,8 @@ def generate_release_notes(last_successful, tag, branch):
|
|||||||
section += f"{fmt_msg(m)}\n"
|
section += f"{fmt_msg(m)}\n"
|
||||||
|
|
||||||
prev_authors = authors(branch)
|
prev_authors = authors(branch)
|
||||||
new_authors = authors(f"{last_successful}..{current}") - prev_authors
|
recent_authors = authors(f"{last_successful}..{current}")
|
||||||
|
new_authors = recent_authors - prev_authors
|
||||||
|
|
||||||
if new_authors:
|
if new_authors:
|
||||||
section += f"\n{HEADER_MARKER} New Contributors\n"
|
section += f"\n{HEADER_MARKER} New Contributors\n"
|
||||||
@@ -170,7 +206,7 @@ def update_release_md(existing, new_section, tag):
|
|||||||
# retrieval
|
# retrieval
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
def retrieve_tag(tag, file_path):
|
def retrieve_tag(tag, file_path: Path):
|
||||||
if not file_path.exists():
|
if not file_path.exists():
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@@ -209,30 +245,20 @@ def main():
|
|||||||
" generate_release_notes.py --retrieve <tag> [--output-dir DIR]"
|
" generate_release_notes.py --retrieve <tag> [--output-dir DIR]"
|
||||||
)
|
)
|
||||||
|
|
||||||
# parse optional output dir
|
|
||||||
output_dir = Path.cwd()
|
output_dir = Path.cwd()
|
||||||
|
|
||||||
if "--output-dir" in args:
|
if "--output-dir" in args:
|
||||||
idx = args.index("--output-dir")
|
idx = args.index("--output-dir")
|
||||||
try:
|
output_dir = Path(args[idx + 1]).resolve()
|
||||||
output_dir = Path(args[idx + 1]).resolve()
|
|
||||||
except IndexError:
|
|
||||||
sys.exit("Missing value for --output-dir")
|
|
||||||
|
|
||||||
del args[idx:idx + 2]
|
del args[idx:idx + 2]
|
||||||
|
|
||||||
output_dir.mkdir(parents=True, exist_ok=True)
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
release_file = output_dir / "release-notes.md"
|
release_file = output_dir / "release-notes.md"
|
||||||
|
|
||||||
# retrieval mode
|
|
||||||
if args[0] == "--retrieve":
|
if args[0] == "--retrieve":
|
||||||
if len(args) < 2:
|
|
||||||
sys.exit("Missing tag after --retrieve")
|
|
||||||
|
|
||||||
print(retrieve_tag(args[1], release_file))
|
print(retrieve_tag(args[1], release_file))
|
||||||
return
|
return
|
||||||
|
|
||||||
# generation mode
|
|
||||||
last_successful = args[0]
|
last_successful = args[0]
|
||||||
tag = args[1] if len(args) > 1 else head_commit()
|
tag = args[1] if len(args) > 1 else head_commit()
|
||||||
branch = args[2] if len(args) > 2 else (
|
branch = args[2] if len(args) > 2 else (
|
||||||
@@ -241,12 +267,7 @@ def main():
|
|||||||
|
|
||||||
new_section = generate_release_notes(last_successful, tag, branch)
|
new_section = generate_release_notes(last_successful, tag, branch)
|
||||||
|
|
||||||
existing = (
|
existing = release_file.read_text() if release_file.exists() else ""
|
||||||
release_file.read_text()
|
|
||||||
if release_file.exists()
|
|
||||||
else ""
|
|
||||||
)
|
|
||||||
|
|
||||||
updated = update_release_md(existing, new_section, tag)
|
updated = update_release_md(existing, new_section, tag)
|
||||||
|
|
||||||
release_file.write_text(updated)
|
release_file.write_text(updated)
|
||||||
|
|||||||
@@ -5,16 +5,34 @@ import json
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import argparse
|
import argparse
|
||||||
|
import textwrap
|
||||||
|
import sys
|
||||||
|
|
||||||
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# helpers
|
# helpers
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def resolve_script(name: str) -> Path:
|
||||||
|
p = Path.cwd() / name
|
||||||
|
if p.exists():
|
||||||
|
return p
|
||||||
|
return SCRIPT_DIR / name
|
||||||
|
|
||||||
|
|
||||||
def sh(cmd: str, cwd: Path) -> str:
|
def sh(cmd: str, cwd: Path) -> str:
|
||||||
return subprocess.check_output(
|
try:
|
||||||
cmd, shell=True, cwd=cwd
|
return subprocess.check_output(
|
||||||
).decode().strip()
|
cmd,
|
||||||
|
shell=True,
|
||||||
|
cwd=cwd,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
).decode().strip()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(e.output.decode(), file=sys.stderr)
|
||||||
|
raise SystemExit(f"Command failed: {cmd}")
|
||||||
|
|
||||||
|
|
||||||
def file_size(path: Path) -> int:
|
def file_size(path: Path) -> int:
|
||||||
@@ -38,36 +56,26 @@ def sha256(path: Path) -> str:
|
|||||||
def main():
|
def main():
|
||||||
p = argparse.ArgumentParser()
|
p = argparse.ArgumentParser()
|
||||||
|
|
||||||
p.add_argument(
|
p.add_argument("--repo-root", required=True)
|
||||||
"--repo-root",
|
p.add_argument("--ipa", required=True)
|
||||||
required=True,
|
p.add_argument("--output-dir", required=True)
|
||||||
help="Repo used for git history + release notes",
|
|
||||||
)
|
|
||||||
|
|
||||||
p.add_argument(
|
p.add_argument(
|
||||||
"--ipa",
|
"--output-name",
|
||||||
required=True,
|
default="source_metadata.json",
|
||||||
help="Path to IPA file",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
p.add_argument(
|
p.add_argument("--release-notes-dir", required=True)
|
||||||
"--output-dir",
|
|
||||||
required=True,
|
|
||||||
help="Output Directory where source_metadata.json is written",
|
|
||||||
)
|
|
||||||
|
|
||||||
p.add_argument(
|
|
||||||
"--release-notes-dir",
|
|
||||||
required=True,
|
|
||||||
help="Output Directory where release-notes.md is generated/read",
|
|
||||||
)
|
|
||||||
|
|
||||||
p.add_argument("--release-tag", required=True)
|
p.add_argument("--release-tag", required=True)
|
||||||
p.add_argument("--version", required=True)
|
|
||||||
p.add_argument("--marketing-version", required=True)
|
p.add_argument("--marketing-version", required=True)
|
||||||
p.add_argument("--short-commit", required=True)
|
p.add_argument("--short-commit", required=True)
|
||||||
p.add_argument("--release-channel", required=True)
|
p.add_argument("--release-channel", required=True)
|
||||||
p.add_argument("--bundle-id", required=True)
|
p.add_argument("--bundle-id", required=True)
|
||||||
|
|
||||||
|
# optional
|
||||||
|
p.add_argument("--last-successful-commit")
|
||||||
|
|
||||||
p.add_argument("--is-beta", action="store_true")
|
p.add_argument("--is-beta", action="store_true")
|
||||||
|
|
||||||
args = p.parse_args()
|
args = p.parse_args()
|
||||||
@@ -86,22 +94,30 @@ def main():
|
|||||||
notes_dir.mkdir(parents=True, exist_ok=True)
|
notes_dir.mkdir(parents=True, exist_ok=True)
|
||||||
out_dir.mkdir(parents=True, exist_ok=True)
|
out_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
out_file = out_dir / "source_metadata.json"
|
out_file = out_dir / args.output_name
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
# ensure release notes exist
|
# generate release notes
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
print("Generating release notes…")
|
print("Generating release notes…")
|
||||||
|
|
||||||
sh(
|
script = resolve_script("generate_release_notes.py")
|
||||||
(
|
|
||||||
"python3 generate_release_notes.py "
|
if args.last_successful_commit:
|
||||||
|
gen_cmd = (
|
||||||
|
f"python3 {script} "
|
||||||
|
f"{args.last_successful_commit} {args.release_tag} "
|
||||||
|
f"--output-dir \"{notes_dir}\""
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
gen_cmd = (
|
||||||
|
f"python3 {script} "
|
||||||
f"{args.short_commit} {args.release_tag} "
|
f"{args.short_commit} {args.release_tag} "
|
||||||
f"--output-dir \"{notes_dir}\""
|
f"--output-dir \"{notes_dir}\""
|
||||||
),
|
)
|
||||||
cwd=repo_root,
|
|
||||||
)
|
sh(gen_cmd, cwd=repo_root)
|
||||||
|
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
# retrieve release notes
|
# retrieve release notes
|
||||||
@@ -109,7 +125,7 @@ def main():
|
|||||||
|
|
||||||
notes = sh(
|
notes = sh(
|
||||||
(
|
(
|
||||||
"python3 generate_release_notes.py "
|
f"python3 {script} "
|
||||||
f"--retrieve {args.release_tag} "
|
f"--retrieve {args.release_tag} "
|
||||||
f"--output-dir \"{notes_dir}\""
|
f"--output-dir \"{notes_dir}\""
|
||||||
),
|
),
|
||||||
@@ -120,19 +136,19 @@ def main():
|
|||||||
# compute metadata
|
# compute metadata
|
||||||
# ------------------------------------------------------
|
# ------------------------------------------------------
|
||||||
|
|
||||||
now = datetime.datetime.now(datetime.UTC)
|
now = datetime.datetime.now(datetime.timezone.utc)
|
||||||
formatted = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
formatted = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||||
human = now.strftime("%c")
|
human = now.strftime("%c")
|
||||||
|
|
||||||
localized_description = f"""
|
localized_description = textwrap.dedent(f"""
|
||||||
This is release for:
|
This is release for:
|
||||||
- version: "{args.version}"
|
- version: "{args.marketing_version}"
|
||||||
- revision: "{args.short_commit}"
|
- revision: "{args.short_commit}"
|
||||||
- timestamp: "{human}"
|
- timestamp: "{human}"
|
||||||
|
|
||||||
Release Notes:
|
Release Notes:
|
||||||
{notes}
|
{notes}
|
||||||
""".strip()
|
""").strip()
|
||||||
|
|
||||||
metadata = {
|
metadata = {
|
||||||
"is_beta": bool(args.is_beta),
|
"is_beta": bool(args.is_beta),
|
||||||
|
|||||||
@@ -5,176 +5,177 @@ import sys
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
'''
|
# ----------------------------------------------------------
|
||||||
metadata.json template
|
# metadata
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
{
|
def load_metadata(metadata_file: Path):
|
||||||
"version_ipa": "0.0.0",
|
if not metadata_file.exists():
|
||||||
"version_date": "2000-12-18T00:00:00Z",
|
raise SystemExit(f"Missing metadata file: {metadata_file}")
|
||||||
"is_beta": true,
|
|
||||||
"release_channel": "alpha",
|
with open(metadata_file, "r", encoding="utf-8") as f:
|
||||||
"size": 0,
|
meta = json.load(f)
|
||||||
"sha256": "",
|
|
||||||
"localized_description": "Invalid Update",
|
print(" ====> Required parameter list <====")
|
||||||
"download_url": "https://github.com/SideStore/SideStore/releases/download/0.0.0/SideStore.ipa",
|
for k, v in meta.items():
|
||||||
"bundle_identifier": "com.SideStore.SideStore"
|
print(f"{k}: {v}")
|
||||||
}
|
|
||||||
'''
|
required = [
|
||||||
|
"bundle_identifier",
|
||||||
|
"version_ipa",
|
||||||
|
"version_date",
|
||||||
|
"release_channel",
|
||||||
|
"size",
|
||||||
|
"sha256",
|
||||||
|
"localized_description",
|
||||||
|
"download_url",
|
||||||
|
]
|
||||||
|
|
||||||
|
for r in required:
|
||||||
|
if not meta.get(r):
|
||||||
|
raise SystemExit("One or more required metadata fields missing")
|
||||||
|
|
||||||
|
meta["size"] = int(meta["size"])
|
||||||
|
meta["release_channel"] = meta["release_channel"].lower()
|
||||||
|
|
||||||
|
return meta
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# args
|
# source loading
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
if len(sys.argv) < 3:
|
def load_source(source_file: Path):
|
||||||
print("Usage: python3 update_apps.py <metadata.json> <source.json>")
|
if source_file.exists():
|
||||||
sys.exit(1)
|
with open(source_file, "r", encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
else:
|
||||||
|
print("source.json missing — creating minimal structure")
|
||||||
|
data = {"version": 2, "apps": []}
|
||||||
|
|
||||||
metadata_file = Path(sys.argv[1])
|
if int(data.get("version", 1)) < 2:
|
||||||
source_file = Path(sys.argv[2])
|
raise SystemExit("Only v2 and above are supported")
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# load metadata
|
# locate app
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
if not metadata_file.exists():
|
def ensure_app(data, bundle_id):
|
||||||
print(f"Missing metadata file: {metadata_file}")
|
apps = data.setdefault("apps", [])
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
with open(metadata_file, "r", encoding="utf-8") as f:
|
app = next(
|
||||||
meta = json.load(f)
|
(a for a in apps if a.get("bundleIdentifier") == bundle_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
VERSION_IPA = meta.get("version_ipa")
|
if app is None:
|
||||||
VERSION_DATE = meta.get("version_date")
|
print("App entry missing — creating new app entry")
|
||||||
IS_BETA = meta.get("is_beta")
|
app = {
|
||||||
RELEASE_CHANNEL = meta.get("release_channel")
|
"bundleIdentifier": bundle_id,
|
||||||
SIZE = meta.get("size")
|
"releaseChannels": [],
|
||||||
SHA256 = meta.get("sha256")
|
}
|
||||||
LOCALIZED_DESCRIPTION = meta.get("localized_description")
|
apps.append(app)
|
||||||
DOWNLOAD_URL = meta.get("download_url")
|
|
||||||
BUNDLE_IDENTIFIER = meta.get("bundle_identifier")
|
|
||||||
|
|
||||||
print(" ====> Required parameter list <====")
|
return app
|
||||||
print("Bundle Identifier:", BUNDLE_IDENTIFIER)
|
|
||||||
print("Version:", VERSION_IPA)
|
|
||||||
print("Version Date:", VERSION_DATE)
|
|
||||||
print("IsBeta:", IS_BETA)
|
|
||||||
print("ReleaseChannel:", RELEASE_CHANNEL)
|
|
||||||
print("Size:", SIZE)
|
|
||||||
print("Sha256:", SHA256)
|
|
||||||
print("Localized Description:", LOCALIZED_DESCRIPTION)
|
|
||||||
print("Download URL:", DOWNLOAD_URL)
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# validation
|
# update storefront
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
if (
|
def update_storefront_if_needed(app, meta):
|
||||||
not BUNDLE_IDENTIFIER
|
if meta["release_channel"] == "stable":
|
||||||
or not VERSION_IPA
|
app.update({
|
||||||
or not VERSION_DATE
|
"version": meta["version_ipa"],
|
||||||
or not RELEASE_CHANNEL
|
"versionDate": meta["version_date"],
|
||||||
or not SIZE
|
"size": meta["size"],
|
||||||
or not SHA256
|
"sha256": meta["sha256"],
|
||||||
or not LOCALIZED_DESCRIPTION
|
"localizedDescription": meta["localized_description"],
|
||||||
or not DOWNLOAD_URL
|
"downloadURL": meta["download_url"],
|
||||||
):
|
})
|
||||||
print("One or more required metadata fields missing")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
SIZE = int(SIZE)
|
|
||||||
RELEASE_CHANNEL = RELEASE_CHANNEL.lower()
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# load or create source.json
|
# update release channel (ORIGINAL FORMAT)
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
if source_file.exists():
|
def update_release_channel(app, meta):
|
||||||
with open(source_file, "r", encoding="utf-8") as f:
|
channels = app.setdefault("releaseChannels", [])
|
||||||
data = json.load(f)
|
|
||||||
else:
|
new_version = {
|
||||||
print("source.json missing — creating minimal structure")
|
"version": meta["version_ipa"],
|
||||||
data = {
|
"date": meta["version_date"],
|
||||||
"version": 2,
|
"localizedDescription": meta["localized_description"],
|
||||||
"apps": []
|
"downloadURL": meta["download_url"],
|
||||||
|
"size": meta["size"],
|
||||||
|
"sha256": meta["sha256"],
|
||||||
}
|
}
|
||||||
|
|
||||||
if int(data.get("version", 1)) < 2:
|
tracks = [
|
||||||
print("Only v2 and above are supported")
|
t for t in channels
|
||||||
sys.exit(1)
|
if isinstance(t, dict)
|
||||||
|
and t.get("track") == meta["release_channel"]
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(tracks) > 1:
|
||||||
|
raise SystemExit(f"Multiple tracks named {meta['release_channel']}")
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
if not tracks:
|
||||||
# ensure app entry exists
|
channels.insert(0, {
|
||||||
# ----------------------------------------------------------
|
"track": meta["release_channel"],
|
||||||
|
"releases": [new_version],
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
track = tracks[0]
|
||||||
|
releases = track.setdefault("releases", [])
|
||||||
|
|
||||||
apps = data.setdefault("apps", [])
|
if not releases:
|
||||||
|
releases.append(new_version)
|
||||||
app = next(
|
else:
|
||||||
(a for a in apps if a.get("bundleIdentifier") == BUNDLE_IDENTIFIER),
|
releases[0] = new_version
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
if app is None:
|
|
||||||
print("App entry missing — creating new app entry")
|
|
||||||
app = {
|
|
||||||
"bundleIdentifier": BUNDLE_IDENTIFIER,
|
|
||||||
"releaseChannels": []
|
|
||||||
}
|
|
||||||
apps.append(app)
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
|
||||||
# update logic
|
|
||||||
# ----------------------------------------------------------
|
|
||||||
|
|
||||||
if RELEASE_CHANNEL == "stable":
|
|
||||||
app.update({
|
|
||||||
"version": VERSION_IPA,
|
|
||||||
"versionDate": VERSION_DATE,
|
|
||||||
"size": SIZE,
|
|
||||||
"sha256": SHA256,
|
|
||||||
"localizedDescription": LOCALIZED_DESCRIPTION,
|
|
||||||
"downloadURL": DOWNLOAD_URL,
|
|
||||||
})
|
|
||||||
|
|
||||||
channels = app.setdefault("releaseChannels", [])
|
|
||||||
|
|
||||||
new_version = {
|
|
||||||
"version": VERSION_IPA,
|
|
||||||
"date": VERSION_DATE,
|
|
||||||
"localizedDescription": LOCALIZED_DESCRIPTION,
|
|
||||||
"downloadURL": DOWNLOAD_URL,
|
|
||||||
"size": SIZE,
|
|
||||||
"sha256": SHA256,
|
|
||||||
}
|
|
||||||
|
|
||||||
tracks = [t for t in channels if t.get("track") == RELEASE_CHANNEL]
|
|
||||||
|
|
||||||
if len(tracks) > 1:
|
|
||||||
print(f"Multiple tracks named {RELEASE_CHANNEL}")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if not tracks:
|
|
||||||
channels.insert(0, {
|
|
||||||
"track": RELEASE_CHANNEL,
|
|
||||||
"releases": [new_version],
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
tracks[0]["releases"][0] = new_version
|
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# save
|
# save
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
print("\nUpdated Sources File:\n")
|
def save_source(source_file: Path, data):
|
||||||
print(json.dumps(data, indent=2, ensure_ascii=False))
|
print("\nUpdated Sources File:\n")
|
||||||
|
print(json.dumps(data, indent=2, ensure_ascii=False))
|
||||||
|
|
||||||
with open(source_file, "w", encoding="utf-8") as f:
|
source_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
||||||
|
|
||||||
print("JSON successfully updated.")
|
with open(source_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(data, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
|
print("JSON successfully updated.")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# main
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 3:
|
||||||
|
print("Usage: python3 update_apps.py <metadata.json> <source.json>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
metadata_file = Path(sys.argv[1])
|
||||||
|
source_file = Path(sys.argv[2])
|
||||||
|
|
||||||
|
meta = load_metadata(metadata_file)
|
||||||
|
data = load_source(source_file)
|
||||||
|
|
||||||
|
app = ensure_app(data, meta["bundle_identifier"])
|
||||||
|
|
||||||
|
update_storefront_if_needed(app, meta)
|
||||||
|
update_release_channel(app, meta)
|
||||||
|
|
||||||
|
save_source(source_file, data)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -6,10 +6,12 @@ import datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
import inspect
|
||||||
|
import re
|
||||||
|
|
||||||
# REPO ROOT relative to script dir
|
# REPO ROOT relative to script dir
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
SCRIPTS = ROOT / 'scripts/ci'
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# helpers
|
# helpers
|
||||||
@@ -104,13 +106,6 @@ def reserve_build_number(repo, max_attempts=5):
|
|||||||
write(data)
|
write(data)
|
||||||
|
|
||||||
run("git add version.json", check=False, cwd=repo)
|
run("git add version.json", check=False, cwd=repo)
|
||||||
|
|
||||||
print("---- DEBUG reserve_build_number ----", file=sys.stderr)
|
|
||||||
print(f"attempt: {attempt}", file=sys.stderr)
|
|
||||||
print(f"data: {data!r}", file=sys.stderr)
|
|
||||||
print(version_json.read_text(), file=sys.stderr)
|
|
||||||
print("------------------------------------", file=sys.stderr)
|
|
||||||
|
|
||||||
run(f"git commit -m '{data['tag']} - build no: {data['build']}' || true", check=False, cwd=repo)
|
run(f"git commit -m '{data['tag']} - build no: {data['build']}' || true", check=False, cwd=repo)
|
||||||
|
|
||||||
rc = subprocess.call("git push", shell=True, cwd=repo)
|
rc = subprocess.call("git push", shell=True, cwd=repo)
|
||||||
@@ -125,17 +120,33 @@ def reserve_build_number(repo, max_attempts=5):
|
|||||||
raise SystemExit("Failed reserving build number")
|
raise SystemExit("Failed reserving build number")
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# MARKETING VERSION
|
# PROJECT INFO
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
|
def get_product_name():
|
||||||
|
return runAndGet(
|
||||||
|
"xcodebuild -showBuildSettings "
|
||||||
|
"| grep PRODUCT_NAME "
|
||||||
|
"| tail -1 "
|
||||||
|
"| sed -e 's/.*= //g'"
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_bundle_id():
|
||||||
|
return runAndGet(
|
||||||
|
"xcodebuild -showBuildSettings 2>&1 "
|
||||||
|
"| grep 'PRODUCT_BUNDLE_IDENTIFIER = ' "
|
||||||
|
"| tail -1 "
|
||||||
|
"| sed -e 's/.*= //g'"
|
||||||
|
)
|
||||||
|
|
||||||
def get_marketing_version():
|
def get_marketing_version():
|
||||||
return runAndGet("grep MARKETING_VERSION Build.xcconfig | sed -e 's/MARKETING_VERSION = //g'")
|
return runAndGet(f"grep MARKETING_VERSION {ROOT}/Build.xcconfig | sed -e 's/MARKETING_VERSION = //g'")
|
||||||
|
|
||||||
def set_marketing_version(qualified):
|
def set_marketing_version(qualified):
|
||||||
run(
|
run(
|
||||||
f"sed -E "
|
f"sed -E -i '' "
|
||||||
f"'s/^MARKETING_VERSION = .*/MARKETING_VERSION = {qualified}/' "
|
f"'s/^MARKETING_VERSION = .*/MARKETING_VERSION = {qualified}/' "
|
||||||
f"-i '' {ROOT}/Build.xcconfig"
|
f"{ROOT}/Build.xcconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
def compute_qualified_version(marketing, build_num, channel, short):
|
def compute_qualified_version(marketing, build_num, channel, short):
|
||||||
@@ -160,16 +171,12 @@ def clean_spm_cache():
|
|||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
def build():
|
def build():
|
||||||
run("make clean")
|
|
||||||
run("rm -rf ~/Library/Developer/Xcode/DerivedData/*", check=False)
|
|
||||||
run("mkdir -p build/logs")
|
run("mkdir -p build/logs")
|
||||||
|
|
||||||
run(
|
run(
|
||||||
"set -o pipefail && "
|
"set -o pipefail && "
|
||||||
"NSUnbufferedIO=YES make -B build "
|
"NSUnbufferedIO=YES make -B build "
|
||||||
"2>&1 | tee -a build/logs/build.log | xcbeautify --renderer github-actions"
|
"2>&1 | tee -a build/logs/build.log | xcbeautify --renderer github-actions"
|
||||||
)
|
)
|
||||||
|
|
||||||
run("make fakesign | tee -a build/logs/build.log")
|
run("make fakesign | tee -a build/logs/build.log")
|
||||||
run("make ipa | tee -a build/logs/build.log")
|
run("make ipa | tee -a build/logs/build.log")
|
||||||
run("zip -r -9 ./SideStore.dSYMs.zip ./SideStore.xcarchive/dSYMs")
|
run("zip -r -9 ./SideStore.dSYMs.zip ./SideStore.xcarchive/dSYMs")
|
||||||
@@ -253,19 +260,28 @@ def encrypt_logs(name):
|
|||||||
|
|
||||||
def release_notes(tag):
|
def release_notes(tag):
|
||||||
run(
|
run(
|
||||||
f"python3 generate_release_notes.py "
|
f"python3 {SCRIPTS}/generate_release_notes.py "
|
||||||
f"{tag} "
|
f"{tag} "
|
||||||
f"--repo-root {ROOT} "
|
f"--repo-root {ROOT} "
|
||||||
f"--output-dir {ROOT}"
|
f"--output-dir {ROOT}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def retrieve_release_notes(tag):
|
||||||
|
return runAndGet(
|
||||||
|
f"python3 {SCRIPTS}/generate_release_notes.py "
|
||||||
|
f"--retrieve {tag} "
|
||||||
|
f"--output-dir {ROOT}"
|
||||||
|
)
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# DEPLOY SOURCE.JSON
|
# DEPLOY SOURCE.JSON
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
|
|
||||||
def deploy(repo, source_json, release_tag, short_commit, marketing_version, version, channel, bundle_id, ipa_name):
|
def deploy(repo, source_json, release_tag, short_commit, marketing_version, channel, bundle_id, ipa_name, last_successful_commit=None):
|
||||||
repo = Path(repo).resolve()
|
repo = (ROOT / repo).resolve()
|
||||||
ipa_path = ROOT / ipa_name
|
ipa_path = ROOT / ipa_name
|
||||||
|
source_json_path = repo / source_json
|
||||||
|
metadata = 'source-metadata.json'
|
||||||
|
|
||||||
if not repo.exists():
|
if not repo.exists():
|
||||||
raise SystemExit(f"{repo} repo missing")
|
raise SystemExit(f"{repo} repo missing")
|
||||||
@@ -273,54 +289,153 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, vers
|
|||||||
if not ipa_path.exists():
|
if not ipa_path.exists():
|
||||||
raise SystemExit(f"{ipa_path} missing")
|
raise SystemExit(f"{ipa_path} missing")
|
||||||
|
|
||||||
run(f"pushd {repo}", check=True)
|
if not source_json_path.exists():
|
||||||
try:
|
raise SystemExit(f"{source_json} missing inside repo")
|
||||||
# source_json is RELATIVE to repo
|
|
||||||
if not Path(source_json).exists():
|
|
||||||
raise SystemExit(f"{source_json} missing inside repo")
|
|
||||||
|
|
||||||
run(
|
cmd = (
|
||||||
f"python3 {ROOT}/generate_source_metadata.py "
|
f"python3 {SCRIPTS}/generate_source_metadata.py "
|
||||||
f"--repo-root {ROOT} "
|
f"--repo-root {ROOT} "
|
||||||
f"--ipa {ipa_path} "
|
f"--ipa {ipa_path} "
|
||||||
f"--output-dir . "
|
f"--output-dir {ROOT} "
|
||||||
f"--release-notes-dir . "
|
f"--output-name {metadata} "
|
||||||
f"--release-tag {release_tag} "
|
f"--release-notes-dir {ROOT} "
|
||||||
f"--version {version} "
|
f"--release-tag {release_tag} "
|
||||||
f"--marketing-version {marketing_version} "
|
f"--marketing-version {marketing_version} "
|
||||||
f"--short-commit {short_commit} "
|
f"--short-commit {short_commit} "
|
||||||
f"--release-channel {channel} "
|
f"--release-channel {channel} "
|
||||||
f"--bundle-id {bundle_id}"
|
f"--bundle-id {bundle_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# pass only if provided
|
||||||
|
if last_successful_commit:
|
||||||
|
cmd += f" --last-successful-commit {last_successful_commit}"
|
||||||
|
|
||||||
|
run(cmd)
|
||||||
|
|
||||||
|
run("git config user.name 'GitHub Actions'", check=False)
|
||||||
|
run("git config user.email 'github-actions@github.com'", check=False)
|
||||||
|
|
||||||
|
# ------------------------------------------------------
|
||||||
|
run("git fetch origin main", check=False, cwd=repo)
|
||||||
|
run("git switch main || git switch -c main origin/main", cwd=repo)
|
||||||
|
run("git reset --hard origin/main", cwd=repo)
|
||||||
|
# ------------------------------------------------------
|
||||||
|
|
||||||
|
max_attempts = 5
|
||||||
|
for attempt in range(1, max_attempts + 1):
|
||||||
|
if attempt > 1:
|
||||||
|
run("git fetch --depth=1 origin HEAD", check=False, cwd=repo)
|
||||||
|
run("git reset --hard FETCH_HEAD", check=False, cwd=repo)
|
||||||
|
|
||||||
|
# regenerate after reset so we don't lose changes
|
||||||
|
run(f"python3 {SCRIPTS}/update_source_metadata.py '{ROOT}/{metadata}' '{source_json_path}'", cwd=repo)
|
||||||
|
run(f"git add --verbose {source_json}", cwd=repo)
|
||||||
|
run(f"git commit -m '{release_tag} - deployed {marketing_version}' || true", cwd=repo)
|
||||||
|
|
||||||
|
rc = subprocess.call("git push", shell=True, cwd=repo)
|
||||||
|
|
||||||
|
if rc == 0:
|
||||||
|
print("Deploy push succeeded", file=sys.stderr)
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"Push rejected (attempt {attempt}/{max_attempts}), retrying...", file=sys.stderr)
|
||||||
|
time.sleep(0.5)
|
||||||
|
else:
|
||||||
|
raise SystemExit("Deploy push failed after retries")
|
||||||
|
|
||||||
|
def last_successful_commit(workflow, branch):
|
||||||
|
import json
|
||||||
|
|
||||||
|
try:
|
||||||
|
out = runAndGet(
|
||||||
|
f'gh run list '
|
||||||
|
f'--workflow "{workflow}" '
|
||||||
|
f'--json headSha,conclusion,headBranch'
|
||||||
)
|
)
|
||||||
|
|
||||||
run("git config user.name 'GitHub Actions'", check=False)
|
runs = json.loads(out)
|
||||||
run("git config user.email 'github-actions@github.com'", check=False)
|
|
||||||
|
|
||||||
run(f"python3 {ROOT}/scripts/update_source_metadata.py '{source_json}'")
|
for r in runs:
|
||||||
|
if r.get("conclusion") == "success" and r.get("headBranch") == branch:
|
||||||
|
return r["headSha"]
|
||||||
|
|
||||||
max_attempts = 5
|
except Exception:
|
||||||
for attempt in range(1, max_attempts + 1):
|
pass
|
||||||
run("git fetch --depth=1 origin HEAD", check=False)
|
|
||||||
run("git reset --hard FETCH_HEAD", check=False)
|
|
||||||
|
|
||||||
# regenerate after reset so we don't lose changes
|
return None
|
||||||
run(f"python3 {ROOT}/scripts/update_source_metadata.py '{source_json}'")
|
|
||||||
run(f"git add --verbose {source_json}", check=False)
|
|
||||||
run(f"git commit -m '{release_tag} - deployed {version}' || true", check=False)
|
|
||||||
|
|
||||||
rc = subprocess.call("git push", shell=True)
|
def upload_release(release_name, release_tag, commit_sha, repo, upstream_recommendation):
|
||||||
|
token = getenv("GH_TOKEN")
|
||||||
|
if token:
|
||||||
|
os.environ["GH_TOKEN"] = token
|
||||||
|
|
||||||
if rc == 0:
|
metadata_path = ROOT / "source-metadata.json"
|
||||||
print("Deploy push succeeded", file=sys.stderr)
|
|
||||||
break
|
|
||||||
|
|
||||||
print(f"Push rejected (attempt {attempt}/{max_attempts}), retrying...", file=sys.stderr)
|
if not metadata_path.exists():
|
||||||
time.sleep(0.5)
|
raise SystemExit("source-metadata.json missing")
|
||||||
else:
|
|
||||||
raise SystemExit("Deploy push failed after retries")
|
|
||||||
|
|
||||||
finally:
|
meta = json.loads(metadata_path.read_text())
|
||||||
run("popd", check=False)
|
|
||||||
|
marketing_version = meta.get("version_ipa")
|
||||||
|
is_beta = bool(meta.get("is_beta"))
|
||||||
|
build_datetime = meta.get("version_date")
|
||||||
|
|
||||||
|
dt = datetime.datetime.fromisoformat(
|
||||||
|
build_datetime.replace("Z", "+00:00")
|
||||||
|
)
|
||||||
|
built_time = dt.strftime("%a %b %d %H:%M:%S %Y")
|
||||||
|
built_date = dt.strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
release_notes = runAndGet(
|
||||||
|
f"python3 {SCRIPTS}/generate_release_notes.py "
|
||||||
|
f"--retrieve {release_tag} "
|
||||||
|
f"--output-dir {ROOT}"
|
||||||
|
)
|
||||||
|
# normalize section header
|
||||||
|
release_notes = re.sub(
|
||||||
|
r'^\s*#{1,6}\s*what(?:\'?s|\s+is)?\s+(?:new|changed).*',
|
||||||
|
"## What's Changed",
|
||||||
|
release_notes,
|
||||||
|
flags=re.IGNORECASE | re.MULTILINE,
|
||||||
|
)
|
||||||
|
|
||||||
|
upstream_block = ""
|
||||||
|
if upstream_recommendation and upstream_recommendation.strip():
|
||||||
|
upstream_block = upstream_recommendation.strip() + "\n\n"
|
||||||
|
|
||||||
|
raw_body = f"""
|
||||||
|
This is an ⚠️ **EXPERIMENTAL** ⚠️ {release_name} build for commit [{commit_sha}](https://github.com/{repo}/commit/{commit_sha}).
|
||||||
|
|
||||||
|
{release_name} builds are **extremely experimental builds only meant to be used by developers and beta testers. They often contain bugs and experimental features. Use at your own risk!**
|
||||||
|
|
||||||
|
{upstream_block}## Build Info
|
||||||
|
|
||||||
|
Built at (UTC): `{built_time}`
|
||||||
|
Built at (UTC date): `{built_date}`
|
||||||
|
Commit SHA: `{commit_sha}`
|
||||||
|
Version: `{marketing_version}`
|
||||||
|
"""
|
||||||
|
|
||||||
|
header = inspect.cleandoc(raw_body)
|
||||||
|
body = header + "\n\n" + release_notes.lstrip() + "\n"
|
||||||
|
|
||||||
|
body_file = ROOT / "release_body.md"
|
||||||
|
body_file.write_text(body, encoding="utf-8")
|
||||||
|
|
||||||
|
prerelease_flag = "--prerelease" if is_beta else ""
|
||||||
|
|
||||||
|
run(
|
||||||
|
f'gh release edit "{release_tag}" '
|
||||||
|
f'--title "{release_name}" '
|
||||||
|
f'--notes-file "{body_file}" '
|
||||||
|
f'{prerelease_flag}'
|
||||||
|
)
|
||||||
|
|
||||||
|
run(
|
||||||
|
f'gh release upload "{release_tag}" '
|
||||||
|
f'SideStore.ipa SideStore.dSYMs.zip encrypted-build-logs.zip'
|
||||||
|
f'--clobber'
|
||||||
|
)
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# ENTRYPOINT
|
# ENTRYPOINT
|
||||||
@@ -333,12 +448,14 @@ COMMANDS = {
|
|||||||
"commid-id" : (short_commit, 0, ""),
|
"commid-id" : (short_commit, 0, ""),
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# VERSION / MARKETING
|
# PROJECT INFO
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
"get-marketing-version" : (get_marketing_version, 0, ""),
|
"get-marketing-version" : (get_marketing_version, 0, ""),
|
||||||
"set-marketing-version" : (set_marketing_version, 1, "<qualified_version>"),
|
"set-marketing-version" : (set_marketing_version, 1, "<qualified_version>"),
|
||||||
"compute-qualified" : (compute_qualified_version, 4, "<marketing> <build_num> <channel> <short_commit>"),
|
"compute-qualified" : (compute_qualified_version, 4, "<marketing> <build_num> <channel> <short_commit>"),
|
||||||
"reserve_build_number" : (reserve_build_number, 1, "<repo>"),
|
"reserve_build_number" : (reserve_build_number, 1, "<repo>"),
|
||||||
|
"get-product-name" : (get_product_name, 0, ""),
|
||||||
|
"get-bundle-id" : (get_bundle_id, 0, ""),
|
||||||
|
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# CLEAN
|
# CLEAN
|
||||||
@@ -370,8 +487,12 @@ COMMANDS = {
|
|||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
# RELEASE / DEPLOY
|
# RELEASE / DEPLOY
|
||||||
# ----------------------------------------------------------
|
# ----------------------------------------------------------
|
||||||
"release-notes" : (release_notes, 1, "<tag>"),
|
"last-successful-commit" : (last_successful_commit, 2, "<workflow_name> <branch>"),
|
||||||
"deploy" : (deploy, 9, "<repo> <source_json> <release_tag> <short_commit> <marketing_version> <version> <channel> <bundle_id> <ipa_name>"),
|
"release-notes" : (release_notes, 1, "<tag>"),
|
||||||
|
"retrieve-release-notes" : (retrieve_release_notes, 1, "<tag>"),
|
||||||
|
"deploy" : (deploy, 9,
|
||||||
|
"<repo> <source_json> <release_tag> <short_commit> <marketing_version> <channel> <bundle_id> <ipa_name> [last_successful_commit]"),
|
||||||
|
"upload-release" : (upload_release, 5, "<release_name> <release_tag> <commit_sha> <repo> <upstream_recommendation>"),
|
||||||
}
|
}
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -399,7 +520,13 @@ def main():
|
|||||||
raise SystemExit(f"Usage: workflow.py {cmd}{suffix}")
|
raise SystemExit(f"Usage: workflow.py {cmd}{suffix}")
|
||||||
|
|
||||||
args = sys.argv[2:2 + argc]
|
args = sys.argv[2:2 + argc]
|
||||||
func(*args) if argc else func()
|
|
||||||
|
result = func(*args) if argc else func()
|
||||||
|
|
||||||
|
# ONLY real outputs go to stdout
|
||||||
|
if result is not None:
|
||||||
|
sys.stdout.write(str(result))
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
11
source-metadata.json
Normal file
11
source-metadata.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"is_beta": false,
|
||||||
|
"bundle_identifier": "com.SideStore.SideStore",
|
||||||
|
"version_ipa": "0.6.3",
|
||||||
|
"version_date": "2026-02-23T23:38:22Z",
|
||||||
|
"release_channel": "nightly",
|
||||||
|
"size": 29313346,
|
||||||
|
"sha256": "51ec327bca0b0056ccd4c2eb1a130cb7c5bb21de2f303251eea3e0a7336699c4",
|
||||||
|
"download_url": "https://github.com/SideStore/SideStore/releases/download/nightly/SideStore.ipa",
|
||||||
|
"localized_description": "This is release for:\n - version: \"0.6.3-nightly.2026.02.24.42+abc123de\"\n - revision: \"99712f00\"\n - timestamp: \"Mon Feb 23 23:38:22 2026\"\n\nRelease Notes:\n#### What's Changed\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- altsign updated to latest\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- re added openSSL from new path\n- updated altsign to use xcframework for openSSL which was causing huge download of 1.2 GB each time\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: improve more ci worflow\n- CI: full rewrite - moved logic into ci.py and kept workflow scripts mostly dummy\n\n#### Full Changelog: [38715283...99712f00](https://github.com/SideStore/SideStore/compare/38715283073ea37949a462b889ce3cad403ea499...99712f0020a4f2ae57d8d781514fa735f893c23a)"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user