Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
73168ac
Add versioned release bundle manifest
Staphylococcus Aug 30, 2026
a67cba9
Merge pull request #104 from Staphylococcus/issue-95-release-bundle-m…
Staphylococcus Aug 30, 2026
f292127
feat: add host upgrade compatibility preflight
Staphylococcus Aug 30, 2026
3a9d001
fix: harden upgrade preflight safety checks
Staphylococcus Aug 30, 2026
01a732b
fix: enforce upgrade preflight safety invariants
Staphylococcus Aug 30, 2026
514d683
fix: secure candidate bundle containment
Staphylococcus Aug 30, 2026
2ac38ab
Merge pull request #105 from Staphylococcus/issue-100-host-upgrade-pr…
Staphylococcus Aug 30, 2026
5dfbb08
feat: add release bundle upgrade mode
Staphylococcus Aug 30, 2026
45acea9
Merge pull request #106 from Staphylococcus/issue-96-installer-upgrad…
Staphylococcus Aug 30, 2026
24757e1
feat: securely acquire release bundles
Staphylococcus Aug 31, 2026
23bb208
test: replay release bundle responses offline
Staphylococcus Aug 31, 2026
ba3c447
test: satisfy current clippy byte slice lint
Staphylococcus Aug 31, 2026
af61408
Merge pull request #107 from Staphylococcus/issue-97-secure-release-b…
Staphylococcus Aug 31, 2026
4f28dc7
feat: add assisted update installation
Staphylococcus Sep 1, 2026
7a3ce64
Merge pull request #108 from Staphylococcus/issue-98-updates-install
Staphylococcus Sep 1, 2026
b15c49f
chore(release): prepare 1.4.0-beta.2
Staphylococcus Sep 1, 2026
2215839
Merge pull request #109 from Staphylococcus/release/v1.4.0-beta.2-prep
Staphylococcus Sep 1, 2026
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
22 changes: 19 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ jobs:
- name: Validate release promotion contract
run: python3 scripts/test_release_promotion.py

- name: Validate release bundle manifest contract
run: python3 scripts/test_release_bundle_manifest.py

bundle-smoke-test:
runs-on: ubuntu-latest
needs: verify
Expand All @@ -65,13 +68,26 @@ jobs:
run: sudo apt-get update && sudo apt-get install -y musl-tools zenity

- name: Build lg-buddy release binary
env:
LG_BUDDY_BUILD_COMMIT: ${{ github.sha }}
LG_BUDDY_RELEASE_VERSION: 0.0.0-ci.smoke
run: cargo build --release -p lg-buddy --target x86_64-unknown-linux-musl

- name: Create release bundle
run: ./scripts/build-release-bundle.sh --target x86_64-unknown-linux-musl --version ci-smoke --output-dir dist
run: |
umask 0002
./scripts/build-release-bundle.sh --target x86_64-unknown-linux-musl --version 0.0.0-ci.smoke --output-dir dist

- name: Smoke test release bundle
run: ./scripts/test-release-bundle.sh --skip-pip-install --archive dist/lg-buddy-ci-smoke-x86_64-unknown-linux-musl.tar.gz
run: |
./scripts/test-release-bundle.sh \
--skip-pip-install \
--archive dist/lg-buddy-0.0.0-ci.smoke-x86_64-unknown-linux-musl.tar.gz \
--expected-tag v0.0.0-ci.smoke \
--expected-version 0.0.0-ci.smoke \
--expected-channel prerelease \
--expected-target x86_64-unknown-linux-musl \
--expected-commit "${{ github.sha }}"

- name: Generate checksums
run: |
Expand All @@ -86,4 +102,4 @@ jobs:
- name: Dry-run publish release assets
env:
GH_RELEASE_DRY_RUN: "1"
run: ./scripts/publish-release-assets.sh --dist-dir dist --tag v0.0.0-ci-smoke
run: ./scripts/publish-release-assets.sh --dist-dir dist --tag v0.0.0-ci.smoke --commit "${{ github.sha }}"
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,10 @@ jobs:
./scripts/test-release-bundle.sh \
--skip-pip-install \
--archive "dist/lg-buddy-${{ needs.validate.outputs.version }}-x86_64-unknown-linux-musl.tar.gz" \
--expected-tag "${{ needs.validate.outputs.tag }}" \
--expected-version "${{ needs.validate.outputs.version }}" \
--expected-channel "${{ needs.validate.outputs.channel }}" \
--expected-target x86_64-unknown-linux-musl \
--expected-commit "${{ needs.validate.outputs.head_sha }}"

- name: Generate checksums
Expand Down
101 changes: 95 additions & 6 deletions Cargo.lock

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

16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ selection and troubleshooting. Protocol and event details are documented in the
## Before You Install

