Skip to content

Commit 3e15969

Browse files
committed
initial commit
0 parents  commit 3e15969

8 files changed

+207
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.zip

img/image-1.png

18.8 KB
Loading

img/image-2.png

37.9 KB
Loading

img/image-3.png

13.7 KB
Loading

img/image.png

34.8 KB
Loading

readme_header.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
### BlenderBIM daily builds with in-blender autoupdate
2+
3+
Only supported for BlenderBIM with Python 3.11 (default Python version in Blender 4.2+) as Blender doesn't support separate builds for different Python versions.
4+
5+
1. Setup BlenderBIM Daily Builds Repository.
6+
7+
Drag'n'drop url for your platform from the table below to Blender, accept adding a new repository.
8+
9+
![](img/image-1.png)
10+
11+
During repository creation check "Check Updates On Startup" to get updates for daily BlenderBIM builds automatically.
12+
13+
![](img/image-2.png)
14+
15+
Alternatively, repository can be created manually, without drag'n'drop:
16+
17+
- Open Blender Preferences -> "+" -> "Add Remote Remository". You'll see a window similar to the one above.
18+
19+
- Use as URL: `https://raw.githubusercontent.com/IfcOpenShell/blenderbim_unstable_repo/main/index.json` and check auto-updates if you want them.
20+
21+
22+
23+
2. Install extension.
24+
25+
To install the BlenderBIM extension you can either drag'n'drop the same url again and install it:
26+
27+
![alt text](img/image-3.png)
28+
29+
Or you can search for "blenderbim" in Extensions and Install it.
30+
31+
![](img/image.png)
32+
33+
34+
### Available daily builds
35+

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lxml
2+
pygithub
3+
markdownify

