ka-promote: normalise git-format-patch trailers (-- \n<version>\n) when concatenating #31

Closed
opened 2026-05-18 16:15:53 +00:00 by marfrit · 0 comments
Owner

Problem

ka-promote ohm concatenates per-include .patch files into a single cumulative.patch and assumes each input is well-formed for patch(1) to parse in series. Discovered during ohm pkgrel=4/5 builds (PR #28): whether a patch needs a trailing -- \n<version>\n\n sentinel depends on its position in the concatenation:

  • A patch that is not last in the cumulative needs the trailer so patch(1) knows where the patch ends and the next From <sha> header begins.
  • A patch that is last must NOT have an orphan trailer, because patch(1) reads it at EOF as the start of a malformed new patch and errors out (malformed patch at line N: 2.54.0).

This means source patches in patches/... series-dirs have to be position-aware, which is brittle: adding a new include at the end of fleet/<host>.yaml requires editing the trailer of the patch that used to be last.

Reproducer

In fleet/ohm.yaml during the pkgrel=4 → pkgrel=5 work:

  • pkgrel=4 had 3 includes; SCS was last → SCS trailer had to be stripped (commit 84734ba).
  • pkgrel=5 added a 4th include (besser#18 fix) → SCS no longer last, needs trailer back (commit added today, ceec602afa85...).

Proposed fix

Make ka-promote's write_cumulative() normalise trailers automatically:

def write_cumulative(resolved, out_path):
    with open(out_path, "wb") as out:
        for i, r in enumerate(resolved):
            with open(r["src"], "rb") as src:
                data = src.read()
            # Strip any trailing git-format-patch sentinel from the source
            data = strip_trailer(data)
            out.write(data)
            # Add a separator between patches, but not after the last one
            if i != len(resolved) - 1:
                out.write(b"-- \n2.54.0\n\n")
    ...

def strip_trailer(data):
    # Match common 'git format-patch' trailers regardless of version
    import re
    return re.sub(rb'\n-- \n\d+\.\d+\.\d+\n+\Z', b'\n', data)

This way source patches in patches/... can be in either shape; the cumulative is always well-formed for patch(1) regardless of include ordering.

Acceptance

  • Adding or reordering an include in fleet/<host>.yaml does not require editing any patch source file's trailer.
  • Existing trailer-stripped patches (the current state of patches/arch/arm64/xor-neon-...-style) continue to work.
  • ka-promote --validate-against <clean-checkout> continues to pass on ohm.

Companion

Once this lands, the brittle commit history on PR #28 (multiple trailer flip-flops as the cumulative shape changed) can be linted away in a one-shot reformat of source patches to a canonical form. Tracked here separately from the migration to keep PR #28 focused on shipping.

## Problem `ka-promote ohm` concatenates per-include `.patch` files into a single `cumulative.patch` and assumes each input is well-formed for `patch(1)` to parse in series. Discovered during ohm pkgrel=4/5 builds (PR #28): whether a patch needs a trailing `-- \n<version>\n\n` sentinel depends on its position in the concatenation: - A patch that is *not last* in the cumulative needs the trailer so `patch(1)` knows where the patch ends and the next `From <sha>` header begins. - A patch that *is last* must NOT have an orphan trailer, because `patch(1)` reads it at EOF as the start of a malformed new patch and errors out (`malformed patch at line N: 2.54.0`). This means source patches in `patches/...` series-dirs have to be position-aware, which is brittle: adding a new include at the end of `fleet/<host>.yaml` requires editing the trailer of the patch that used to be last. ## Reproducer In `fleet/ohm.yaml` during the pkgrel=4 → pkgrel=5 work: - pkgrel=4 had 3 includes; SCS was last → SCS trailer had to be stripped (commit 84734ba). - pkgrel=5 added a 4th include (besser#18 fix) → SCS no longer last, needs trailer back (commit added today, ceec602afa85...). ## Proposed fix Make `ka-promote`'s `write_cumulative()` normalise trailers automatically: ```python def write_cumulative(resolved, out_path): with open(out_path, "wb") as out: for i, r in enumerate(resolved): with open(r["src"], "rb") as src: data = src.read() # Strip any trailing git-format-patch sentinel from the source data = strip_trailer(data) out.write(data) # Add a separator between patches, but not after the last one if i != len(resolved) - 1: out.write(b"-- \n2.54.0\n\n") ... def strip_trailer(data): # Match common 'git format-patch' trailers regardless of version import re return re.sub(rb'\n-- \n\d+\.\d+\.\d+\n+\Z', b'\n', data) ``` This way source patches in `patches/...` can be in either shape; the cumulative is always well-formed for `patch(1)` regardless of include ordering. ## Acceptance - Adding or reordering an include in `fleet/<host>.yaml` does not require editing any patch source file's trailer. - Existing trailer-stripped patches (the current state of `patches/arch/arm64/xor-neon-...`-style) continue to work. - `ka-promote --validate-against <clean-checkout>` continues to pass on ohm. ## Companion Once this lands, the brittle commit history on PR #28 (multiple trailer flip-flops as the cumulative shape changed) can be linted away in a one-shot reformat of source patches to a canonical form. Tracked here separately from the migration to keep PR #28 focused on shipping.
Sign in to join this conversation.