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
16 changes: 0 additions & 16 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,9 @@ jobs:
- uses: actions/checkout@v7.0.0
with:
persist-credentials: false
- uses: frenck/action-addon-linter@v2.21
with:
path: ./ftw-beta
- uses: frenck/action-addon-linter@v2.21
with:
path: ./ftw-0x-beta
- id: stable
name: Detect the stable app
run: |
if [ -f ftw/config.yaml ]; then
echo "exists=true" >>"${GITHUB_OUTPUT}"
else
echo "exists=false" >>"${GITHUB_OUTPUT}"
echo "::notice::ftw/config.yaml is absent until the first stable promotion"
fi
- uses: frenck/action-addon-linter@v2.21
if: steps.stable.outputs.exists == 'true'
with:
path: ./ftw

prepare-build:
name: Prepare native build matrix
Expand Down
2 changes: 2 additions & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ qualification yet. Follow [the pilot checks](pilot/README.md).
The records below describe the retired app, not current install recommendations.
2.x and 3.x receive no more updates. The app does not follow new 0.x; see
[the install choices](README.md).
The store no longer lists these apps. The last beta manifest is kept in
`ftw-beta/retired.yaml`, which Supervisor does not load as an app.

`compatibility.yaml` (schema 2) is the source of truth for what each app
installs. The app version mirrors the FTW Core version it runs.
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ Open **Settings → Apps → Install app → ⋮ → Repositories**, add
`https://github.com/srcfl/home-assistant-addons`, install **FTW (0.x beta)**,
start it and choose **Open web UI**.

The old **FTW (beta)** and **FTW** apps below stay on the retired 3.x line and
are unchanged.
The app store offers only **FTW (0.x beta)**. The retired 3.x apps are no
longer listed for new installs. Existing installations and their data stay
on the device; keep a backup before moving to the new app.

## Alternative: FTW on a separate Linux host

Expand Down
16 changes: 14 additions & 2 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

Sourceful maintains this repository. Fredrik owns FTW's product direction.
The app slug is `ftw-0x-beta`; its name is **FTW (0.x beta)**.
Home Assistant has no `beta` stage. Keep the beta name and version, and omit
`stage` so the store does not add an Experimental flag. This does not promote
FTW to stable.
An already installed app keeps its old metadata until its next update or
rebuild. Reloading the store applies the new store listing; no reinstall is
needed.