setup_extensions_repo.py

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import os
2+
import sys
3+
import json
4+
import requests
5+
import shutil
6+
import markdownify
7+
from pathlib import Path
8+
from urllib.parse import urljoin
9+
from lxml import etree, html
10+
from github import Github
11+
from typing import Union
12+
13+
# Useful:
14+
# - https://docs.blender.org/manual/en/latest/advanced/extensions/creating_repository/static_repository.html
15+
# - https://developer.blender.org/docs/features/extensions/api_listing/
16+
17+
18+
PACKAGES_FOLDER = Path(".")
19+
INDEX_PATH = PACKAGES_FOLDER / "index.json"
20+
HTML_PATH = PACKAGES_FOLDER / "index.html"
21+
MD_PATH = PACKAGES_FOLDER / "readme.md"
22+
MD_HEADER_PATH = PACKAGES_FOLDER / "readme_header.md"
23+
INDEX_URL = "https://raw.githubusercontent.com/IfcOpenShell/blenderbim_unstable_repo/main/index.json"
24+
BASE_URL = "https://github.com/IfcOpenShell/IfcOpenShell/releases/download/blenderbim-{version}/"
25+
BLENDER_PLATFORMS = ["windows-x64", "macos-x64", "macos-arm64", "linux-x64"]
26+
# Blender doesn't support separate builds for different Python versions :(
27+
PYTHON_VERSION = "py311"
28+
29+
30+
def check_url(url) -> bool:
31+
try:
32+
response = requests.head(url, allow_redirects=True) # Use HEAD request to check URL status
33+
if response.status_code == 200:
34+
print(f"URL is reachable: {url}")
35+
return True
36+
else:
37+
print(f"URL returned status code {response.status_code}: {url}")
38+
except requests.RequestException as e:
39+
print(f"URL check failed with exception: {e}: {url}")
40+
41+
return False
42+
43+
44+
def get_platform(filename: str) -> Union[str, None]:
45+
for platorm in BLENDER_PLATFORMS:
46+
if platorm in filename:
47+
return platorm
48+
49+
50+
class ExtensionsRepo:
51+
def __init__(self, github_tag: str):
52+
self.fetch_urls(github_tag)
53+
self.run_blender()
54+
self.patch_repo_files()
55+
56+
def fetch_urls(self, github_tag: str) -> None:
57+
g = Github()
58+
repo = g.get_repo("IfcOpenShell/IfcOpenShell")
59+
60+
if github_tag == "--last-tag":
61+
for i, release in enumerate(repo.get_releases()):
62+
if i >= 10:
63+
raise Exception("Couldn't find a release with a valid tag in the last 10 releases.")
64+
if release.tag_name.startswith("blenderbim-"):
65+
github_tag = release.tag_name
66+
break
67+
68+
release = repo.get_release(github_tag)
69+
platforms_urls = {}
70+
for asset in release.get_assets():
71+
name = asset.name
72+
if PYTHON_VERSION not in name:
73+
continue
74+
if not (platform := get_platform(name)):
75+
continue
76+
platforms_urls[platform] = asset.browser_download_url
77+
78+
if len(platforms_urls) != len(BLENDER_PLATFORMS):
79+
missing_platforms = set(BLENDER_PLATFORMS) - set(platforms_urls)
80+
raise Exception(
81+
f"Couldn't find in the release '{github_tag}' .zip files for some platforms: '{missing_platforms}'."
82+
)
83+
84+
for url in platforms_urls.values():
85+
print(f"Downloading {url}...")
86+
with requests.get(url, stream=True) as r, open(PACKAGES_FOLDER / url.rsplit("/", 1)[-1], "wb") as f:
87+
shutil.copyfileobj(r.raw, f)
88+
print("Finished downloading .zip packages.")
89+
90+
def run_blender(self) -> None:
91+
return_code = os.system(f"blender --command extension server-generate --repo-dir={PACKAGES_FOLDER} --html")
92+
if return_code != 0:
93+
raise Exception(f"Blender return code was '{return_code}'.")
94+
95+
print("Finished blender 'extension server-generate'.")
96+
97+
def patch_repo_files(self) -> None:
98+
self.replaced_urls = {}
99+
self.patch_index_json()
100+
self.patch_index_html()
101+
self.convert_html_to_md()
102+
103+
def patch_index_json(self) -> None:
104+
with open(INDEX_PATH, "rb") as fi:
105+
index = json.load(fi)
106+
107+
replaced_urls = {}
108+
109+
for package in index["data"]:
110+
version = package["version"]
111+
archive_url = package["archive_url"]
112+
url = urljoin(BASE_URL.format(version=version), archive_url)
113+
package["archive_url"] = url
114+
replaced_urls[archive_url] = url
115+
116+
self.replaced_urls = replaced_urls
117+
118+
with open(INDEX_PATH, "w") as fo:
119+
json.dump(index, fo, indent=2)
120+
fo.write("\n")
121+
122+
print("Finished updating index.json")
123+
124+
def patch_index_html(self) -> None:
125+
with open(HTML_PATH, "r", encoding="utf-8") as f:
126+
tree = html.parse(f)
127+
128+
for a in tree.xpath("//a[starts-with(@href, './blenderbim_')]"):
129+
href = a.get("href")
130+
url, url_arguments = href.split("?")
131+
# Replace relative urls with absolute urls to the releases.
132+
# Replace relative index.json url to the repo url.
133+
url_arguments = url_arguments.replace(".%2Findex.json", INDEX_URL)
134+
new_href = f"{self.replaced_urls[url]}?{url_arguments}"
135+
a.set("href", new_href)
136+
137+
with open(HTML_PATH, "w", encoding="utf-8") as f:
138+
html_string = etree.tostring(tree, method="html", pretty_print=True, encoding="utf-8").decode("utf-8")
139+
f.write(html_string)
140+
141+
print("Finished updating index.html")
142+
143+
def convert_html_to_md(self) -> None:
144+
with open(HTML_PATH, "r", encoding="utf-8") as file:
145+
html_content = file.read()
146+
147+
parser = etree.HTMLParser()
148+
tree = etree.fromstring(html_content, parser)
149+
150+
# Convert HTML to Markdown
151+
markdown_content = markdownify.markdownify(
152+
etree.tostring(tree, pretty_print=True, method="html").decode("utf-8")
153+
)
154+
155+
with open(MD_HEADER_PATH, "r", encoding="utf-8") as fo:
156+
with open(MD_PATH, "w", encoding="utf-8") as file:
157+
file.write(fo.read())
158+
file.write(markdown_content)
159+
160+
os.unlink(HTML_PATH)
161+
print("Finished updating readme.md")
162+
163+
164+
if __name__ == "__main__":
165+
if len(sys.argv) < 2:
166+
script_name = Path(__file__).name
167+
raise Exception(f"Usage: 'py {script_name} <github_releases_tag>' | 'py {script_name} --last-tag'")
168+
ExtensionsRepo(sys.argv[1])

0 commit comments

Comments
 (0)