diff --git a/.gitignore b/.gitignore index b558bb7..9594b4d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ venv *.bin *.json +config/ diff --git a/README.md b/README.md index f92150f..b3ffb3c 100644 --- a/README.md +++ b/README.md @@ -2,39 +2,43 @@ ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/esphome/build-action) - This action takes a yaml file for an ESPHome device and will compile and output -the build firmware file and a partial `manifest.json` file that can be used to flash +the build firmware file and a partial `manifest.json` file that can be used to flash a device via [ESP Web Tools](https://esphome.github.io/esp-web-tools). ## Example usage ```yaml - uses: esphome/build-action@v1 with: yaml_file: my_configuration.yaml - ``` This action is used by the [ESPHome publish workflow](https://github.com/esphome/workflows/blob/main/.github/workflows/publish.yml) that is used to compile firmware and publish simple GitHub pages sites for projects. ## Inputs -Name | Default | Description -------------|---------------|------------ -`yaml_file` | _None_ | The YAML file to be compiled. -`version` | `latest` | The ESPHome version to build using. -`platform` | `linux/amd64` | The docker platform to use during build. (linux/amd64, linux/arm64, linux/arm/v7) -`cache` | `false` | Whether to cache the build folder. +| Name | Default | Description | +| ----------------- | ------------- | --------------------------------------------------------------------------------- | +| `yaml_file` | _None_ | The YAML file to be compiled. | +| `version` | `latest` | The ESPHome version to build using. | +| `platform` | `linux/amd64` | The docker platform to use during build. (linux/amd64, linux/arm64, linux/arm/v7) | +| `cache` | `false` | Whether to cache the build folder. | +| `release_summary` | _None_ | A small summary of the release that will be added to the manifest file. | +| `release_url` | _None_ | A URL to the release page that will be added to the manifest file. | ## Outputs -Name | Description -----------|------------ -`name` | The name of the device in yaml with the platform (eg. ESP32 or ESP8266) appended. -`version` | The ESPHome version used during build. +| Name | Description | +| --------------- | --------------------------------------------------------------------------------- | +| `name` | The name of the device in yaml with the platform (eg. ESP32 or ESP8266) appended. | +| `version` | The ESPHome version used during build. | +| `original_name` | The original name of the device in yaml. | ## Output files -This action will output a folder named with the output `name` and will contain two files, `{name}.bin` and `manifest.json`. +This action will output a folder named with the output `name` and will contain three files: + +- `manifest.json` - This goes into the `builds` section of an esp-web-tools manifest.json. +- `{name}.factory.bin` - The firmware to be flashed with esp-web-tools. +- `{name}.ota.bin` - The firmware that can be flashed over-the-air to the device using the [Managed Updated via HTTP Request](https://esphome.io/components/update/http_request). diff --git a/action.yml b/action.yml index 833fb3a..7543829 100644 --- a/action.yml +++ b/action.yml @@ -17,14 +17,25 @@ inputs: description: Cache build directory required: false default: false + release_summary: + description: Release summary + required: false + default: "" + release_url: + description: Release URL + required: false + default: "" outputs: name: - description: Name of device extracted from configuration + description: Name of device extracted from configuration with the platform appended value: ${{ steps.build-step.outputs.name }} version: description: ESPHome version value: ${{ steps.build-step.outputs.esphome-version }} + original_name: + description: Original name of device extracted from configuration + value: ${{ steps.build-step.outputs.original_name }} runs: using: composite @@ -79,6 +90,7 @@ runs: -v "$(pwd)":"/github/workspace" -v "$HOME:$HOME" \ --user $(id -u):$(id -g) \ -e INPUT_YAML_FILE -e HOME \ + -e INPUT_RELEASE_SUMMARY -e INPUT_RELEASE_URL \ -e GITHUB_JOB -e GITHUB_REF -e GITHUB_SHA -e GITHUB_REPOSITORY \ -e GITHUB_REPOSITORY_OWNER -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER \ -e GITHUB_ACTOR -e GITHUB_OUTPUT \ diff --git a/entrypoint.py b/entrypoint.py index 9a55969..90c6812 100755 --- a/entrypoint.py +++ b/entrypoint.py @@ -8,6 +8,7 @@ from pathlib import Path import re import yaml +import hashlib ESP32_CHIP_FAMILIES = { @@ -62,6 +63,9 @@ name = config["esphome"]["name"] +with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as github_output: + print(f"original_name={name}", file=github_output) + platform = "" if "esp32" in config: platform = config["esp32"]["variant"].lower() @@ -91,30 +95,39 @@ elf = Path(data["prog_path"]) if platform == "rp2040": - source_bin = elf.with_name("firmware.uf2") - dest_bin = file_base / f"{name}.uf2" + source_factory_bin = elf.with_name("firmware.uf2") + dest_factory_bin = file_base / f"{name}.uf2" else: - source_bin = elf.with_name("firmware.factory.bin") - dest_bin = file_base / f"{name}.bin" + source_factory_bin = elf.with_name("firmware.factory.bin") + dest_factory_bin = file_base / f"{name}.factory.bin" + +source_ota_bin = elf.with_name("firmware.ota.bin") +dest_ota_bin = file_base / f"{name}.ota.bin" + print("::endgroup::") print("::group::Copy firmware file(s) to folder") file_base.mkdir(parents=True, exist_ok=True) -shutil.copyfile(source_bin, dest_bin) +shutil.copyfile(source_factory_bin, dest_factory_bin) +shutil.copyfile(source_ota_bin, dest_ota_bin) print("::endgroup::") -if platform == "rp2040": - sys.exit(0) print("::group::Write manifest.json file") + chip_family = None define: str +has_factory_part = False for define in data["defines"]: if define == "USE_ESP8266": chip_family = "ESP8266" + has_factory_part = True + break + if define == "USE_RP2040": + chip_family = "RP2040" break if m := re.match(r"USE_ESP32_VARIANT_(\w+)", define): chip_family = m.group(1) @@ -122,17 +135,31 @@ raise Exception(f"Unsupported chip family: {chip_family}") chip_family = ESP32_CHIP_FAMILIES[chip_family] + has_factory_part = True break +ota_md5 = hashlib.md5(open(dest_ota_bin, "rb").read()).hexdigest() + manifest = { "chipFamily": chip_family, - "parts": [ + "ota": { + "path": str(dest_ota_bin), + "md5": ota_md5, + }, +} + +if release_summary := os.environ.get("INPUT_RELEASE_SUMMARY"): + manifest["ota"]["summary"] = release_summary +if release_url := os.environ.get("INPUT_RELEASE_URL"): + manifest["ota"]["release_url"] = release_url + +if has_factory_part: + manifest["parts"] = [ { - "path": str(dest_bin), + "path": str(dest_factory_bin), "offset": 0x00, } - ], -} + ] print("Writing manifest file:") print(json.dumps(manifest, indent=2))