CI: improve more ci worflow

This commit is contained in:
mahee96
2026-02-24 05:27:15 +05:30
parent 99712f0020
commit 31d07534d0
9 changed files with 463 additions and 93 deletions

View File

@@ -16,6 +16,8 @@ jobs:
runs-on: macos-26
env:
REF_NAME: nightly
CHANNEL: nightly
BUNDLE_ID: com.SideStore.SideStore
steps:
- uses: actions/checkout@v4
@@ -166,13 +168,22 @@ jobs:
# --------------------------------------------------
- name: Deploy
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 \
Dependencies/apps-v2.json \
"_includes/source.json" \
"${{ env.REF_NAME }}" \
"$SOURCE_JSON" \
"$REF_NAME" \
"$SHORT_COMMIT" \
"$MARKETING_VERSION" \
"$VERSION" \
"${{ env.REF_NAME }}" \
"com.SideStore.SideStore" \
"SideStore.ipa"
"$CHANNEL" \
"$BUNDLE_ID" \
"$IPA_NAME" \
"$LAST_SUCCESSFUL_COMMIT"

View File

@@ -881,6 +881,90 @@
remoteGlobalIDString = 191E5FAA290A5D92001A3B7C;
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 */ = {
isa = PBXContainerItemProxy;
containerPortal = A8A5AB6D2F4C2CFC00572B4A /* minimuxer.xcodeproj */;
@@ -2168,6 +2252,12 @@
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>"; };
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; };
A8A5A7E82F4C2CFC00572B4A /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = "<group>"; };
A8A5A7E92F4C2CFC00572B4A /* build.rs */ = {isa = PBXFileReference; lastKnownFileType = text; path = build.rs; sourceTree = "<group>"; };
@@ -3231,6 +3321,60 @@
name = Products;
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 */ = {
isa = PBXGroup;
children = (
@@ -4497,6 +4641,10 @@
ProductGroup = A8A5B06A2F4C347700572B4A /* Products */;
ProjectRef = A8A5B0622F4C347700572B4A /* libfragmentzip.xcodeproj */;
},
{
ProductGroup = A8636E9B2F4CF74D00E66784 /* Products */;
ProjectRef = A8636E922F4CF74D00E66784 /* libfragmentzip.xcodeproj */;
},
{
ProductGroup = A8FAC0612F4B50D10061A851 /* Products */;
ProjectRef = A8FABCE72F4B50D10061A851 /* libfragmentzip.xcodeproj */;
@@ -4609,6 +4757,10 @@
ProductGroup = A8EEDA212F4B19B000F2436D /* Products */;
ProjectRef = A8EED1D72F4B19B000F2436D /* libgeneral.xcodeproj */;
},
{
ProductGroup = A8636E992F4CF74D00E66784 /* Products */;
ProjectRef = A8636E6E2F4CF74D00E66784 /* libgeneral.xcodeproj */;
},
{
ProductGroup = A81197412F4C1C710013ABD0 /* Products */;
ProjectRef = A81193E22F4C1C710013ABD0 /* libgeneral.xcodeproj */;
@@ -4773,6 +4925,10 @@
ProductGroup = A8A5B7332F4C4C8600572B4A /* Products */;
ProjectRef = A8A5B7322F4C4C8600572B4A /* Roxas.xcodeproj */;
},
{
ProductGroup = A86372122F4CF74D00E66784 /* Products */;
ProjectRef = A86372112F4CF74D00E66784 /* Roxas.xcodeproj */;
},
{
ProductGroup = A8EEDA272F4B19B000F2436D /* Products */;
ProjectRef = A8EEDA1E2F4B19B000F2436D /* Roxas.xcodeproj */;
@@ -4817,6 +4973,10 @@
ProductGroup = A81180DC2F4C1B230013ABD0 /* Products */;
ProjectRef = A81180302F4C1B230013ABD0 /* SampleApp.xcodeproj */;
},
{
ProductGroup = A863719F2F4CF74D00E66784 /* Products */;
ProjectRef = A86371872F4CF74D00E66784 /* SampleApp.xcodeproj */;
},
{
ProductGroup = A81197372F4C1C710013ABD0 /* Products */;
ProjectRef = A81196912F4C1C710013ABD0 /* SampleApp.xcodeproj */;
@@ -4865,6 +5025,10 @@
ProductGroup = A8A5ACE92F4C339400572B4A /* Products */;
ProjectRef = A8A5ACD02F4C339400572B4A /* SampleApp.xcodeproj */;
},
{
ProductGroup = A863719B2F4CF74D00E66784 /* Products */;
ProjectRef = A863716E2F4CF74D00E66784 /* SampleApp.xcodeproj */;
},
{
ProductGroup = A8A5ACE72F4C339400572B4A /* Products */;
ProjectRef = A8A5ACB72F4C339400572B4A /* SampleApp.xcodeproj */;
@@ -4973,6 +5137,10 @@
ProductGroup = A81197392F4C1C710013ABD0 /* Products */;
ProjectRef = A811969D2F4C1C710013ABD0 /* SwiftSampleApp.xcodeproj */;
},
{
ProductGroup = A863719D2F4CF74D00E66784 /* Products */;
ProjectRef = A86371932F4CF74D00E66784 /* SwiftSampleApp.xcodeproj */;
},
{
ProductGroup = A8FAC0592F4B50D10061A851 /* Products */;
ProjectRef = A8FABF7E2F4B50D10061A851 /* SwiftSampleApp.xcodeproj */;
@@ -5860,6 +6028,90 @@
remoteRef = A85AED342F4B2315002E2E11 /* PBXContainerItemProxy */;
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 */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;

37
release-notes.md Normal file
View 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)

View File

@@ -19,6 +19,20 @@ def run(cmd: str) -> str:
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():
return run("git rev-parse HEAD")
@@ -35,12 +49,8 @@ def repo_url():
def commit_messages(start, end="HEAD"):
try:
out = run(f"git log {start}..{end} --pretty=format:%s")
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()
out = run(f"git log {start}..{end} --pretty=format:%s")
return out.splitlines() if out else []
def authors(range_expr, fmt="%an"):
@@ -76,10 +86,35 @@ def fmt_author(author):
# 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):
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)
# 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"{HEADER_MARKER} What's Changed\n"
@@ -90,7 +125,8 @@ def generate_release_notes(last_successful, tag, branch):
section += f"{fmt_msg(m)}\n"
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:
section += f"\n{HEADER_MARKER} New Contributors\n"
@@ -170,7 +206,7 @@ def update_release_md(existing, new_section, tag):
# retrieval
# ----------------------------------------------------------
def retrieve_tag(tag, file_path):
def retrieve_tag(tag, file_path: Path):
if not file_path.exists():
return ""
@@ -209,30 +245,20 @@ def main():
" generate_release_notes.py --retrieve <tag> [--output-dir DIR]"
)
# parse optional output dir
output_dir = Path.cwd()
if "--output-dir" in args:
idx = args.index("--output-dir")
try:
output_dir = Path(args[idx + 1]).resolve()
except IndexError:
sys.exit("Missing value for --output-dir")
output_dir = Path(args[idx + 1]).resolve()
del args[idx:idx + 2]
output_dir.mkdir(parents=True, exist_ok=True)
release_file = output_dir / "release-notes.md"
# retrieval mode
if args[0] == "--retrieve":
if len(args) < 2:
sys.exit("Missing tag after --retrieve")
print(retrieve_tag(args[1], release_file))
return
# generation mode
last_successful = args[0]
tag = args[1] if len(args) > 1 else head_commit()
branch = args[2] if len(args) > 2 else (
@@ -241,12 +267,7 @@ def main():
new_section = generate_release_notes(last_successful, tag, branch)
existing = (
release_file.read_text()
if release_file.exists()
else ""
)
existing = release_file.read_text() if release_file.exists() else ""
updated = update_release_md(existing, new_section, tag)
release_file.write_text(updated)

View File

@@ -5,16 +5,33 @@ import json
import subprocess
from pathlib import Path
import argparse
import sys
SCRIPT_DIR = Path(__file__).resolve().parent
# ----------------------------------------------------------
# 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:
return subprocess.check_output(
cmd, shell=True, cwd=cwd
).decode().strip()
try:
return subprocess.check_output(
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:
@@ -38,35 +55,16 @@ def sha256(path: Path) -> str:
def main():
p = argparse.ArgumentParser()
p.add_argument(
"--repo-root",
required=True,
help="Repo used for git history + release notes",
)
p.add_argument("--repo-root", required=True)
p.add_argument("--ipa", required=True)
p.add_argument("--output-dir", required=True)
p.add_argument(
"--ipa",
required=True,
help="Path to IPA file",
)
p.add_argument(
"--output-dir",
required=True,
help="Output Directory where source_metadata.json is written",
)
p.add_argument(
"--output-name",
default="source_metadata.json",
help="Output metadata filename",
)
p.add_argument(
"--release-notes-dir",
required=True,
help="Output Directory where release-notes.md is generated/read",
)
p.add_argument("--release-notes-dir", required=True)
p.add_argument("--release-tag", required=True)
p.add_argument("--version", required=True)
@@ -74,6 +72,10 @@ def main():
p.add_argument("--short-commit", required=True)
p.add_argument("--release-channel", required=True)
p.add_argument("--bundle-id", required=True)
# optional
p.add_argument("--last-successful-commit")
p.add_argument("--is-beta", action="store_true")
args = p.parse_args()
@@ -95,19 +97,27 @@ def main():
out_file = out_dir / args.output_name
# ------------------------------------------------------
# ensure release notes exist
# generate release notes
# ------------------------------------------------------
print("Generating release notes…")
sh(
(
"python3 generate_release_notes.py "
script = resolve_script("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"--output-dir \"{notes_dir}\""
),
cwd=repo_root,
)
)
sh(gen_cmd, cwd=repo_root)
# ------------------------------------------------------
# retrieve release notes
@@ -115,7 +125,7 @@ def main():
notes = sh(
(
"python3 generate_release_notes.py "
f"python3 {script} "
f"--retrieve {args.release_tag} "
f"--output-dir \"{notes_dir}\""
),
@@ -126,7 +136,7 @@ def main():
# compute metadata
# ------------------------------------------------------
now = datetime.datetime.now(datetime.UTC)
now = datetime.datetime.now(datetime.timezone.utc)
formatted = now.strftime("%Y-%m-%dT%H:%M:%SZ")
human = now.strftime("%c")

View File

@@ -5,23 +5,6 @@ import sys
from pathlib import Path
'''
metadata.json template
{
"version_ipa": "0.0.0",
"version_date": "2000-12-18T00:00:00Z",
"is_beta": true,
"release_channel": "alpha",
"size": 0,
"sha256": "",
"localized_description": "Invalid Update",
"download_url": "https://github.com/SideStore/SideStore/releases/download/0.0.0/SideStore.ipa",
"bundle_identifier": "com.SideStore.SideStore"
}
'''
# ----------------------------------------------------------
# args
# ----------------------------------------------------------
@@ -89,7 +72,7 @@ RELEASE_CHANNEL = RELEASE_CHANNEL.lower()
# ----------------------------------------------------------
# load or create source.json
# load source.json
# ----------------------------------------------------------
if source_file.exists():
@@ -108,7 +91,7 @@ if int(data.get("version", 1)) < 2:
# ----------------------------------------------------------
# ensure app entry exists
# locate app
# ----------------------------------------------------------
apps = data.setdefault("apps", [])
@@ -128,7 +111,7 @@ if app is None:
# ----------------------------------------------------------
# update logic
# update storefront metadata (stable only)
# ----------------------------------------------------------
if RELEASE_CHANNEL == "stable":
@@ -141,6 +124,11 @@ if RELEASE_CHANNEL == "stable":
"downloadURL": DOWNLOAD_URL,
})
# ----------------------------------------------------------
# releaseChannels update (ORIGINAL FORMAT)
# ----------------------------------------------------------
channels = app.setdefault("releaseChannels", [])
new_version = {
@@ -152,19 +140,31 @@ new_version = {
"sha256": SHA256,
}
tracks = [t for t in channels if t.get("track") == RELEASE_CHANNEL]
# find track
tracks = [
t for t in channels
if isinstance(t, dict) and t.get("track") == RELEASE_CHANNEL
]
if len(tracks) > 1:
print(f"Multiple tracks named {RELEASE_CHANNEL}")
sys.exit(1)
if not tracks:
# create new track at top (original behaviour)
channels.insert(0, {
"track": RELEASE_CHANNEL,
"releases": [new_version],
})
else:
tracks[0]["releases"][0] = new_version
track = tracks[0]
releases = track.setdefault("releases", [])
if not releases:
releases.append(new_version)
else:
# replace top entry only (original logic)
releases[0] = new_version
# ----------------------------------------------------------

View File

@@ -253,7 +253,7 @@ def release_notes(tag):
# 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, version, channel, bundle_id, ipa_name, last_successful_commit=None):
repo = (ROOT / repo).resolve()
ipa_path = ROOT / ipa_name
source_path = repo / source_json
@@ -268,8 +268,7 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, vers
if not source_path.exists():
raise SystemExit(f"{source_json} missing inside repo")
run(
cmd = (
f"python3 {SCRIPTS}/generate_source_metadata.py "
f"--repo-root {ROOT} "
f"--ipa {ipa_path} "
@@ -284,6 +283,12 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, vers
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)
@@ -309,6 +314,27 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, vers
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'
)
runs = json.loads(out)
for r in runs:
if r.get("conclusion") == "success" and r.get("headBranch") == branch:
return r["headSha"]
except Exception:
pass
return None
# ----------------------------------------------------------
# ENTRYPOINT
# ----------------------------------------------------------
@@ -357,8 +383,10 @@ COMMANDS = {
# ----------------------------------------------------------
# RELEASE / DEPLOY
# ----------------------------------------------------------
"release-notes" : (release_notes, 1, "<tag>"),
"deploy" : (deploy, 9, "<repo> <source_json> <release_tag> <short_commit> <marketing_version> <version> <channel> <bundle_id> <ipa_name>"),
"last-successful-commit" : (last_successful_commit, 2, "<workflow_name> <branch>"),
"release-notes" : (release_notes, 1, "<tag>"),
"deploy" : (deploy, 10,
"<repo> <source_json> <release_tag> <short_commit> <marketing_version> <version> <channel> <bundle_id> <ipa_name> [last_successful_commit]"),
}
def main():

11
source-metadata.json Normal file
View 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)"
}