mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-27 23:47:39 +01:00
CI: improve more ci worflow
This commit is contained in:
6
.github/workflows/alpha.yml
vendored
6
.github/workflows/alpha.yml
vendored
@@ -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 }}
|
||||||
|
|
||||||
|
|||||||
14
.github/workflows/nightly.yml
vendored
14
.github/workflows/nightly.yml
vendored
@@ -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"
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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():
|
||||||
|
|||||||
Reference in New Issue
Block a user