CI: improve more ci worflow

This commit is contained in:
mahee96
2026-02-24 06:22:03 +05:30
parent 3a05485c40
commit 3d47d486ef
5 changed files with 216 additions and 160 deletions

View File

@@ -62,7 +62,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
with: with:
path: | path: |
~/Library/Alphaer/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 }}
@@ -72,7 +72,7 @@ jobs:
uses: actions/cache/restore@v3 uses: actions/cache/restore@v3
with: with:
path: | path: |
~/Library/Alphaer/Xcode/DerivedData ~/Library/Developer/Xcode/DerivedData
~/Library/Caches/org.swift.swiftpm ~/Library/Caches/org.swift.swiftpm
key: xcode-build-cache-${{ github.ref_name }}- key: xcode-build-cache-${{ github.ref_name }}-
@@ -117,7 +117,7 @@ jobs:
uses: actions/cache/save@v3 uses: actions/cache/save@v3
with: with:
path: | path: |
~/Library/Alphaer/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 }}

View File

@@ -17,7 +17,6 @@ jobs:
env: env:
REF_NAME: nightly REF_NAME: nightly
CHANNEL: nightly CHANNEL: nightly
BUNDLE_ID: com.SideStore.SideStore
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -167,6 +166,8 @@ jobs:
# deploy # deploy
# -------------------------------------------------- # --------------------------------------------------
- name: Deploy - name: Deploy
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: | run: |
PRODUCT_NAME=$(python3 scripts/ci/workflow.py get-product-name) PRODUCT_NAME=$(python3 scripts/ci/workflow.py get-product-name)
BUNDLE_ID=$(python3 scripts/ci/workflow.py get-bundle-id) BUNDLE_ID=$(python3 scripts/ci/workflow.py get-bundle-id)
@@ -187,3 +188,14 @@ jobs:
"$BUNDLE_ID" \ "$BUNDLE_ID" \
"$IPA_NAME" \ "$IPA_NAME" \
"$LAST_SUCCESSFUL_COMMIT" "$LAST_SUCCESSFUL_COMMIT"
python3 scripts/ci/workflow.py upload-release \
"$RELEASE_NAME" \
"$RELEASE_TAG" \
"$IS_BETA" \
"$VERSION" \
"$GITHUB_SHA" \
"$GITHUB_REPOSITORY" \
"$BUILT_DATE" \
"$BUILT_DATE_ALT" \
"$RELEASE_NOTES"

View File