1. Core publishes a native 0.x beta with both Linux packages, their checksums
and `ftw-native-release.json`.
Expand Down Expand Up @@ -43,6 +49,12 @@ recovery. Their presence does not authorize publication. The
[previous release procedure](https://github.com/srcfl/home-assistant-addons/blob/82172cec84d7d756e35ed40da48e82c59ef5a6f1/RELEASING.md)
describes that historical pipeline; it is not the current release policy.

Keep the old apps and their data available for recovery. Do not remove them
as part of the 0.x beta launch. New installs use
The old apps are no longer listed in the store. `ftw-beta/retired.yaml` keeps
the last manifest for recovery; do not rename it to `config.yaml`, `config.yml`
or `config.json`. Supervisor would list it again, even in a nested folder.
The old `ftw` stable app has no manifest and was never published.

Removing a store entry does not uninstall an existing app or delete its data.
HA may flag the old app as removed from its repository. Keep its backup and
published images for recovery. New installs use
[FTW (0.x beta)](ftw-0x-beta/DOCS.md), or the separate-host route in the README.
2 changes: 2 additions & 0 deletions ftw-0x-beta/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
## 0.154.1-beta.1

- First FTW 0.x beta app, built from the v0.154.1-beta.1 release package.
- Show this beta in the app store without Home Assistant's Experimental flag.
- Remove the retired 3.x app from the store for new installs.
2 changes: 1 addition & 1 deletion ftw-0x-beta/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# FTW (0.x beta)

New FTW 0.x for Home Assistant OS, built on the device from the checksummed
release package of a pinned FTW beta. Experimental: run it on one site at a
release package of a pinned FTW beta. Beta: run it on one site at a
time, and stop any other FTW (old app or separate host) before this one
controls equipment. See [DOCS.md](DOCS.md).
1 change: 0 additions & 1 deletion ftw-0x-beta/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ url: https://github.com/srcfl/home-assistant-addons/tree/main/ftw-0x-beta
arch:
- amd64
- aarch64
stage: experimental
host_network: true
ports:
8080/tcp: 8080
Expand Down
5 changes: 3 additions & 2 deletions ftw-beta/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# FTW (beta) — old release line

2.x and 3.x receive no further updates. This app does not follow new 0.x.
Do not install the 3.x beta to get current FTW. No new-line app has shipped;
the stable app remains unpublished.
This app is no longer in the store. Its last manifest is kept in
`retired.yaml` for recovery. New installs use
[FTW (0.x beta)](../ftw-0x-beta/DOCS.md); the old stable app was never published.

Use [Install and update FTW](https://github.com/srcfl/ftw/blob/master/docs/native-beta.md)
to run current FTW on a separate Linux host. Keep a backup of this app and
Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions ftw/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# FTW — old release line

2.x and 3.x receive no further updates. This app does not follow new 0.x.
Do not install the 3.x beta to get current FTW. No new-line app has shipped;
the stable app remains unpublished.
This stable app was never published. New installs use
[FTW (0.x beta)](../ftw-0x-beta/DOCS.md).

Use [Install and update FTW](https://github.com/srcfl/ftw/blob/master/docs/native-beta.md)
to run current FTW on a separate Linux host. Keep a backup of this app and
Expand Down
22 changes: 20 additions & 2 deletions scripts/validate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Validate the repository, both app manifests and the compatibility record."""
"""Validate the app catalog and retired compatibility records."""

from __future__ import annotations

Expand Down Expand Up @@ -83,6 +83,23 @@ def validate_repository() -> None:
require(dockerfile.is_file(), "ftw/Dockerfile is the shared build context and must exist")
validate_core_image_tag_contract(dockerfile.read_text(encoding="utf-8"))
require(not (ROOT / "ftw-beta" / "Dockerfile").exists(), "ftw-beta must not carry its own Dockerfile")
validate_catalog()


def validate_catalog() -> None:
# Supervisor finds config files at any depth, skipping hidden and rootfs folders.
manifests = {
path.relative_to(ROOT).as_posix()
for path in ROOT.glob("**/config.*")
if path.suffix in (".yaml", ".yml", ".json")
and not any(part.startswith(".") or part == "rootfs" for part in path.relative_to(ROOT).parts)
}
require(
manifests == {"ftw-0x-beta/config.yaml"},
f"only ftw-0x-beta may be listed in the app store, found {sorted(manifests)}",
)
config = load_yaml(ROOT / "ftw-0x-beta/config.yaml")
require("stage" not in config, "native beta must omit stage; Home Assistant has no beta stage")


def validate_core_pin(core: Any, channel: str) -> None:
Expand Down Expand Up @@ -222,7 +239,8 @@ def validate_channel(channel: str, compat: dict[str, Any]) -> None:
validate_beta(compat)
else:
validate_stable(compat, required=True)
config = load_yaml(add_on_directory(channel) / "config.yaml")
filename = "retired.yaml" if channel == "beta" else "config.yaml"
config = load_yaml(add_on_directory(channel) / filename)
validate_add_on_config(config, compat, channel)


Expand Down
2 changes: 1 addition & 1 deletion tests/test_upstream_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def load_repository() -> tuple[dict, str, str]:
root = upstream_sync.ROOT
return (
upstream_sync.load_yaml(root / "compatibility.yaml"),
(root / "ftw-beta/config.yaml").read_text(encoding="utf-8"),
(root / "ftw-beta/retired.yaml").read_text(encoding="utf-8"),
(root / "ftw-beta/CHANGELOG.md").read_text(encoding="utf-8"),
)

Expand Down
28 changes: 27 additions & 1 deletion tests/test_validate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from __future__ import annotations

import copy
import pathlib
import tempfile
import unittest
from unittest import mock

import yaml

Expand All @@ -16,7 +19,7 @@

def load_repository() -> tuple[dict, dict, str]:
compat = validate.load_yaml(validate.ROOT / "compatibility.yaml")
beta_text = (validate.ROOT / "ftw-beta/config.yaml").read_text(encoding="utf-8")
beta_text = (validate.ROOT / "ftw-beta/retired.yaml").read_text(encoding="utf-8")
return compat, yaml.safe_load(beta_text), beta_text


Expand Down Expand Up @@ -50,6 +53,29 @@ def setUp(self) -> None:
def test_repository_layout_passes(self) -> None:
validate.validate_repository()

def test_retired_app_cannot_return_to_the_store(self) -> None:
for filename in ("config.yaml", "config.yml", "config.json"):
with self.subTest(filename=filename), tempfile.TemporaryDirectory() as directory:
root = pathlib.Path(directory)
(root / "ftw-0x-beta").mkdir()
(root / "ftw-0x-beta/config.yaml").write_text("name: FTW (0.x beta)\n")
old = root / "archive/ftw-beta"
old.mkdir(parents=True)
(old / filename).write_text("{}\n")
with mock.patch.object(validate, "ROOT", root):
with self.assertRaisesRegex(validate.ValidationError, "only ftw-0x-beta"):
validate.validate_catalog()

def test_native_beta_does_not_use_a_stage_flag(self) -> None:
for stage in ("experimental", "beta"):
with self.subTest(stage=stage), tempfile.TemporaryDirectory() as directory:
root = pathlib.Path(directory)
(root / "ftw-0x-beta").mkdir()
(root / "ftw-0x-beta/config.yaml").write_text(f"stage: {stage}\n")
with mock.patch.object(validate, "ROOT", root):
with self.assertRaisesRegex(validate.ValidationError, "must omit stage"):
validate.validate_catalog()

def test_dockerfile_binds_the_image_tag_to_the_core_version(self) -> None:
validate.validate_core_image_tag_contract(self.dockerfile)
for version in ("v3.4.2-beta.4", "v3.4.2"):
Expand Down
Loading