Shell AI generated content : freebsd-automerge — An automated upgrade assistant for FreeBSD

# freebsd-automerge.sh — An automated upgrade assistant for FreeBSD

## Introduction

I would like to share a shell script I developed with the assistance of Claude
(Anthropic's AI) to automate the most tedious part of FreeBSD upgrades: resolving
merge conflicts in configuration files.

The script is called **freebsd-automerge.sh** and it handles the complete upgrade
workflow from start to finish, including conflict resolution, backup, and the
install/reboot cycle.

---

## The problem it solves

Anyone who has upgraded FreeBSD using `freebsd-update upgrade` knows the pain:
when configuration files have been locally modified, freebsd-update stops and
asks you to manually resolve conflicts one by one in an editor. On a system with
many customized files, this can mean dozens of interruptions requiring careful
manual editing.

The standard approach (mergemaster, etcupdate) works well but still requires
significant manual intervention. I wanted something fully automated that could
handle an upgrade — including a major version jump — with minimal human input.

---

## How it works

The core idea is a **3-way merge**:

```
base OLD (e.g. 14.3-RELEASE clean) → your customized file = YOUR changes
base OLD (e.g. 14.3-RELEASE clean) → base NEW (14.4-RELEASE) = FreeBSD changes
```

By comparing both sets of changes independently against the original clean base,
the script can automatically apply both your customizations and FreeBSD's updates
without conflicts — exactly the same mechanism git uses for merging branches.

The script operates in two distinct modes:

### EDITOR mode
The script registers itself as the `$EDITOR` for `freebsd-update`:

```sh
EDITOR=/root/freebsd-automerge.sh freebsd-update -r 14.4-RELEASE upgrade
```

When freebsd-update encounters a file it cannot merge automatically, instead of
opening nano it calls our script with the conflicted file path. The script then:

1. Extracts the clean OLD base version from `base.txz`
2. Extracts the clean NEW base version from `base.txz`
3. Uses `merge(1)` (from the base system) for the 3-way merge in-place
4. If residual conflicts remain, falls back to nano for manual resolution
5. Returns control to freebsd-update

### MAIN mode
The primary workflow:

1. Interactive menu querying the FreeBSD mirror in real time to show available
RELEASE, STABLE, RC, and BETA targets
2. Downloads `base.txz` for both OLD and NEW releases
3. Runs `freebsd-update upgrade` with itself as EDITOR
4. After upgrade, scans `/etc` and `/usr/share` for any remaining `<<<<<<<`
markers and resolves them with the same 3-way merge
5. Creates a centralized backup of all conflicted files with SHA256 manifest
and generates a `restore.sh` script for rollback
6. Runs `freebsd-update install` and handles the reboot cycle automatically,
including the two-pass install required for major version upgrades

---

## Key features

- **Zero configuration** — detects OLD and NEW versions automatically from
`uname -r` and `/var/db/freebsd-update/tag`
- **Live mirror query** — the target selection menu fetches available versions
directly from the FreeBSD mirror at runtime
- **Kernel/userland selection** — asks whether to upgrade both or userland only,
useful for systems running custom kernels (e.g. ARM SBCs with custom DRM drivers)
- **Auto-confirm mode** — `--auto-confirm` flag uses `expect(1)` to answer
freebsd-update's interactive prompts automatically
- **Major upgrade support** — handles the two-install, two-reboot cycle for
major version jumps (e.g. 14.x → 15.x) using a temporary `rc.d` service
that survives the first reboot
- **Safe by design** — full backup before touching anything, restore script
generated automatically, SHA256 integrity verification

---

## Current limitations and known bugs

I want to be transparent about what still needs work:

1. **STABLE branch detection** — the menu regex for `-STABLE` targets does not
always match the mirror directory listing correctly

2. **State recovery after interruption** — if the script is killed mid-upgrade,
resuming it does not correctly re-enter the freebsd-update merge phase;
it tries to call `freebsd-update install` prematurely

3. **grep -c arithmetic** — a subtle shell arithmetic bug produces
`[: 0 0: bad number` in some cases when counting conflict markers

4. **STABLE/CURRENT upgrade type** — `is_major_upgrade()` only compares
major version numbers; it does not distinguish STABLE or CURRENT targets
which may need different handling

5. **Terminal compatibility** — Unicode box-drawing characters in menus do not
render correctly in all FreeBSD terminal configurations; should fall back
to plain ASCII

---

## What I would love feedback on

This is where I genuinely need input from people who know FreeBSD internals
better than I do:

**1. Is using `$EDITOR` the right hook into freebsd-update?**
Is there a cleaner, more supported way to intercept freebsd-update's merge
phase? I am essentially abusing the EDITOR variable — it works, but it feels
fragile. Is there an official API or hook mechanism I am missing?

**2. The 3-way merge approach — is merge(1) the right tool?**
I am using `merge(1)` from the `rcs` package. Are there better alternatives
in the FreeBSD base system? `diff3(1)` is available but requires more
orchestration. `patch(1)` could work but loses context. Thoughts?

**3. Handling STABLE and CURRENT upgrades**
Upgrading to a -STABLE or -CURRENT branch is fundamentally different from
a RELEASE upgrade — freebsd-update behaves differently, and the base.txz
download path is different. Has anyone automated this reliably?

**4. The two-reboot major upgrade cycle**
I implemented this using a temporary `rc.d` service (`automerge_resume`)
that runs `freebsd-update install` after the first reboot and then removes
itself. It works, but it feels like a hack. Is there a cleaner pattern?

**5. pkg alignment after major upgrades**
After a major upgrade (e.g. 14.x → 15.x), all installed packages need to be
rebuilt or reinstalled. I currently just tell the user to run `pkg upgrade -f`
manually. Should this be integrated into the script? What is the safest
sequence?

---

## How to get it

The script is a single self-contained shell file with no external dependencies
beyond `merge(1)` (from `pkg install rcs`) and optionally `expect(1)` for
auto-confirm mode.

Usage:

```sh
# Show interactive target menu (queries mirror live)
sh freebsd-automerge.sh

# Filter menu to branch 14
sh freebsd-automerge.sh 14

# Direct upgrade, no menu
sh freebsd-automerge.sh 14.4-RELEASE

# Fully automated (requires: pkg install expect)
sh freebsd-automerge.sh --auto-confirm 14
```

---

## Credits

The script was conceived and directed by **ZioMario** (forums.freebsd.org user:
ZioMario), developed with the assistance of **Claude** (Anthropic AI).
The idea, architecture decisions, testing, and debugging were all driven by
ZioMario — Claude was used as an implementation tool.

All feedback, pull requests, and brutal criticism are welcome.
 

Attachments

  • Thanks
Reactions: vmb
Some structural fixes applied :

1. Architecture Use uname -m/uname -p correctly instead of the pointless case switch
2. die() Use printf instead of echo to properly handle \n escape sequences
3. Editor open_editor() respects $VISUAL/$EDITOR env vars, falls back to ee then vi (both always present in FreeBSD base)
4. ASCII boxes All Unicode box-drawing characters replaced with plain ASCII +, =, |
5. grep -c Fixed "bad number" error by explicitly initializing counters to 0 before grep
6. Tag file parsing Use `awk -F'
7. upgrade_type() New function that properly distinguishes MAJOR / MINOR / STABLE / CURRENT upgrades
8. STABLE warning Menu marks STABLE entries with (*) and warns that snapshots may be temporarily unavailable on the mirror

Rename zip file in sh.
 

Attachments

"All feedback, pull requests, and brutal criticism are welcome."

Nothing brutal towards you, but "AI" generated content should be a flag on things created with "AI" assistance on the forums. Or at least upfront in the title.

There are a few of these posts on the forums that don't have an "AI" advisory in the title.

In the same way that things are labelled for potential health risks, for example. Or hazardous material warnings, for another example.
 
"All feedback, pull requests, and brutal criticism are welcome."

Nothing brutal towards you, but "AI" generated content should be a flag on things created with "AI" assistance on the forums. Or at least upfront in the title.

There are a few of these posts on the forums that don't have an "AI" advisory in the title.

In the same way that things are labelled for potential health risks, for example. Or hazardous material warnings, for another example.

Sure. I changed the title.
 
Back
Top