Skip to content
This repository was archived by the owner on Feb 14, 2025. It is now read-only.

Commit 82fd83a

Browse files
committed
force build
1 parent f0f7697 commit 82fd83a

File tree

2 files changed

+288
-0
lines changed

2 files changed

+288
-0
lines changed

.github/workflows/nightly.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Build all packages
2+
3+
on:
4+
push:
5+
branches:
6+
- nightly
7+
# paths:
8+
# - src/**
9+
10+
# on: ["push"]
11+
12+
jobs:
13+
build:
14+
strategy:
15+
matrix:
16+
include:
17+
- { target: linux-64, os: ubuntu-latest }
18+
- { target: osx-arm64, os: macos-14 }
19+
fail-fast: false
20+
21+
runs-on: ${{ matrix.os }}
22+
timeout-minutes: 5
23+
24+
defaults:
25+
run:
26+
shell: bash
27+
28+
steps:
29+
- name: Checkout repo
30+
uses: actions/checkout@v4
31+
32+
- name: Build package for target platform
33+
env:
34+
TARGET_PLATFORM: ${{ matrix.target }}
35+
PREFIX_API_KEY: ${{ secrets.PREFIX_API_KEY }}
36+
CONDA_BLD_PATH: ${{ runner.workspace }}/.rattler
37+
38+
run: |
39+
curl -ssL https://magic.modular.com | bash
40+
source $HOME/.bash_profile
41+
42+
# Temporary method to fetch the rattler binary.
43+
RATTLER_BINARY="rattler-build-aarch64-apple-darwin"
44+
if [[ $TARGET_PLATFORM == "linux-64" ]]; then RATTLER_BINARY="rattler-build-x86_64-unknown-linux-musl"; fi
45+
curl -SL --progress-bar https://github.com/prefix-dev/rattler-build/releases/latest/download/${RATTLER_BINARY} -o rattler-build
46+
chmod +x rattler-build
47+
48+
# Build and push
49+
magic run -e nightly build -m nightly
50+
magic run -e nightly publish -c mojo-community-nightly

