Skip to content
This repository was archived by the owner on Apr 5, 2026. It is now read-only.

Commit 838d5c9

Browse files
authored
Add AltStore Source Support (#416)
* add altstore source * rename altstore source
1 parent 85baac6 commit 838d5c9

3 files changed

Lines changed: 289 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Update AltStore Source
2+
3+
on:
4+
workflow_run:
5+
workflows: ["Build ALL"]
6+
types: [completed]
7+
workflow_dispatch:
8+
9+
jobs:
10+
update-source:
11+
runs-on: ubuntu-latest
12+
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v3
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: '3.x'
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install requests
26+
27+
- name: Record job start time
28+
id: job_start_time
29+
run: echo "start_time=$(date +%s)" >> $GITHUB_OUTPUT
30+
31+
- name: Update AltStore source
32+
id: update_source
33+
env:
34+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
run: |
36+
python update_alt_store.py
37+
git config --global user.name 'GitHub Action'
38+
git config --global user.email 'action@github.com'
39+
git add alt_store.json
40+
if git diff --staged --quiet; then
41+
echo "changes=false" >> $GITHUB_OUTPUT
42+
else
43+
git commit -m "Updated source with latest release"
44+
git push
45+
echo "changes=true" >> $GITHUB_OUTPUT
46+
fi
47+
48+
- name: Calculate job duration
49+
id: duration
50+
if: always()
51+
run: |
52+
end_time=$(date +%s)
53+
duration=$((end_time - ${{ steps.job_start_time.outputs.start_time }}))
54+
echo "duration=$duration seconds" >> $GITHUB_OUTPUT
55+
56+
- name: Create job summary
57+
run: |
58+
if [[ "${{ steps.update_source.outputs.changes }}" == "true" ]]; then
59+
echo "## Update Altstore Source Summary 🚀" >> $GITHUB_STEP_SUMMARY
60+
echo "✅ Changes Detected and Applied" >> $GITHUB_STEP_SUMMARY
61+
echo "" >> $GITHUB_STEP_SUMMARY
62+
echo "The alt_store.json file has been updated with the latest release information." >> $GITHUB_STEP_SUMMARY
63+
else
64+
echo "## Update Altstore Source Summary 🚀" >> $GITHUB_STEP_SUMMARY
65+
echo "🔍 No Changes Detected" >> $GITHUB_STEP_SUMMARY
66+
echo "" >> $GITHUB_STEP_SUMMARY
67+
echo "The alt_store.json file is up to date. No changes were necessary." >> $GITHUB_STEP_SUMMARY
68+
fi
69+
echo "" >> $GITHUB_STEP_SUMMARY
70+
echo "🕐 Execution Time" >> $GITHUB_STEP_SUMMARY
71+
echo "" >> $GITHUB_STEP_SUMMARY
72+
echo "This job took ${{ steps.duration.outputs.duration }} to complete." >> $GITHUB_STEP_SUMMARY
73+
echo "" >> $GITHUB_STEP_SUMMARY
74+
echo "📆 Next Scheduled Run" >> $GITHUB_STEP_SUMMARY
75+
echo "" >> $GITHUB_STEP_SUMMARY
76+
echo "The next scheduled run will be tomorrow at midnight UTC." >> $GITHUB_STEP_SUMMARY

alt_store.json

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "Venera",
3+
"identifier": "com.github.wgh136.venera.source",
4+
"website": "https://github.com/venera-app/venera",
5+
"subtitle": "Venera official AltStore Source.",
6+
"description": "This is the official AltStore Source for Venera.\n\n A comic reader that supports reading local and network comics",
7+
"tintColor": "#0784FC",
8+
"iconURL": "https://raw.githubusercontent.com/venera-app/venera/master/assets/app_icon.png",
9+
"apps": [
10+
{
11+
"beta": false,
12+
"name": "Venera",
13+
"bundleIdentifier": "com.github.wgh136.venera",
14+
"developerName": "wgh136",
15+
"subtitle": "A comic reader that supports reading local and network comics",
16+
"version": "1.4.5",
17+
"versionDate": "2025-06-18",
18+
"versionDescription": "1. Fixed an abnormal single image height issue when \"imagesPerPage > 1\". 379 \r\n2. Fixed an invalid page calculation issue when \"showSingleImageOnFirstPage\" is enabled. \r\n3. Fixed an issue with incorrect reading history when displaying a single image on the first page. \r\n4. Fixed abnormal history recording when pages are not flipped. 392 \r\n5. Fixed an issue where the download task would stop after exiting the reader. 387 \r\n6. Fixed a \"RangeError\" when translating tags. 356 \r\n7. Reset the current folder to null on the favorites page if the folder is invalid. 389 \r\n8. Fixed various issues when using a custom download path on Android. 400 \r\n9. Set the initial chapter to the first downloaded chapter if no history exists when starting to read a local comic. 405 \r\n10. Removed the config file repository URL from the app.",
19+
"downloadURL": "https://github.com/venera-app/venera/releases/download/v1.4.5/venera-ios-1.4.5%2B145.ipa",
20+
"localizedDescription": "A comic reader that supports reading local and network comics",
21+
"iconURL": "https://raw.githubusercontent.com/venera-app/venera/master/assets/app_icon.png",
22+
"tintColor": "#0784FC",
23+
"category": "utilities",
24+
"size": 14960268,
25+
"appPermissions": {
26+
"entitlements": [
27+
"application-identifier",
28+
"com.apple.security.application-groups",
29+
"get-task-allow",
30+
"keychain-access-groups",
31+
"com.apple.developer.kernel.extended-virtual-addressing",
32+
"com.apple.developer.kernel.increased-memory-limit",
33+
"com.apple.developer.healthkit.background-delivery"
34+
],
35+
"privacy": {
36+
"NSFaceIDUsageDescription": "The guest app is requesting for this permission.",
37+
"NSPhotoLibraryAddUsageDescription": "The guest app is requesting for this permission."
38+
}
39+
},
40+
"versions": [
41+
{
42+
"version": "1.4.5",
43+
"date": "2025-06-18",
44+
"localizedDescription": "1. Fixed an abnormal single image height issue when \"imagesPerPage > 1\". 379 \r\n2. Fixed an invalid page calculation issue when \"showSingleImageOnFirstPage\" is enabled. \r\n3. Fixed an issue with incorrect reading history when displaying a single image on the first page. \r\n4. Fixed abnormal history recording when pages are not flipped. 392 \r\n5. Fixed an issue where the download task would stop after exiting the reader. 387 \r\n6. Fixed a \"RangeError\" when translating tags. 356 \r\n7. Reset the current folder to null on the favorites page if the folder is invalid. 389 \r\n8. Fixed various issues when using a custom download path on Android. 400 \r\n9. Set the initial chapter to the first downloaded chapter if no history exists when starting to read a local comic. 405 \r\n10. Removed the config file repository URL from the app.",
45+
"downloadURL": "https://github.com/venera-app/venera/releases/download/v1.4.5/venera-ios-1.4.5%2B145.ipa",
46+
"size": 14960268
47+
}
48+
]
49+
}
50+
],
51+
"news": [
52+
{
53+
"appID": "com.github.wgh136.venera",
54+
"caption": "Update of Venera just got released!",
55+
"date": "2025-06-18T09:02:01Z",
56+
"identifier": "release-v1.4.5",
57+
"notify": true,
58+
"tintColor": "#0784FC",
59+
"title": "v1.4.5 - Venera 18/06/25",
60+
"url": "https://github.com/venera-app/venera/releases/tag/v1.4.5"
61+
}
62+
]
63+
}

