Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ authors = ["octos-app contributors"]
# Reference and AppCard. A git source shares its repository's revision with
# the pins below; tools/native_apps.py writes this block and checks that.
# BEGIN native-apps: pins (generated by tools/native_apps.py from native-apps.json; do not edit)
rinx = { git = "https://github.com/hagency-org/Rinx.git", rev = "68afcf796d303aaf646eeb832d65c450a56c92b5", default-features = false, features = ["octosense-module"] }
rinx = { git = "https://github.com/hagency-org/Rinx.git", tag = "v1.0.0", default-features = false, features = ["octosense-module"] }
octosense-reference = { path = "apps/reference" }
makepad-sheets = { git = "https://github.com/OctoSense-org/makepad.git", rev = "a5a3cf5b0dbd1b56c25defe6d98479f25843e770", default-features = false }
makepad-terminal = { git = "https://github.com/OctoSense-org/makepad.git", rev = "a5a3cf5b0dbd1b56c25defe6d98479f25843e770" }
Expand Down
2 changes: 1 addition & 1 deletion native-apps.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"id": "rinx",
"crate": "rinx",
"source": { "git": "https://github.com/hagency-org/Rinx.git", "rev": "68afcf796d303aaf646eeb832d65c450a56c92b5", "local": null },
"source": { "git": "https://github.com/hagency-org/Rinx.git", "tag": "v1.0.0", "rev": "0879548ba826c786eb6ad3f60dfba7bd342bd750", "local": null },
"module": "rinx::module::RINX_MODULE",
"bin": null,
"default_features": false,
Expand Down
55 changes: 47 additions & 8 deletions tools/native_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
It refuses `process` hosting on mobile or wasm targets, plain `process` on
Linux (non-Vulkan Linux runs every app in-process: `process-if-vulkan`),
`process` without a `bin`, and two revisions of one git repository in the
root pins. After a write, Cargo.lock is brought in step by cargo itself
root pins. Apps released by tag (Rinx, hagency-org/Rinx#37) must be pinned by
a release tag (`vX.Y.Z` or `vX.Y.Z-rc.N`) together with the commit it names:
the root pin says `tag = …`, and --check also reads Cargo.lock and refuses a
lock whose commit for that tag is not the manifest's `rev` (a moved tag). After a write, Cargo.lock is brought in step by cargo itself
(`cargo update -p` for a pin that changed, then `cargo metadata`); --no-lock
skips that.

Expand All @@ -45,6 +48,11 @@
ROOT = Path(__file__).resolve().parents[1]
MANIFEST = "native-apps.json"
RUST_FILE = "crates/shell/src/native_apps.rs"
# Apps OctoSense takes only as tagged releases (hagency-org/Rinx#37): never a
# raw commit. main may take a release candidate; OctoSense's own releases take
# only a final release (checked by their release workflows, not here).
TAGGED_RELEASES = {"rinx"}
RELEASE_TAG = r"v\d+\.\d+\.\d+(-rc\.\d+)?"
AGENTS_FILE = "crates/ai-host/src/native_agents.rs"
PACKAGES = {"desktop": "desktop/Cargo.toml", "phone": "phone/Cargo.toml"}

Expand Down Expand Up @@ -122,10 +130,14 @@ def validate(data):
elif "path" in source:
if set(source) != {"path"}:
problems.append(f"{where}: a path source has only 'path'")
elif set(source) != {"git", "rev", "local"}:
problems.append(f"{where}: source is {{git, rev, local}} or {{path}}")
elif set(source) not in ({"git", "rev", "local"}, {"git", "tag", "rev", "local"}):
problems.append(f"{where}: source is {{git, rev, local}}, {{git, tag, rev, local}} or {{path}}")
elif not re.fullmatch(r"[0-9a-f]{40}", str(source["rev"])):
problems.append(f"{where}: source.rev must be a full commit id")
problems.append(f"{where}: source.rev must be a full commit id (for a tag: the commit it names)")
elif "tag" in source and not re.fullmatch(RELEASE_TAG, str(source["tag"])):
problems.append(f"{where}: source.tag must be a release tag vX.Y.Z or vX.Y.Z-rc.N")
if isinstance(source, dict) and ident in TAGGED_RELEASES and "tag" not in source:
problems.append(f"{where}: is taken only as a tagged release (hagency-org/Rinx#37): pin source.tag, not a raw rev")
if not modules_of(app) or not all(re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*(::[A-Za-z_][A-Za-z0-9_]*)+", m) for m in modules_of(app)):
problems.append(f"{where}: module must be a Rust path (or a list of them)")
if app["bin"] is not None and not (isinstance(app["bin"], str) and app["bin"]):
Expand Down Expand Up @@ -402,7 +414,8 @@ def pin_line(app):
if "path" in source:
fields = [f"path = {s(source['path'])}"]
else:
fields = [f"git = {s(source['git'])}", f"rev = {s(source['rev'])}"]
pin = f"tag = {s(source['tag'])}" if "tag" in source else f"rev = {s(source['rev'])}"
fields = [f"git = {s(source['git'])}", pin]
if not app["default_features"]:
fields.append("default-features = false")
if app["crate_features"]:
Expand Down Expand Up @@ -763,7 +776,7 @@ def check_one_revision(apps, text):
problems = []
for app in apps:
source = app["source"]
if "git" not in source:
if "git" not in source or "tag" in source:
continue
for crate, git, rev in workspace_pins(text):
if same_git(git, source["git"]) and rev != source["rev"]:
Expand Down Expand Up @@ -803,6 +816,29 @@ def read(rel):
return out


def check_tagged_locks(root, apps):
"""A tag-pinned app's Cargo.lock entry names the manifest's commit.

Cargo records `git+<url>?tag=<tag>#<commit>`; a tag moved after review
shows up here as a different commit."""
lock = Path(root) / "Cargo.lock"
if not lock.is_file():
return []
text, problems = lock.read_text(), []
for app in apps:
source = app["source"]
if "tag" not in source:
continue
pattern = re.compile(r'name = "%s"\nversion = "[^"]*"\nsource = "git\+([^"?#]+)\?tag=([^"#]+)#([0-9a-f]{40})"' % re.escape(app["crate"]))
found = [m for m in pattern.finditer(text) if same_git(m.group(1), source["git"])]
if not found:
problems.append(f"Cargo.lock has no {app['crate']} from {source['git']} at tag {source['tag']}; run python3 tools/native_apps.py")
for m in found:
if m.group(2) != source["tag"] or m.group(3) != source["rev"]:
problems.append(f"Cargo.lock: {app['crate']} {m.group(2)} is {m.group(3)[:12]}, native-apps.json says {source['tag']} is {source['rev'][:12]} (a moved tag?)")
return problems


def changed_pins(old, new):
"""Crates whose pin in [workspace.dependencies] changed (not added)."""
def pins(text):
Expand Down Expand Up @@ -842,9 +878,12 @@ def main(argv=None, root=ROOT):
if args.check:
for rel in drifted:
print(f"::error::{rel} is not what native-apps.json generates; run python3 tools/native_apps.py")
if not drifted:
lock_problems = check_tagged_locks(root, load(root))
for problem in lock_problems:
print(f"::error::{problem}")
if not drifted and not lock_problems:
note("every generated place matches native-apps.json")
return 1 if drifted else 0
return 1 if drifted or lock_problems else 0
for rel in drifted:
(Path(root) / rel).write_text(results[rel][1])
note(f"wrote {rel}")
Expand Down
30 changes: 28 additions & 2 deletions tools/test_native_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,32 @@ def assertRefused(self, pattern):


class Validation(Fixture):
def test_rinx_is_taken_only_as_a_tagged_release(self):
source = self.app("rinx")["source"]
del source["tag"]
self.assertRefused(r"rinx.*tagged release")

def test_refuses_a_tag_that_is_not_a_release(self):
self.app("rinx")["source"]["tag"] = "main"
self.assertRefused(r"release tag vX\.Y\.Z")
self.app("rinx")["source"]["tag"] = "v1.1.0-rc.2"
native_apps.validate(self.data)

def test_a_tag_pin_names_the_tag_and_the_lock_its_commit(self):
rinx = self.app("rinx")
self.assertIn('tag = "%s"' % rinx["source"]["tag"], native_apps.pin_line(rinx))
self.assertNotIn("rev =", native_apps.pin_line(rinx))
shutil.copy(ROOT / "Cargo.lock", self.root / "Cargo.lock")
self.assertEqual(native_apps.check_tagged_locks(self.root, native_apps.load(self.root)), [])

def test_a_moved_tag_fails_the_check(self):
shutil.copy(ROOT / "Cargo.lock", self.root / "Cargo.lock")
self.app("rinx")["source"]["rev"] = "f" * 40
self.save()
problems = native_apps.check_tagged_locks(self.root, native_apps.load(self.root))
self.assertTrue(problems and "moved tag" in problems[0], problems)
self.assertEqual(self.run_main("--check"), 1)

def test_refuses_process_on_mobile_and_wasm(self):
for target in ("android", "ios", "ohos", "wasm"):
self.app("terminal")["hosting"][target] = "process"
Expand Down Expand Up @@ -274,13 +300,13 @@ def test_missing_markers_are_an_error(self):
native_apps.generate(self.root)

def test_a_changed_pin_is_updated_through_cargo(self):
self.app("rinx")["source"]["rev"] = "1" * 40
self.app("rinx")["source"].update(tag="v1.0.1", rev="1" * 40)
self.save()
with patch.object(native_apps.subprocess, "run") as run:
self.assertEqual(self.run_main(), 0)
commands = [call.args[0] for call in run.call_args_list]
self.assertEqual(commands, [["cargo", "update", "-p", "rinx"], ["cargo", "metadata", "--format-version", "1"]])
self.assertIn('rinx = { git = "https://github.com/hagency-org/Rinx.git", rev = "' + "1" * 40 + '"',
self.assertIn('rinx = { git = "https://github.com/hagency-org/Rinx.git", tag = "v1.0.1"',
(self.root / "Cargo.toml").read_text())

def test_nothing_changed_runs_no_cargo(self):
Expand Down