scripts/util.py

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
import tomllib
2+
import argparse
3+
import os
4+
import subprocess
5+
import shutil
6+
import glob
7+
from typing import Any
8+
9+
10+
TEMP_DIR = os.path.expandvars("$HOME/tmp")
11+
RECIPE_DIR = "./src"
12+
13+
14+
def build_dependency_list(dependencies: dict[str, str]) -> list[str]:
15+
"""Converts the list of dependencies from the mojoproject.toml into a list of strings for the recipe."""
16+
deps: list[str] = []
17+
for name, version in dependencies.items():
18+
start = 0
19+
operator = "=="
20+
if version[0] in {"<", ">"}:
21+
if version[1] != "=":
22+
operator = version[0]
23+
start = 1
24+
else:
25+
operator = version[:2]
26+
start = 2
27+
28+
deps.append(f"- {name} {operator} {version[start:]}")
29+
30+
return deps
31+
32+
33+
def load_project_config() -> dict[str, Any]:
34+
"""Loads the project configuration from the mojoproject.toml file."""
35+
with open("mojoproject.toml", "rb") as f:
36+
return tomllib.load(f)
37+
38+
39+
def generate_recipe(args: Any) -> None:
40+
"""Generates a recipe for the project based on the project configuration in the mojoproject.toml."""
41+
# Load the project configuration and recipe template.
42+
config: dict[str, Any] = load_project_config()
43+
recipe: str
44+
with open("src/recipe.tmpl", "r") as f:
45+
recipe = f.read()
46+
47+
# Replace the placeholders in the recipe with the project configuration.
48+
recipe = (
49+
recipe.replace("{{NAME}}", config["project"]["name"])
50+
.replace("{{DESCRIPTION}}", config["project"]["description"])
51+
.replace("{{LICENSE}}", config["project"]["license"])
52+
.replace("{{LICENSE_FILE}}", config["project"]["license-file"])
53+
.replace("{{HOMEPAGE}}", config["project"]["homepage"])
54+
.replace("{{REPOSITORY}}", config["project"]["repository"])
55+
.replace("{{VERSION}}", config["project"]["version"])
56+
)
57+
58+
# Dependencies are the only notable field that changes between environments.
59+
dependencies: dict[str, str]
60+
match args.mode:
61+
case "default":
62+
dependencies = config["dependencies"]
63+
case _:
64+
dependencies = config["feature"][args.mode]["dependencies"]
65+
66+
deps = build_dependency_list(dependencies)
67+
recipe = recipe.replace("{{DEPENDENCIES}}", "\n".join(deps))
68+
69+
# Write the final recipe.
70+
with open("src/recipe.yaml", "w+") as f:
71+
recipe = f.write(recipe)
72+
73+
74+
def publish_to_prefix(args: Any) -> None:
75+
"""Publishes the conda packages to the specified conda channel."""
76+
# If CONDA_BLD_PATH is set, then pubilsh from there. Otherwise, publish from the current directory.
77+
conda_build_path = os.environ.get("CONDA_BLD_PATH", os.getcwd())
78+
if not conda_build_path:
79+
raise ValueError("CONDA_BLD_PATH environment variable is not set. This ")
80+
81+
print(f"Publishing packages to: {args.channel}")
82+
for file in glob.glob(f'{conda_build_path}/**/*.conda'):
83+
try:
84+
subprocess.run(
85+
["magic", "run", "rattler-build", "upload", "prefix", "-c", args.channel, file],
86+
check=True,
87+
)
88+
except subprocess.CalledProcessError:
89+
pass
90+
os.remove(file)
91+
92+
93+
def remove_temp_directory() -> None:
94+
"""Removes the temporary directory used for building the package."""
95+
if os.path.exists(TEMP_DIR):
96+
print("Removing temp directory.")
97+
shutil.rmtree(TEMP_DIR)
98+
99+
100+
def prepare_temp_directory() -> None:
101+
"""Creates the temporary directory used for building the package. Adds the compiled mojo package to the directory."""
102+
package = load_project_config()["project"]["name"]
103+
remove_temp_directory()
104+
os.mkdir(TEMP_DIR)
105+
subprocess.run(
106+
["mojo", "package", f"src/{package}", "-o", f"{TEMP_DIR}/{package}.mojopkg"],
107+
check=True,
108+
)
109+
110+
111+
def execute_package_tests(args: Any) -> None:
112+
"""Executes the tests for the package."""
113+
TEST_DIR = "./test"
114+
115+
print("Building package and copying tests.")
116+
prepare_temp_directory()
117+
shutil.copytree(TEST_DIR, TEMP_DIR, dirs_exist_ok=True)
118+
119+
print("Running tests...")
120+
subprocess.run(["mojo", "test", TEMP_DIR], check=True)
121+
122+
remove_temp_directory()
123+
124+
125+
def execute_package_examples(args: Any) -> None:
126+
"""Executes the examples for the package."""
127+
EXAMPLE_DIR = "./examples"
128+
129+
print("Building package and copying examples.")
130+
prepare_temp_directory()
131+
shutil.copytree(EXAMPLE_DIR, TEMP_DIR)
132+
133+
print("Running examples...")
134+
subprocess.run(["mojo", "test", TEMP_DIR], check=True)
135+
136+
remove_temp_directory()
137+
138+
139+
def execute_package_benchmarks(args: Any) -> None:
140+
BENCHMARK_DIR = "./benchmarks"
141+
142+
print("Building package and copying benchmarks.")
143+
prepare_temp_directory()
144+
shutil.copytree(BENCHMARK_DIR, TEMP_DIR)
145+
146+
print("Running benchmarks...")
147+
subprocess.run(["mojo", "test", TEMP_DIR], check=True)
148+
149+
remove_temp_directory()
150+
151+
152+
def build_conda_package(args: Any) -> None:
153+
"""Builds the conda package for the project."""
154+
# Build the conda package for the project.
155+
config = load_project_config()
156+
channels: list[str]
157+
match args.mode:
158+
case "default":
159+
channels = config["project"]["channels"]
160+
case _:
161+
channels = config["feature"][args.mode]["channels"]
162+
163+
options: list[str] = []
164+
for channel in channels:
165+
options.extend(["-c", channel])
166+
167+
generate_recipe(args)
168+
subprocess.run(
169+
["magic", "run", "rattler-build", "build", "-r", RECIPE_DIR, "--skip-existing=all", *options],
170+
check=True,
171+
)
172+
os.remove(f"{RECIPE_DIR}/recipe.yaml")
173+
174+
175+
def main():
176+
# Configure the parser to receive the mode argument.
177+
# create the top-level parser
178+
parser = argparse.ArgumentParser(
179+
prog="util", description="Generate a recipe for the project."
180+
)
181+
subcommands = parser.add_subparsers(help="sub-command help")
182+
183+
# create the parser for the "templater" command
184+
templater = subcommands.add_parser("templater", help="template help")
185+
templater.add_argument(
186+
"-m",
187+
"--mode",
188+
type=str,
189+
default="default",
190+
help="The environment to generate the recipe for. Defaults to 'default' for the standard version.",
191+
)
192+
templater.set_defaults(func=generate_recipe)
193+
194+
# create the parser for the "build" command
195+
build = subcommands.add_parser("build", help="build help")
196+
build.add_argument(
197+
"-m",
198+
"--mode",
199+
type=str,
200+
default="default",
201+
help="The environment to build the package using. Defaults to 'default' for the standard version.",
202+
)
203+
build.set_defaults(func=build_conda_package)
204+
205+
# create the parser for the "publish" command
206+
publish = subcommands.add_parser("publish", help="publish help")
207+
publish.add_argument(
208+
"-c",
209+
"--channel",
210+
type=str,
211+
default="mojo-community",
212+
help="The prefix.dev conda channel to publish to. Defaults to 'mojo-community'.",
213+
)
214+
publish.set_defaults(func=publish_to_prefix)
215+
216+
# create the parser for the "run" command
217+
run = subcommands.add_parser("run", help="run help")
218+
run_subcommands = run.add_subparsers(help="run sub-command help")
219+
220+
# create the parser for the "run tests" command
221+
run_tests = run_subcommands.add_parser("tests", help="tests help")
222+
run_tests.set_defaults(func=execute_package_tests)
223+
224+
# create the parser for the "run benchmarks" command
225+
run_benchmarks = run_subcommands.add_parser("benchmarks", help="benchmarks help")
226+
run_benchmarks.set_defaults(func=execute_package_benchmarks)
227+
228+
# create the parser for the "run examples" command
229+
run_examples = run_subcommands.add_parser("examples", help="examples help")
230+
run_examples.set_defaults(func=execute_package_examples)
231+
232+
args = parser.parse_args()
233+
if args.func:
234+
args.func(args)
235+
236+
237+
if __name__ == "__main__":
238+
main()

0 commit comments

Comments
 (0)