update_alt_store.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import json
2+
import plistlib
3+
import re
4+
import requests
5+
import os
6+
from datetime import datetime
7+
8+
def prepare_description(text):
9+
text = re.sub('<[^<]+?>', '', text) # Remove HTML tags
10+
text = re.sub(r'#{1,6}\s?', '', text) # Remove markdown header tags
11+
text = re.sub(r'\*{2}', '', text) # Remove all occurrences of two consecutive asterisks
12+
text = re.sub(r'(?<=\r|\n)-', '•', text) # Only replace - with • if it is preceded by \r or \n
13+
text = re.sub(r'`', '"', text) # Replace ` with "
14+
text = re.sub(r'\r\n\r\n', '\r \n', text) # Replace \r\n\r\n with \r \n (avoid incorrect display of the description regarding paragraphs)
15+
return text
16+
17+
def fetch_latest_release(repo_url):
18+
api_url = f"https://api.github.com/repos/{repo_url}/releases"
19+
headers = {
20+
"Accept": "application/vnd.github+json",
21+
}
22+
try:
23+
response = requests.get(api_url, headers=headers)
24+
response.raise_for_status()
25+
release = response.json()
26+
return release
27+
except requests.RequestException as e:
28+
print(f"Error fetching releases: {e}")
29+
raise
30+
31+
def get_file_size(url):
32+
try:
33+
response = requests.head(url)
34+
response.raise_for_status()
35+
return int(response.headers.get('Content-Length', 0))
36+
except requests.RequestException as e:
37+
print(f"Error getting file size: {e}")
38+
return 194586
39+
40+
def update_json_file_release(json_file, latest_release):
41+
if isinstance(latest_release, list) and latest_release:
42+
latest_release = latest_release[0]
43+
else:
44+
print("Error getting latest release")
45+
return
46+
47+
try:
48+
with open(json_file, "r") as file:
49+
data = json.load(file)
50+
except json.JSONDecodeError as e:
51+
print(f"Error reading JSON file: {e}")
52+
data = {"apps": []}
53+
raise
54+
55+
app = data["apps"][0]
56+
57+
full_version = latest_release["tag_name"]
58+
tag = latest_release["tag_name"]
59+
# Extract version like 1.4.5 from tag, which may be like 'v1.4.5'
60+
version_match = re.search(r"(\d+\.\d+\.\d+)", full_version)
61+
if version_match:
62+
version = version_match.group(1)
63+
else:
64+
print("Error: Could not parse version from tag_name.")
65+
return
66+
version_date = latest_release["published_at"]
67+
date_obj = datetime.strptime(version_date, "%Y-%m-%dT%H:%M:%SZ")
68+
version_date = date_obj.strftime("%Y-%m-%d")
69+
70+
description = latest_release["body"]
71+
description = prepare_description(description)
72+
73+
assets = latest_release.get("assets", [])
74+
download_url = None
75+
size = None
76+
for asset in assets:
77+
# venera-ios-1.4.5+145.ipa
78+
if asset["name"] == f"venera-ios-{version}+{version.replace('.', '')}.ipa":
79+
download_url = asset["browser_download_url"]
80+
size = asset["size"]
81+
break
82+
83+
if download_url is None or size is None:
84+
print("Error: IPA file not found in release assets.")
85+
return
86+
87+
version_entry = {
88+
"version": version,
89+
"date": version_date,
90+
"localizedDescription": description,
91+
"downloadURL": download_url,
92+
"size": size
93+
}
94+
95+
duplicate_entries = [item for item in app["versions"] if item["version"] == version]
96+
if duplicate_entries:
97+
app["versions"].remove(duplicate_entries[0])
98+
99+
app["versions"].insert(0, version_entry)
100+
101+
app.update({
102+
"version": version,
103+
"versionDate": version_date,
104+
"versionDescription": description,
105+
"downloadURL": download_url,
106+
"size": size
107+
})
108+
109+
if "news" not in data:
110+
data["news"] = []
111+
112+
news_identifier = f"release-{full_version}"
113+
date_string = date_obj.strftime("%d/%m/%y")
114+
news_entry = {
115+
"appID": "com.github.wgh136.venera",
116+
"caption": f"Update of Venera just got released!",
117+
"date": latest_release["published_at"],
118+
"identifier": news_identifier,
119+
"notify": True,
120+
"tintColor": "#0784FC",
121+
"title": f"{full_version} - Venera {date_string}",
122+
"url": f"https://github.com/venera-app/venera/releases/tag/{tag}"
123+
}
124+
125+
news_entry_exists = any(item["identifier"] == news_identifier for item in data["news"])
126+
if not news_entry_exists:
127+
data["news"].append(news_entry)
128+
129+
try:
130+
with open(json_file, "w") as file:
131+
json.dump(data, file, indent=2)
132+
print("JSON file updated successfully.")
133+
except IOError as e:
134+
print(f"Error writing to JSON file: {e}")
135+
raise
136+
137+
def main():
138+
repo_url = "venera-app/venera"
139+
is_nightly = "NIGHTLY_LINK" in os.environ
140+
141+
try:
142+
fetched_data_latest = fetch_latest_release(repo_url)
143+
json_file = "alt_store.json"
144+
update_json_file_release(json_file, fetched_data_latest)
145+
except Exception as e:
146+
print(f"An error occurred: {e}")
147+
raise
148+
149+
if __name__ == "__main__":
150+
main()

0 commit comments

Comments
 (0)