The native `lg_webos` control path does not require Python. Native-only packages
can omit the Python client, `venv`, and `pip`. The current `install.sh` flow
can omit the Python client, `venv`, and `pip`. The current fresh-install flow
still provisions `bscpylgtv` as a compatibility fallback and installs the
brightness dialog, so release-bundle installation checks for Python 3 with
`venv` and `pip`, plus `zenity`. `swayidle` is required only when using that
desktop backend.
brightness dialog, so release-bundle installation checks for Python 3 with a
`venv` that provisions `pip`, plus `zenity`. `swayidle` is required only when
using that desktop backend.

### Debian, Ubuntu, and Pop!_OS

Expand Down Expand Up @@ -95,6 +95,13 @@ during setup. With the default `bscpylgtv` platform, the prompt may instead
appear on first use; see the
[bscpylgtv first-use guide](https://github.com/chros73/bscpylgtv/blob/master/docs/guides/first_use.md).

To check, verify, and install the next release from your saved update channel,
run `lg-buddy updates install` as your regular user. It checks host
compatibility, shows the exact target identity, asks for explicit confirmation,
and then runs the verified bundle's upgrade installer. Upgrade mode preserves
configuration and credentials and does not repeat setup or pairing;
incompatible and legacy layouts are refused rather than migrated.

The shell installer targets conventional Linux installations with mutable
system locations. First-class NixOS packaging is tracked in
[issue #24](https://github.com/Staphylococcus/LG_Buddy/issues/24).
Expand All @@ -118,6 +125,7 @@ lg-buddy volume mute
lg-buddy settings list
lg-buddy settings describe screen.backend
lg-buddy updates check
lg-buddy updates install
lg-buddy --version
```

Expand Down
6 changes: 5 additions & 1 deletion crates/lg-buddy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
[package]
name = "lg-buddy"
version = "1.3.0"
version = "1.4.0-beta.2"
edition = "2021"
publish = false

[dependencies]
dbus = { version = "0.9.11", features = ["vendored"] }
dbus-crossroads = "0.5.3"
evdev = "0.13.2"
flate2 = "1"
libc = "0.2"
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
semver = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.10"
tar = { version = "0.4", default-features = false }
tungstenite = { version = "0.30", features = ["rustls-tls-webpki-roots"] }
ureq = "2"
url = "2"
wayland-client = { version = "0.31.15", default-features = false }
wayland-protocols = { version = "0.32.13", default-features = false, features = ["client", "staging"] }

Expand Down
57 changes: 57 additions & 0 deletions crates/lg-buddy/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use std::env;
use std::fs;
use std::path::PathBuf;

const PREFIX: &str = "LG_BUDDY_RELEASE_IDENTITY_V1\0";
const SUFFIX: &str = "\0LG_BUDDY_RELEASE_IDENTITY_END\0";

fn main() {
println!("cargo:rerun-if-env-changed=LG_BUDDY_RELEASE_VERSION");
println!("cargo:rerun-if-env-changed=LG_BUDDY_BUILD_COMMIT");

let package_version = env::var("CARGO_PKG_VERSION").expect("Cargo package version");
let release_version = env::var("LG_BUDDY_RELEASE_VERSION")
.ok()
.filter(|value| !value.trim().is_empty());
let version = release_version.as_deref().unwrap_or(&package_version);
let channel = match release_version.as_deref() {
None => "dev",
Some(value) if value.contains('-') => "prerelease",
Some(_) => "stable",
};
let commit = env::var("LG_BUDDY_BUILD_COMMIT")
.ok()
.filter(|value| !value.trim().is_empty())
.unwrap_or_else(|| "unknown".to_string());
let target = env::var("TARGET").expect("Cargo target triple");
for (name, value) in [
("version", version),
("channel", channel),
("target", target.as_str()),
("commit", commit.as_str()),
] {
assert!(
value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'.' | b'_' | b'-')),
"{name} contains unsupported release-identity characters"
);
}

let manifest = format!(
"{{\"schema_version\":1,\"critical\":[\"release_tag\",\"version\",\"channel\",\"target\",\"commit\"],\"release_tag\":\"v{version}\",\"version\":\"{version}\",\"channel\":\"{channel}\",\"target\":\"{target}\",\"commit\":\"{commit}\"}}"
);
let record = format!("{PREFIX}{manifest}{SUFFIX}").into_bytes();
let bytes = record
.iter()
.map(u8::to_string)
.collect::<Vec<_>>()
.join(",");
let generated = format!(
"#[used]\n#[link_section = \".lg_buddy.identity\"]\nstatic LG_BUDDY_EMBEDDED_RELEASE_IDENTITY: [u8; {}] = [{bytes}];\n",
record.len()
);
let output =
PathBuf::from(env::var_os("OUT_DIR").expect("Cargo OUT_DIR")).join("release_identity.rs");
fs::write(output, generated).expect("write embedded release identity");
}
3 changes: 2 additions & 1 deletion crates/lg-buddy/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ impl RuntimeEventKind {
| Command::DetectBackend
| Command::Dev(_)
| Command::Settings(_)
| Command::Updates(_) => None,
| Command::Updates(_)
| Command::UpgradePreflight { .. } => None,
}
}
}
Expand Down
Loading
Loading