@@ -5,6 +5,7 @@ import json
import subprocess import subprocess
from pathlib import Path from pathlib import Path
import argparse import argparse
import textwrap
import sys import sys
SCRIPT_DIR = Path(__file__).resolve().parent SCRIPT_DIR = Path(__file__).resolve().parent
@@ -140,7 +141,7 @@ def main():
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.version}"
- revision: "{args.short_commit}" - revision: "{args.short_commit}"
@@ -148,7 +149,7 @@ This is release for:
Release Notes: Release Notes:
{notes} {notes}
""".strip() """).strip()
metadata = { metadata = {
"is_beta": bool(args.is_beta), "is_beta": bool(args.is_beta),

View File

@@ -6,154 +6,126 @@ from pathlib import Path
# ---------------------------------------------------------- # ----------------------------------------------------------
# args # metadata
# ----------------------------------------------------------
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])
# ----------------------------------------------------------
# load metadata
# ---------------------------------------------------------- # ----------------------------------------------------------
def load_metadata(metadata_file: Path):
if not metadata_file.exists(): if not metadata_file.exists():
print(f"Missing metadata file: {metadata_file}") raise SystemExit(f"Missing metadata file: {metadata_file}")
sys.exit(1)
with open(metadata_file, "r", encoding="utf-8") as f: with open(metadata_file, "r", encoding="utf-8") as f:
meta = json.load(f) meta = json.load(f)
VERSION_IPA = meta.get("version_ipa")
VERSION_DATE = meta.get("version_date")
IS_BETA = meta.get("is_beta")
RELEASE_CHANNEL = meta.get("release_channel")
SIZE = meta.get("size")
SHA256 = meta.get("sha256")
LOCALIZED_DESCRIPTION = meta.get("localized_description")
DOWNLOAD_URL = meta.get("download_url")
BUNDLE_IDENTIFIER = meta.get("bundle_identifier")
print(" ====> Required parameter list <====") print(" ====> Required parameter list <====")
print("Bundle Identifier:", BUNDLE_IDENTIFIER) for k, v in meta.items():
print("Version:", VERSION_IPA) print(f"{k}: {v}")
print("Version Date:", VERSION_DATE)
print("IsBeta:", IS_BETA) required = [
print("ReleaseChannel:", RELEASE_CHANNEL) "bundle_identifier",
print("Size:", SIZE) "version_ipa",
print("Sha256:", SHA256) "version_date",
print("Localized Description:", LOCALIZED_DESCRIPTION) "release_channel",
print("Download URL:", DOWNLOAD_URL) "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
# ---------------------------------------------------------- # ----------------------------------------------------------
# validation # source loading
# ----------------------------------------------------------
if (
not BUNDLE_IDENTIFIER
or not VERSION_IPA
or not VERSION_DATE
or not RELEASE_CHANNEL
or not SIZE
or not SHA256
or not LOCALIZED_DESCRIPTION
or not DOWNLOAD_URL
):
print("One or more required metadata fields missing")
sys.exit(1)
SIZE = int(SIZE)
RELEASE_CHANNEL = RELEASE_CHANNEL.lower()
# ----------------------------------------------------------
# load source.json
# ---------------------------------------------------------- # ----------------------------------------------------------
def load_source(source_file: Path):
if source_file.exists(): if source_file.exists():
with open(source_file, "r", encoding="utf-8") as f: with open(source_file, "r", encoding="utf-8") as f:
data = json.load(f) data = json.load(f)
else: else:
print("source.json missing — creating minimal structure") print("source.json missing — creating minimal structure")
data = { data = {"version": 2, "apps": []}
"version": 2,
"apps": []
}
if int(data.get("version", 1)) < 2: if int(data.get("version", 1)) < 2:
print("Only v2 and above are supported") raise SystemExit("Only v2 and above are supported")
sys.exit(1)
return data
# ---------------------------------------------------------- # ----------------------------------------------------------
# locate app # locate app
# ---------------------------------------------------------- # ----------------------------------------------------------
def ensure_app(data, bundle_id):
apps = data.setdefault("apps", []) apps = data.setdefault("apps", [])
app = next( app = next(
(a for a in apps if a.get("bundleIdentifier") == BUNDLE_IDENTIFIER), (a for a in apps if a.get("bundleIdentifier") == bundle_id),
None None,
) )
if app is None: if app is None:
print("App entry missing — creating new app entry") print("App entry missing — creating new app entry")
app = { app = {
"bundleIdentifier": BUNDLE_IDENTIFIER, "bundleIdentifier": bundle_id,
"releaseChannels": [] "releaseChannels": [],
} }
apps.append(app) apps.append(app)
return app
# ---------------------------------------------------------- # ----------------------------------------------------------
# update storefront metadata (stable only) # update storefront
# ---------------------------------------------------------- # ----------------------------------------------------------
if RELEASE_CHANNEL == "stable": def update_storefront_if_needed(app, meta):
if meta["release_channel"] == "stable":
app.update({ app.update({
"version": VERSION_IPA, "version": meta["version_ipa"],
"versionDate": VERSION_DATE, "versionDate": meta["version_date"],
"size": SIZE, "size": meta["size"],
"sha256": SHA256, "sha256": meta["sha256"],
"localizedDescription": LOCALIZED_DESCRIPTION, "localizedDescription": meta["localized_description"],
"downloadURL": DOWNLOAD_URL, "downloadURL": meta["download_url"],
}) })
# ---------------------------------------------------------- # ----------------------------------------------------------
# releaseChannels update (ORIGINAL FORMAT) # update release channel (ORIGINAL FORMAT)
# ---------------------------------------------------------- # ----------------------------------------------------------
def update_release_channel(app, meta):
channels = app.setdefault("releaseChannels", []) channels = app.setdefault("releaseChannels", [])
new_version = { new_version = {
"version": VERSION_IPA, "version": meta["version_ipa"],
"date": VERSION_DATE, "date": meta["version_date"],
"localizedDescription": LOCALIZED_DESCRIPTION, "localizedDescription": meta["localized_description"],
"downloadURL": DOWNLOAD_URL, "downloadURL": meta["download_url"],
"size": SIZE, "size": meta["size"],
"sha256": SHA256, "sha256": meta["sha256"],
} }
# find track
tracks = [ tracks = [
t for t in channels t for t in channels
if isinstance(t, dict) and t.get("track") == RELEASE_CHANNEL if isinstance(t, dict)
and t.get("track") == meta["release_channel"]
] ]
if len(tracks) > 1: if len(tracks) > 1:
print(f"Multiple tracks named {RELEASE_CHANNEL}") raise SystemExit(f"Multiple tracks named {meta['release_channel']}")
sys.exit(1)
if not tracks: if not tracks:
# create new track at top (original behaviour)
channels.insert(0, { channels.insert(0, {
"track": RELEASE_CHANNEL, "track": meta["release_channel"],
"releases": [new_version], "releases": [new_version],
}) })
else: else:
@@ -163,7 +135,6 @@ else:
if not releases: if not releases:
releases.append(new_version) releases.append(new_version)
else: else:
# replace top entry only (original logic)
releases[0] = new_version releases[0] = new_version
@@ -171,10 +142,40 @@ else:
# save # save
# ---------------------------------------------------------- # ----------------------------------------------------------
def save_source(source_file: Path, data):
print("\nUpdated Sources File:\n") print("\nUpdated Sources File:\n")
print(json.dumps(data, indent=2, ensure_ascii=False)) print(json.dumps(data, indent=2, ensure_ascii=False))
source_file.parent.mkdir(parents=True, exist_ok=True)
with open(source_file, "w", encoding="utf-8") as f: with open(source_file, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False) json.dump(data, f, indent=2, ensure_ascii=False)
print("JSON successfully updated.") 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()

View File

@@ -6,6 +6,7 @@ import datetime
from pathlib import Path from pathlib import Path
import time import time
import json import json
import textwrap
# REPO ROOT relative to script dir # REPO ROOT relative to script dir
@@ -272,7 +273,7 @@ def release_notes(tag):
def deploy(repo, source_json, release_tag, short_commit, marketing_version, version, channel, bundle_id, ipa_name, last_successful_commit=None): 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() repo = (ROOT / repo).resolve()
ipa_path = ROOT / ipa_name ipa_path = ROOT / ipa_name
source_path = repo / source_json source_json_path = repo / source_json
metadata = 'source-metadata.json' metadata = 'source-metadata.json'
if not repo.exists(): if not repo.exists():
@@ -281,7 +282,7 @@ 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")
if not source_path.exists(): if not source_json_path.exists():
raise SystemExit(f"{source_json} missing inside repo") raise SystemExit(f"{source_json} missing inside repo")
cmd = ( cmd = (
@@ -315,7 +316,7 @@ def deploy(repo, source_json, release_tag, short_commit, marketing_version, vers
run("git reset --hard FETCH_HEAD", check=False, cwd=repo) run("git reset --hard FETCH_HEAD", check=False, cwd=repo)
# regenerate after reset so we don't lose changes # regenerate after reset so we don't lose changes
run(f"python3 {SCRIPTS}/update_source_metadata.py '{ROOT}/{metadata}' '{source_json}'") run(f"python3 {SCRIPTS}/update_source_metadata.py '{ROOT}/{metadata}' '{source_json_path}'")
run(f"git add --verbose {source_json}", check=False) run(f"git add --verbose {source_json}", check=False)
run(f"git commit -m '{release_tag} - deployed {version}' || true", check=False) run(f"git commit -m '{release_tag} - deployed {version}' || true", check=False)
@@ -351,6 +352,45 @@ def last_successful_commit(workflow, branch):
return None return None
def upload_release(release_name, release_tag,is_beta,version,commit_sha,repo,built_date,built_date_alt,upstream_recommendation,release_notes):
token = getenv("GH_TOKEN")
if token:
os.environ["GH_TOKEN"] = token
body = textwrap.dedent(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_recommendation}
## Build Info
Built at (UTC): `{built_date}`
Built at (UTC date): `{built_date_alt}`
Commit SHA: `{commit_sha}`
Version: `{version}`
{release_notes}
""")
body_file = ROOT / "release_body.md"
body_file.write_text(body, encoding="utf-8")
prerelease_flag = "--prerelease" if str(is_beta).lower() in ["true", "1", "yes"] 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 '
f'--clobber'
)
# ---------------------------------------------------------- # ----------------------------------------------------------
# ENTRYPOINT # ENTRYPOINT
# ---------------------------------------------------------- # ----------------------------------------------------------
@@ -405,6 +445,8 @@ COMMANDS = {
"release-notes" : (release_notes, 1, "<tag>"), "release-notes" : (release_notes, 1, "<tag>"),
"deploy" : (deploy, 10, "deploy" : (deploy, 10,
"<repo> <source_json> <release_tag> <short_commit> <marketing_version> <version> <channel> <bundle_id> <ipa_name> [last_successful_commit]"), "<repo> <source_json> <release_tag> <short_commit> <marketing_version> <version> <channel> <bundle_id> <ipa_name> [last_successful_commit]"),
"upload-release" : (upload_release, 9,
"<release_name> <release_tag> <is_beta> <version> <commit_sha> <repo> <built_date> <built_date_alt> <release_notes>"),
} }
def main(): def main():