Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/breaking_configuration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ body:
attributes:
label: Versions
description: Feint, the client, and the provider — `feint version`, `terraform version`.
placeholder: "feint 0.15.0, terraform 1.13.3, scaleway/scaleway 2.81.0"
placeholder: "feint 0.16.0, terraform 1.13.3, scaleway/scaleway 2.81.0"
validations:
required: true

Expand Down
78 changes: 78 additions & 0 deletions CHANGELOG.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,25 @@ change ni l'un ni l'autre a sa place dans `git log`.

## [Unreleased]

## [0.16.0] - 2026-11-11

### Ajouté

- **`feint checkpoint` et `feint reset`** (#853) : un point de reprise
enregistre le plan de contrôle et les octets sous un seul nom, et un reset y
ramène le labo. Il détruit ce que le point de reprise ne contient pas,
redémarre ce que l'hôte ne détient plus, et réapplique les jeux de règles de
pare-feu de chaque pack sur le runtime. Le verdict n'est pas celui de
l'opération : il compare ce qu'un client peut observer, le plan de contrôle,
les octets relus à travers les portes et ce que l'hôte détient, et il répond
« did not converge » au lieu de rapporter l'intention.
- **Une route d'installation Linux dans le démarrage rapide** (#843), sous
celle de Homebrew et générée depuis la même version que la page
d'installation. Six lignes, et elles vérifient : les octets sont contrôlés
contre le `checksums.txt` publié avant toute installation. Ce que cela ne
prouve pas est dit plutôt que sous-entendu : cela prouve que les octets
correspondent à la liste, pas qui a écrit la liste, et la signature de la
liste reste dans `docs/install.md`.
- **`feint run` possède son laboratoire** (#848) : sans `--endpoint`, il démarre
un émulateur, l'attend, résout l'environnement du provider comme le fait
`feint env`, lance la commande et arrête tout ce qu'il a démarré.
Expand All @@ -37,6 +54,28 @@ change ni l'un ni l'autre a sa place dans `git log`.

### Modifié

- **Le chiffre de couverture nomme la population qu'il a comptée.** Il disait
« Of the 548 operations upstream declares » alors que le scan Scaleway couvre
11 des 56 produits de ce SDK : un tiers du fournisseur était imprimé comme le
fournisseur. Une stack qui utilise un produit hors du scan part vers le vrai
endpoint, et le README dit maintenant de quel scan il parle.
- **Le README ne dit plus que l'object storage est hors périmètre.** Il est
sorti en 0.15.0 ; la phrase était hors des blocs générés, là où `feint docs
--check` ne pouvait pas l'atteindre.
- **Un tag est refusé tant qu'un jalon ouvert réclame une version déjà
publiée** (#877). `v0.14.0` et `v0.15.0` étaient publiées alors que des jalons
portant leur nom restaient ouverts avec 19 et 10 issues : le suivi annonçait
deux versions sorties comme inachevées. `tools/release/milestone-gate.sh` pose
la question avant chaque tag, et dit quand il n'a pas pu mesurer plutôt que de
refuser pour une raison qui ne concerne pas le dépôt.
- **`docs/limits.md` a rattrapé des comportements changés depuis des mois**
(#857). Onze sections citaient des issues fermées depuis la dernière
relecture ; trois étaient fausses, dont un titre annonçant que le provider
Terraform Exoscale est refusé alors qu'il pilote le pack depuis septembre.
- **La frontière de ce produit est écrite** (#196, #733) : `docs/limits.md`
énonce que feint ne gère jamais une ressource dont la perte compterait. Deux
textes courants disaient le contraire l'un de l'autre depuis deux mois, sans
se citer.
- **`feint run` laisse la sortie standard à la commande qu'il lance.** Sa
propre prose part sur la sortie d'erreur et le reçu JSON dans un fichier.
Mesuré plutôt que raisonné : la première version écrivait le reçu sur la
Expand All @@ -50,6 +89,45 @@ change ni l'un ni l'autre a sa place dans `git log`.
signalé. Le reçu rapporte aussi le runtime et les capacités que l'émulateur
déclare, et non le mode demandé.

### Corrigé

- **Un reset remet une règle de pare-feu sur l'interface, pas seulement dans le
store** (#871). `Survey` ne rend que des noms, et un jeu de règles dont le
contenu change garde le sien : rien dans l'observation ne pouvait voir la
différence, et un reset annonçait la convergence pendant que le runtime
appliquait encore les règles d'un état que le point de reprise ne décrit pas.
Reproduit par une connexion TCP sous `--vm incus` avant d'être corrigé. La
même passe a corrigé un reset qui démarrait la machine d'un enregistrement
décrit comme arrêté, puis écrivait « running » par-dessus l'état qu'on lui
demandait de retrouver.
- **Le verdict du reset compare les octets d'un objet**, et plus seulement son
nom (#871). Il lisait l'empreinte dans un champ que `ListObjectsV2` n'émet
pas : le champ était toujours vide, et deux contenus différents sous une même
clé se comparaient égaux. Chaque objet est maintenant récupéré et haché par
l'observateur.
- **`feint run` échoue quand il n'a pas tenu une promesse** (#871). Seul
« did not converge » faisait échouer la commande, donc un reset refusé, un
reset illisible et un labo que la commande possédait sans pouvoir l'arrêter
laissaient tous le code de sortie à celui de l'enfant.
- **Un point de reprise est entier, ou c'est le précédent** (#871). Les octets
étaient enregistrés avant l'instantané du plan de contrôle : un échec à la
seconde étape laissait un point de reprise antérieur du même nom avec son
ancien store à côté des nouvelles références d'objets.
- **Les points de reprise nommés cessent de s'écraser** (#871) : l'émulateur en
détient autant qu'on veut, le CLI en écrivait un par instance, donc
`checkpoint A` puis `checkpoint B` rendait `reset A` impossible.
- **Le crochet pre-commit `gofmt` échoue quand gofmt ne peut pas s'exécuter**
(#829). Il lisait la liste des fichiers à reformater et jamais le code de
sortie : tout échec de gofmt produisait une liste vide, et le crochet
répondait « Passed ». Sa fenêtre est un clone neuf avant `mise trust`,
c'est-à-dire le premier commit d'un nouveau contributeur.
- **Un verdict d'équilibrage est tiré de vingt-quatre connexions, pas six**
(#559). Six, c'est une pièce lancée six fois : un équilibreur qui fonctionne
envoyait toutes les connexions du même côté environ une fois sur trente-deux,
et la suite rougissait sur un sujet qui se comportait bien. Le même calcul
valait dans l'autre sens après un délien, en faux vert sur un vrai défaut.


## [0.15.0] - 2026-11-11

### Ajouté
Expand Down
73 changes: 73 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,24 @@ what this project is judged on: **a response shape a client can observe**, and

## [Unreleased]

## [0.16.0] - 2026-11-11

### Added

- **`feint checkpoint` and `feint reset`** (#853): a checkpoint records the
control plane and the object bytes under one name, and a reset returns the
lab to it — destroying what the checkpoint does not hold, starting again what
the host no longer holds, and putting each pack's firewall rule sets back on
the runtime. The verdict is not the operation's own: it compares what a
client can observe, control plane, bytes read back through the doors, and
what the host holds, and it says `did not converge` rather than reporting the
intention.
- **A Linux install route in the Quick Start** (#843), under the Homebrew one
and generated from the same version the install page is pinned to. Six lines,
and they verify: the bytes are checked against the published `checksums.txt`
before anything is installed. What that does not prove is said rather than
implied — it proves the bytes match the list, not who wrote the list, and the
signature over the list stays in `docs/install.md`.
- **`feint run` owns the lab** (#848): with no `--endpoint` it starts an
emulator, waits for it, resolves the provider environment the way `feint env`
does, runs the command and stops everything it started. `--provider` narrows
Expand All @@ -34,6 +50,28 @@ what this project is judged on: **a response shape a client can observe**, and

### Changed

- **The coverage headline names the population it counted.** It read "Of the
548 operations upstream declares" while the Scaleway scan covers 11 of the 56
products in that SDK, so a third of the provider was printed as the provider.
A stack using a product outside the scan reaches the real endpoint, and the
README now says which scan it is talking about.
- **`README.md` stops saying object storage is out of scope.** It shipped in
0.15.0; the sentence sat outside the generated blocks, where `feint docs
--check` could not reach it.
- **A tag is refused while an open milestone claims a version already tagged**
(#877). `v0.14.0` and `v0.15.0` were both published while milestones named
after them stayed open with 19 and 10 issues, so the tracker said two
released versions were unfinished. `tools/release/milestone-gate.sh` asks
before each tag, and says when it could not measure rather than refusing for
a reason that is not about the repository.
- **`docs/limits.md` caught up with behaviour that changed months ago** (#857).
Eleven sections cited issues that had closed since anyone last read them;
three were false, including a heading saying the Exoscale Terraform provider
is refused when it has been driving the pack since September.
- **The boundary of this product is written down** (#196, #733):
`docs/limits.md` states that feint never manages a resource whose loss would
matter. Two current texts had said opposite things for two months, neither
citing the other.
- **`feint run` leaves stdout to the command it runs.** Its own prose goes to
stderr and the JSON receipt to a path. Measured rather than reasoned: the
first version wrote the receipt to stdout, and replaying a surveyed stack
Expand All @@ -45,6 +83,41 @@ what this project is judged on: **a response shape a client can observe**, and
that reported it. The receipt also reports the runtime and the capabilities
the emulator declares, rather than the mode that was asked for.

### Fixed

- **A reset puts a firewall rule back on the interface, not only in the store**
(#871). `Survey` answers names, and a rule set whose content changed keeps its
name, so nothing in the observation could see the difference: a reset reported
convergence while the runtime went on enforcing the rules of a state the
checkpoint does not describe. Reproduced with a TCP connection under
`--vm incus` before being fixed. The same pass fixed a reset starting the
machine of a record the checkpoint describes as stopped, and then writing
`running` over the state it had been asked to return to.
- **The reset verdict compares the bytes of an object**, not just its name
(#871). It read the digest from a field `ListObjectsV2` does not emit, so the
field was always empty and two different contents under one key compared
equal. Each object is now fetched and hashed by the observer.
- **`feint run` fails when it did not keep a promise** (#871). Only
`did not converge` used to fail the run, so a failed reset, one that could not
be read back, and a lab this command owned and could not stop all left the
exit code at the child's.
- **A checkpoint is whole or it is the previous one** (#871). The bytes were
saved before the control-plane snapshot, so a failure in the second step left
an earlier checkpoint of that name holding its old store beside the new object
references.
- **Named checkpoints stop overwriting each other** (#871): the emulator holds
any number and the CLI wrote one file per instance, so `checkpoint A` then
`checkpoint B` made `reset A` impossible.
- **The `gofmt` pre-commit hook fails when gofmt cannot run** (#829). It read
the list of offending files and never the exit code, so any failure of gofmt
produced an empty list and the hook reported `Passed`. Its window is a fresh
clone before `mise trust`, which is a new contributor's first commit.
- **A balancer verdict is drawn from twenty-four connections, not six** (#559).
Six is a coin flipped six times: a balancer that works sent every connection
to one side about one run in thirty-two, so the dataplane suite reddened on a
subject that was behaving. The same arithmetic ran the other way after an
unlink, as a false green over a real defect.

## [0.15.0] - 2026-11-11

### Added
Expand Down
8 changes: 4 additions & 4 deletions README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Sans Homebrew, la même chose sur Linux :

```bash
( set -e
v=0.15.0
v=0.16.0
case $(uname -m) in
x86_64) a=feint-linux-amd64 ;;
aarch64) a=feint-linux-arm64 ;;
Expand Down Expand Up @@ -127,18 +127,18 @@ terraform apply # le vrai provider Scaleway
```yaml
services:
feint:
image: ghcr.io/stephrobert/feint:v0.15.0
image: ghcr.io/stephrobert/feint:v0.16.0
ports: ["4599:4599"]
```

L'image ne sert que le plan de contrôle et ne porte aucun tag `latest` ; [docs/install.md](docs/install.md) donne la forme GitLab, la forme compose et la vérification de signature. Pour la tirer directement : `docker run --rm -p 127.0.0.1:4599:4599 ghcr.io/stephrobert/feint:v0.15.0`.
L'image ne sert que le plan de contrôle et ne porte aucun tag `latest` ; [docs/install.md](docs/install.md) donne la forme GitLab, la forme compose et la vérification de signature. Pour la tirer directement : `docker run --rm -p 127.0.0.1:4599:4599 ghcr.io/stephrobert/feint:v0.16.0`.

**Dans GitHub Actions, sans conteneur**, l'action du Marketplace :

```yaml
- uses: stephrobert/setup-feint@v1
with:
version: 0.15.0
version: 0.16.0
provider: scaleway # exporte ce dont le client officiel a besoin
```

Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Without Homebrew, the same thing on Linux:

```bash
( set -e
v=0.15.0
v=0.16.0
case $(uname -m) in
x86_64) a=feint-linux-amd64 ;;
aarch64) a=feint-linux-arm64 ;;
Expand Down Expand Up @@ -116,18 +116,18 @@ terraform apply # the real Scaleway provider
```yaml
services:
feint:
image: ghcr.io/stephrobert/feint:v0.15.0
image: ghcr.io/stephrobert/feint:v0.16.0
ports: ["4599:4599"]
```

The image is control-plane only and carries no `latest` tag; [docs/install.md](docs/install.md) has the GitLab form, the compose form and the signature verification. Pull it directly with `docker run --rm -p 127.0.0.1:4599:4599 ghcr.io/stephrobert/feint:v0.15.0`.
The image is control-plane only and carries no `latest` tag; [docs/install.md](docs/install.md) has the GitLab form, the compose form and the signature verification. Pull it directly with `docker run --rm -p 127.0.0.1:4599:4599 ghcr.io/stephrobert/feint:v0.16.0`.

**In GitHub Actions without a container** — the action from the Marketplace:

```yaml
- uses: stephrobert/setup-feint@v1
with:
version: 0.15.0
version: 0.16.0
provider: scaleway # exports what the official client needs
```

Expand Down Expand Up @@ -256,7 +256,7 @@ the same 6.0.4, and says what to install, which is the point of having it.
<!-- install:start -->
<!-- Generated by `mise run docs:coverage`. Do not edit by hand. -->

Version **0.15.0**, named rather than fetched as `latest`: a mutable reference
Version **0.16.0**, named rather than fetched as `latest`: a mutable reference
downloads whatever is newest, which is a binary neither of us can name
afterwards and a release adopted the day it is published. This line moves when
the CHANGELOG does, and `feint docs --check` fails until it has.
Expand All @@ -265,7 +265,7 @@ Needs `cosign` (or `gh`, further down) and nothing else. Every file is fetched
to disk and verified before anything runs it.

```bash
base=https://github.com/stephrobert/feint/releases/download/v0.15.0
base=https://github.com/stephrobert/feint/releases/download/v0.16.0

# The published binary for this machine, or a refusal: no default, because
# the wrong architecture fails at exec with a message naming neither.
Expand Down Expand Up @@ -308,7 +308,7 @@ difference between an identity and a provenance: without it the check
accepts anything this repository attested, whichever workflow did it.

```bash
gh release download v0.15.0 --repo stephrobert/feint --pattern 'feint-linux-amd64'
gh release download v0.16.0 --repo stephrobert/feint --pattern 'feint-linux-amd64'
gh attestation verify feint-linux-amd64 --repo stephrobert/feint \
--signer-workflow stephrobert/feint/.github/workflows/release.yml
```
Expand Down
16 changes: 8 additions & 8 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feint itself needs nothing: one Go binary, no dependency, no daemon.
<!-- install:start -->
<!-- Generated by `mise run docs:coverage`. Do not edit by hand. -->

Version **0.15.0**, named rather than fetched as `latest`: a mutable reference
Version **0.16.0**, named rather than fetched as `latest`: a mutable reference
downloads whatever is newest, which is a binary neither of us can name
afterwards and a release adopted the day it is published. This line moves when
the CHANGELOG does, and `feint docs --check` fails until it has.
Expand All @@ -14,7 +14,7 @@ Needs `cosign` (or `gh`, further down) and nothing else. Every file is fetched
to disk and verified before anything runs it.

```bash
base=https://github.com/stephrobert/feint/releases/download/v0.15.0
base=https://github.com/stephrobert/feint/releases/download/v0.16.0

# The published binary for this machine, or a refusal: no default, because
# the wrong architecture fails at exec with a message naming neither.
Expand Down Expand Up @@ -57,7 +57,7 @@ difference between an identity and a provenance: without it the check
accepts anything this repository attested, whichever workflow did it.

```bash
gh release download v0.15.0 --repo stephrobert/feint --pattern 'feint-linux-amd64'
gh release download v0.16.0 --repo stephrobert/feint --pattern 'feint-linux-amd64'
gh attestation verify feint-linux-amd64 --repo stephrobert/feint \
--signer-workflow stephrobert/feint/.github/workflows/release.yml
```
Expand Down Expand Up @@ -176,7 +176,7 @@ One tag per release, the release's own, and nothing mutable: no `latest`,
for the same reason the commands above name a version.

```bash
docker run --rm -p 127.0.0.1:4599:4599 ghcr.io/stephrobert/feint:v0.15.0
docker run --rm -p 127.0.0.1:4599:4599 ghcr.io/stephrobert/feint:v0.16.0
curl http://127.0.0.1:4599/_feint/health
```

Expand All @@ -186,7 +186,7 @@ healthcheck answers, so the first step can talk to it immediately:
```yaml
services:
feint:
image: ghcr.io/stephrobert/feint:v0.15.0
image: ghcr.io/stephrobert/feint:v0.16.0
ports:
- 4599:4599
```
Expand All @@ -195,7 +195,7 @@ In GitLab CI:

```yaml
services:
- name: ghcr.io/stephrobert/feint:v0.15.0
- name: ghcr.io/stephrobert/feint:v0.16.0
alias: feint
```

Expand All @@ -210,11 +210,11 @@ just pushed, and `tools/release/preflight.sh` runs it against the previous
release before a tag exists.

```bash
cosign verify ghcr.io/stephrobert/feint:v0.15.0 \
cosign verify ghcr.io/stephrobert/feint:v0.16.0 \
--certificate-identity-regexp '^https://github\.com/stephrobert/feint/\.github/workflows/release\.yml@refs/tags/v' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com

gh attestation verify oci://ghcr.io/stephrobert/feint:v0.15.0 \
gh attestation verify oci://ghcr.io/stephrobert/feint:v0.16.0 \
--repo stephrobert/feint \
--signer-workflow stephrobert/feint/.github/workflows/release.yml
```
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ that wants real machines behind `--vm` on a host with Incus:
```yaml
- uses: stephrobert/setup-feint@v1
with:
version: 0.15.0
version: 0.16.0
provider: scaleway # exports what the official client needs
- run: terraform apply -auto-approve
```
Expand Down
2 changes: 1 addition & 1 deletion examples/compose/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

services:
feint:
image: ghcr.io/stephrobert/feint:v0.15.0
image: ghcr.io/stephrobert/feint:v0.16.0
# Published on the loopback rather than on every interface. The emulator
# accepts any credential and holds its state in memory — SECURITY.md is
# explicit that it must not be exposed to a network you do not control.
Expand Down
Loading
Loading