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
2 changes: 2 additions & 0 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ jobs:
- name: Desktop tools and scripts
run: |
python3 -m unittest discover -s tools -p 'test_*.py'
# The release packaging script (no build; release-desktop.yml builds).
python3 -m unittest discover -s desktop/scripts -p 'test_package.py'
- name: One Makepad, App Hub, octos and Rinx in the locked graph
run: python3 tools/setup.py --check --cargo
- name: The native apps' generated places match native-apps.json
Expand Down
462 changes: 462 additions & 0 deletions .github/workflows/release-desktop.yml

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ __pycache__/

# Framework checkouts prepared by tools/setup.py.
/.sources

# desktop/scripts/package.py writes this beside release.json while it packages.
/desktop/packaging/.release.generated.json
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,12 +392,13 @@ Path-filtered workflows in `.github/workflows/`, so a change runs only the jobs
| `phone.yml` | `phone/`, `crates/`, `apps/`, the workspace files, `tools/` | compiles Home and its bundled modules, the shell graph guards, and runs the tests of the shell, Home, the AI services, App Hub admission and runtime policy on macOS; the longest job |
| `apps.yml` | `apps/`, `crates/`, the workspace files, `tools/setup.py` | the kernel service, app peers, AI providers config, the Mail and `llm` host services, the shell's AI services (`crates/ai-host`), AppCard |
| `rom.yml` | `rom/`, `phone/android/`, the phone's Android resources and tests, `tools/kernel-artifact.py` | product tests, the generated Agent Binder client, the web installer |
| `release-desktop.yml` | a pushed `desktop-v*` tag, a manual run, or a pull request that changes the packaging (build and scan only) | unsigned desktop packages for macOS, Windows and Linux, the private-path scan; for a tag, signing in the `release` environment and a draft release ([desktop/README.md](desktop/README.md#release-builds)). Not run by `tools/ci-local.sh`. |

Each workflow's graph check (`tools/setup.py --check --cargo`) asserts one Makepad, one App Hub, one octos and one Rinx in the locked graph.

## Releases

ADR 0001 tags each product on its own: `desktop-v*`, `home-v*` (APK), `rom-v*` (image), with build receipts that record the repository commit. System apps ship only inside the shells, admitted by digest; they are not released separately. The ROM release published before the merge, `20260919-j`, is here as [`rom-v20260919-j`](https://github.com/OctoSense-org/OctoSense/releases/tag/rom-v20260919-j). Phones read `update.json` from the moving `rom-latest` release, not from `releases/latest` ([rom/docs/updates.md](rom/docs/updates.md)). Images `20260919-j` and earlier check the retired OctoSense-ROM repository instead, so a phone flashed with one must be reflashed once to receive updates over the air.
ADR 0001 tags each product on its own: `desktop-v*`, `home-v*` (APK), `rom-v*` (image), with build receipts that record the repository commit. A `desktop-v*` tag builds the desktop packages (`.dmg`, Windows installer, `.deb`, `.AppImage`) into a draft release ([Release builds](desktop/README.md#release-builds)). System apps ship only inside the shells, admitted by digest; they are not released separately. The ROM release published before the merge, `20260919-j`, is here as [`rom-v20260919-j`](https://github.com/OctoSense-org/OctoSense/releases/tag/rom-v20260919-j). Phones read `update.json` from the moving `rom-latest` release, not from `releases/latest` ([rom/docs/updates.md](rom/docs/updates.md)). Images `20260919-j` and earlier check the retired OctoSense-ROM repository instead, so a phone flashed with one must be reflashed once to receive updates over the air.

## Contributing

Expand Down
3 changes: 2 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,12 +392,13 @@ python3 ../rom/scripts/build-home.py --help # the Home and B
| `phone.yml` | `phone/`、`crates/`、`apps/`、工作区文件、`tools/` | 在 macOS 上编译 Home 及其内置模块,Shell 依赖图检查,并运行 Shell、Home、AI 服务、App Hub 准入和运行时策略的测试;耗时最长的任务 |
| `apps.yml` | `apps/`、`crates/`、工作区文件、`tools/setup.py` | 内核服务、app peers、AI 服务商配置、邮件与 `llm` 宿主服务、Shell 的 AI 服务(`crates/ai-host`)、AppCard |
| `rom.yml` | `rom/`、`phone/android/`、手机端的 Android 资源与测试、`tools/kernel-artifact.py` | 产品测试、生成的 Agent Binder 客户端、网页安装器 |
| `release-desktop.yml` | 推送的 `desktop-v*` 标签、手动运行,或修改打包的 pull request(只构建和扫描) | macOS、Windows、Linux 的未签名桌面包和私有路径扫描;对标签还会在 `release` 环境中签名并生成草稿 release([desktop/README.zh-CN.md](desktop/README.zh-CN.md#发布构建))。`tools/ci-local.sh` 不运行它。 |

每个工作流的依赖图检查(`tools/setup.py --check --cargo`)确保锁定的依赖图中只有一个 Makepad、一个 App Hub、一个 octos 和一个 Rinx。

## 发布

按 ADR 0001,每个产品单独打标签:`desktop-v*`、`home-v*`(APK)、`rom-v*`(镜像),构建回执记录仓库提交。系统应用只随 Shell 一起发布、按摘要准入,不单独发布。仓库合并前发布的 ROM 版本 `20260919-j` 现为本仓库的 [`rom-v20260919-j`](https://github.com/OctoSense-org/OctoSense/releases/tag/rom-v20260919-j)。手机从固定移动的 `rom-latest` release 读取 `update.json`,而不是 `releases/latest`([rom/docs/updates.md(英文)](rom/docs/updates.md))。`20260919-j` 及更早的镜像检查的是已停用的 OctoSense-ROM 仓库,因此刷了这些镜像的手机需要重新刷写一次,才能收到 OTA 更新。
按 ADR 0001,每个产品单独打标签:`desktop-v*`、`home-v*`(APK)、`rom-v*`(镜像),构建回执记录仓库提交。`desktop-v*` 标签会把桌面包(`.dmg`、Windows 安装包、`.deb`、`.AppImage`)构建到草稿 release 中([发布构建](desktop/README.zh-CN.md#发布构建))。系统应用只随 Shell 一起发布、按摘要准入,不单独发布。仓库合并前发布的 ROM 版本 `20260919-j` 现为本仓库的 [`rom-v20260919-j`](https://github.com/OctoSense-org/OctoSense/releases/tag/rom-v20260919-j)。手机从固定移动的 `rom-latest` release 读取 `update.json`,而不是 `releases/latest`([rom/docs/updates.md(英文)](rom/docs/updates.md))。`20260919-j` 及更早的镜像检查的是已停用的 OctoSense-ROM 仓库,因此刷了这些镜像的手机需要重新刷写一次,才能收到 OTA 更新。

## 参与贡献

Expand Down
40 changes: 37 additions & 3 deletions crates/shell/src/octosense/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,43 @@ pub fn scope_linked_app_data() {
}
}

/// Whether this is a packaged (installed) build: `desktop/scripts/package.py`
/// builds with `MAKEPAD_PACKAGE_DIR` set, and Makepad then reads every
/// `crate_resource` from the package instead of the source tree.
pub const fn packaged() -> bool {
option_env!("MAKEPAD_PACKAGE_DIR").is_some()
}

static PACKAGE_DIR: std::sync::OnceLock<&'static str> = std::sync::OnceLock::new();

/// The running package's source directory (desktop/ or phone/), set by its
/// `octosense_main!` before the app starts. The shell's own
/// `CARGO_MANIFEST_DIR` is crates/shell, which carries no catalog.
/// `octosense_main!` before the app starts (not in a packaged build). The
/// shell's own `CARGO_MANIFEST_DIR` is crates/shell, which carries no catalog.
pub fn set_package_dir(dir: &'static str) {
if packaged() {
return;
}
let _ = PACKAGE_DIR.set(dir);
}

/// A source tree belongs to OctoSense only when it has our provenance marker.
/// The OctoSense checkout this shell runs from: its developer catalog
/// (`config/apps.json`), whose programs it builds and runs, and the Makepad
/// sources. A source tree belongs to OctoSense only when it has our
/// provenance marker.
///
/// A packaged build has none, wherever it runs: not where it was built (that
/// path belongs to the build machine), not the working directory and not its
/// executable's ancestors, so starting the installed app inside someone
/// else's checkout can never make it build and run that checkout's code.
/// `--apps <catalog>` still names a catalog explicitly.
pub fn project_root() -> Option<PathBuf> {
project_root_for(packaged())
}

fn project_root_for(packaged: bool) -> Option<PathBuf> {
if packaged {
return None;
}
let starts = [
PACKAGE_DIR.get().map(PathBuf::from),
std::env::current_exe()
Expand All @@ -123,6 +149,14 @@ pub fn project_root() -> Option<PathBuf> {
mod tests {
use super::*;

#[test]
fn a_packaged_build_has_no_checkout_even_inside_one() {
// The tests run inside the checkout (cwd and CARGO_MANIFEST_DIR).
assert!(project_root_for(false).is_some(), "a source build finds its checkout");
assert_eq!(project_root_for(true), None, "a packaged build never does");
assert!(!packaged(), "tests are a source build");
}

#[test]
fn a_linked_rinx_keeps_its_data_under_a_chosen_home_only() {
let home = Path::new("/tmp/os-home");
Expand Down
11 changes: 7 additions & 4 deletions crates/shell/src/octosense/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ pub fn load_sheet(style: DesktopStyle, dark: bool) -> StyleSheet {
return sheet;
}
let read = |name: &str, bundled: &str| {
// Source checkouts reload on selection; installed/mobile builds use embedded data.
// Source checkouts reload on selection; packaged and mobile builds
// use the embedded data (never a path on the build machine).
#[cfg(not(target_arch = "wasm32"))]
if let Ok(text) = std::fs::read_to_string(
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("resources/themes/octosense").join(name)
) { return text; }
if !super::paths::packaged() {
if let Ok(text) = std::fs::read_to_string(
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("resources/themes/octosense").join(name)
) { return text; }
}
let _ = name;
bundled.to_string()
};
Expand Down
Loading
Loading