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
14 changes: 14 additions & 0 deletions .github/workflows/conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,20 @@ jobs:
if: matrix.client == 'terraform' || matrix.client == 'opentofu'
run: tools/conformance/stacks.sh http://127.0.0.1:4599

# The README's own four commands (#593): `feint up` on
# examples/quickstart, an empty second plan, `feint down`. The example a
# reader copies first is the one whose breakage they meet first, and it
# had no gate at all — a quickstart nobody runs is a README that rots.
#
# On the terraform and opentofu legs for the reason the stacks are, and
# on an emulator and a port of its own for the reason the environment
# suite has one: `feint up` and `feint down` are what it drives, so
# pointed at the shared address its cleanup would stop the emulator this
# job's other steps are measuring.
- name: Run the quick start the way the README prints it
if: matrix.client == 'terraform' || matrix.client == 'opentofu'
run: tools/conformance/quickstart.sh 127.0.0.1:4597

# Fault injection (#26, #356): the four real clients meeting a refusal.
#
# On `fields` because that is the only leg with scw, octl, exo and
Expand Down
47 changes: 47 additions & 0 deletions CHANGELOG.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,55 @@ change ni l'un ni l'autre a sa place dans `git log`.
passe la déclare absente 31 % du temps, là où trois passes ramènent ce risque
à 3 %, pour 295 s la passe.

- **Une matrice de capacités possède chaque phrase qui revendique un client
pour un provider, et `docs:check` relit les pages (#592).** `README.md:41`
disait « Run your Terraform against Scaleway, Outscale or Exoscale » pendant
que `docs/confidence.md` disait l'inverse et que `feint up` refusait
`iac.engine: terraform` pour ce pack depuis l'arrivée de #525 le 2026-08-26.
Tous les gates de documentation sont restés verts, et aucun n'avait tort :
ils comparent des **valeurs** qu'un artefact porte aussi, et une revendication
de capacité n'en est pas une. La matrice (`internal/cli/capability.go`) est
provider × client × mode × support × **preuve** × raison, et la colonne preuve
est ce qui en fait autre chose qu'un tableau : une ligne `supported` se résout
contre le workflow de conformance qui pilote cette paire, une ligne `refused`
contre le `VetoEngine` du pack lui-même, le code que `up` et `down`
consultent, et les deux sens sont contrôlés. La promesse du README est
générée depuis elle dans les deux langues, et chaque bloc généré des pages
d'accueil est relu : une phrase qui nomme une paire refusée ne passe que si
elle nomme aussi l'issue amont qui la changerait. Code de sortie 2, comme le
reste de la chaîne.

- **Le démarrage rapide tient en quatre commandes copiables, sur une stack assez
courte pour se lire d'un trait (#593).** Il enseignait la séquence de 0.10,
sans répertoire, sans `main.tf` et sans bloc provider, sous un
`Apply complete! Resources: 5 added` qu'on ne pouvait pas atteindre depuis
elle. `examples/quickstart/scaleway` et `examples/quickstart/outscale` sont
le premier exemple : un provider, une adresse, une machine, 34 et 43 lignes
de Terraform, `runtime: mode: off`. `examples/stacks/` ne bouge pas et garde
son rôle : c'est la stack de qualification qui a trouvé #249 et #250, et on
lui demandait d'être aussi une première lecture. La ligne d'apply est dérivée
de la configuration, et `tools/conformance/quickstart.sh` la sort du README
pour exiger que l'exécution l'imprime. La suite joue `feint up`, un second
plan vide et `feint down` sur les deux exemples, et le répertoire quickstart
rejoint la population que `feint docs --check` juge déjà : appliqué par la CI
ou déclaré avec sa raison, et épinglant le provider qui a répondu.

- **Les blocs générés qui portent de la prose sont rendus par langue (#591).**
Le README français ouvrait sur un démarrage rapide en anglais, parce que le
générateur injectait le même bloc dans les deux pages au motif qu'une commande
ne se traduit pas. Vrai des commandes, et elles restent partagées avec la
version, l'image et le dépôt ; les phrases autour d'elles s'écrivent deux
fois.

### Corrigé

- **Une seule exécution de `feint docs` qui changeait deux sections d'un README
n'en gardait qu'une.** La cible était écrite depuis une copie découpée en
début d'exécution, après les fonctions qui relisent le même fichier : elle
remettait leurs sections en place et annonçait un succès, et le seul symptôme
était un `docs --check` toujours rouge après une régénération qui venait de
dire `README.md updated`. Mesuré en ajoutant le bloc de promesse.

- **Un serveur créé avec son IP publique ne garde plus une interface non
filtrée à côté de sa filtrée (#548).** Créé *avec* un `ip_id`, un serveur
Scaleway démarre en ne portant que cette adresse : le pilote lui donne donc
Expand Down
49 changes: 49 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,57 @@ what this project is judged on: **a response shape a client can observe**, and
is stated there too: a stack whose `project_name` is not `default` fails on
the provider's own `FindExact`.

- **A capability matrix owns every sentence that claims a client for a
provider, and `docs:check` reads the pages back (#592).** `README.md:41` said
*"Run your Terraform against Scaleway, Outscale or Exoscale"* while
`docs/confidence.md` said the opposite and `feint up` had been refusing
`iac.engine: terraform` for that pack since #525 landed on 2026-08-26. Every
doc gate stayed green throughout, and none of them was wrong: they compare
**values** an artefact also holds, and a claim about capability is not one.
The matrix (`internal/cli/capability.go`) is provider × client × mode ×
support × **proof** × reason, and the proof column is what makes it more than
a table: a `supported` row is resolved against the conformance workflow that
drives that pair, a `refused` row against the pack's own `VetoEngine` — the
code `up` and `down` consult — and both directions are checked, so a veto
nobody wrote down and a row nothing proves are equally refused. The README's
promise is generated from it in both languages, and every generated block of
the front pages is read back: a sentence naming a refused pair passes only if
it also names the upstream issue that would change it. Exit code 2, like the
rest of the chain.

- **The Quick Start is four commands a reader can copy, on a stack short enough
to read whole (#593).** It taught the 0.10 sequence — `feint start`, `eval
"$(feint env scaleway)"`, `terraform apply` — with no directory, no `main.tf`
and no provider block, under an `Apply complete! Resources: 5 added` that was
not reachable from it. `examples/quickstart/scaleway` and
`examples/quickstart/outscale` are the first example now: a provider, an
address, one machine, 34 and 43 lines of Terraform, `runtime: mode: off`.
`examples/stacks/` is unchanged and keeps its job — it is the qualification
stack that found #249 and #250, and it was being asked to be a first read as
well. The apply line is derived from the configuration and
`tools/conformance/quickstart.sh` lifts it out of the README and requires the
run to print it, so the output shown is the output produced. The suite runs
`feint up`, an empty second plan and `feint down` on both examples, on the
terraform and opentofu legs, and the quickstart directory joins the population
`feint docs --check` already judges: applied by CI or declared with a reason,
and pinning the provider that answered.

- **The generated blocks that carry prose are rendered per locale (#591).** The
French README opened with an English Quick Start — "On your machine", "In CI,
or anywhere Docker runs" — because the generator injected one block into both
pages on the rule that a command needs no translation. True of the commands,
and they are still shared along with the version, the image and the
repository; the sentences around them are written twice.

### Fixed

- **A single `feint docs` run that changed two sections of a README kept only
one of them.** The target was written from a copy spliced at the top of the
run, after the helpers that re-read the same file and splice into what they
find — so it put their sections back and reported success, and the only
symptom was `docs --check` still red after a regeneration that had said
`README.md updated`. Measured while adding the promise block.

- **A server created with its public IP no longer keeps an unfiltered
interface beside its filtered one (#548).** Created *with* an `ip_id`, a
Scaleway server boots carrying only that address, so the driver gives it a
Expand Down
78 changes: 55 additions & 23 deletions README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,64 @@
> [!NOTE]
> L'anglais est la source. Cette page est une traduction, et elle peut avoir
> pris du retard : en cas de désaccord entre les deux, [la version
> anglaise](README.md) fait foi. Les blocs générés par `feint docs` — la version
> publiée, le tableau des prérequis, les commandes d'installation, les versions
> des clients, les tableaux de couverture — restent en anglais dans les deux
> pages, parce qu'ils sont rendus depuis le code plutôt qu'écrits à la main.
> anglaise](README.md) fait foi.
>
> Les blocs générés par `feint docs` sont rendus depuis le code, jamais écrits à
> la main. Ceux qui portent de la prose sont rendus par langue : l'encadré
> ci-dessus, la promesse et le démarrage rapide. Ceux qui ne sont qu'un tableau
> de valeurs (versions publiées, prérequis, commandes d'installation, versions
> des clients, tableaux de couverture) restent en anglais dans les deux pages,
> parce qu'une commande ne se traduit pas.

## Démarrage rapide

**Testez votre Terraform sur Scaleway, Outscale ou Exoscale sans compte cloud,
sans identifiants, et sans créer la moindre ressource.**
<!-- promise:start -->
<!-- Generated by `mise run docs:coverage`. Do not edit by hand. -->

**Pointez Terraform et les CLI officielles vers votre propre machine.**
Aucun compte cloud, aucun identifiant, et rien de créé nulle part.

Terraform et OpenTofu pilotent Scaleway et Outscale. Chaque pack a en plus
son CLI officiel, et chacun d'eux pilote cet émulateur de bout en bout :

- **Scaleway** avec `scw`.
- **Outscale** avec `octl`.
- **Exoscale** avec `exo`. Terraform et OpenTofu reviennent le jour où une
version publiée porte le correctif de
exoscale/terraform-provider-exoscale#573, que `feint up` refuse au
portillon jusque-là.
<!-- promise:end -->

<!-- quickstart:start -->
<!-- Generated by `mise run docs:coverage`. Do not edit by hand. -->

**On your machine** — one static binary, nothing else:
**Sur votre machine**, un binaire statique et une stack assez courte pour se lire :

```bash
feint start # detaches, waits until it answers
eval "$(feint env scaleway)" # point the official client at it
terraform apply # the real Scaleway provider
brew install stephrobert/feint/feint
git clone https://github.com/stephrobert/feint
cd feint/examples/quickstart/scaleway
feint up # vérifie la station, démarre l'émulateur, applique le Terraform
feint down # détruit ce qu'il a créé, puis arrête l'émulateur
```

**In CI, or anywhere Docker runs** — the same emulator as a service:
Ce que ces quatre commandes impriment :

```text
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
```

`feint up` lit le `feint.yaml` posé à côté du Terraform : il vérifie la station, démarre l'émulateur, exporte ce dont le client officiel a besoin, lance le moteur et attend les conditions que le fichier déclare. La stack fait un serveur et une adresse, sans runtime de machines. `examples/stacks/scaleway` est celle qui ressemble à de la production, et elle est faite pour casser l'émulateur plutôt que pour se lire en premier.

**Vous avez déjà un projet ?** Pilotez-le à la main, c'est ce qu'un `feint.yaml` vous évite d'écrire :

```bash
feint start # se détache, attend qu'il réponde
eval "$(feint env scaleway)" # pointe le client officiel vers lui
terraform apply # le vrai provider Scaleway
```

**En CI, ou partout où Docker tourne**, le même émulateur en service :

```yaml
services:
Expand All @@ -68,35 +104,31 @@ services:
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.11.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.11.0`.

**In GitHub Actions without a container** — the action from the Marketplace:
**Dans GitHub Actions, sans conteneur**, l'action du Marketplace :

```yaml
- uses: stephrobert/setup-feint@v1
with:
version: 0.11.0
provider: scaleway # exports what the official client needs
provider: scaleway # exporte ce dont le client officiel a besoin
```

It installs the released binary, **verifies its checksum before running it**, and waits until the emulator answers.
Elle installe le binaire publié, **vérifie sa somme de contrôle avant de l'exécuter**, et attend que l'émulateur réponde.
<!-- quickstart:end -->

```text
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
```

![Un job GitHub Actions qui tire l'image, y pointe le CLI Scaleway officiel et applique du Terraform — sans compte cloud et sans secret](docs/assets/ci.gif)

Des pipelines à copier tels quels, sans rien à configurer ni aucun secret à
ajouter : [**examples/**](examples/) — GitHub Actions, GitLab CI, et une action
`setup-feint` qui vérifie l'empreinte du binaire avant de l'exécuter.

Pas de compte. Pas d'identifiants. Rien de facturé, et rien qui continue de
tourner ailleurs que sur votre machine. Le provider qui a produit cette ligne est
le vrai, celui du registre : tout l'argument de ce dépôt est qu'il ne peut pas
voir la différence, et [ce que vous pouvez valider](docs/confidence.md) dit où
cette affirmation s'arrête.
tourner ailleurs que sur votre machine. Le provider qui a imprimé la ligne
`Apply complete!` ci-dessus est le vrai, celui du registre : tout l'argument de
ce dépôt est qu'il ne peut pas voir la différence, et [ce que vous pouvez
valider](docs/confidence.md) dit où cette affirmation s'arrête.

![Démarrage de Feint, le CLI Scaleway officiel pointé dessus, et un terraform apply exécuté contre lui](docs/assets/quickstart.gif)

Expand Down
50 changes: 39 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,45 @@

## Quick start

**Run your Terraform against Scaleway, Outscale or Exoscale without a cloud
account, without credentials, and without creating a single resource.**
<!-- promise:start -->
<!-- Generated by `mise run docs:coverage`. Do not edit by hand. -->

**Point Terraform and the official cloud CLIs at your own machine.**
No cloud account, no credentials, and nothing created anywhere.

Terraform and OpenTofu drive Scaleway and Outscale. Each pack also has its
own official CLI, and every one of them drives this emulator end to end:

- **Scaleway** with `scw`.
- **Outscale** with `octl`.
- **Exoscale** with `exo`. Terraform and OpenTofu join it the day a
published release carries the fix for
exoscale/terraform-provider-exoscale#573, which `feint up` refuses at the
doorstep until one does.
<!-- promise:end -->

<!-- quickstart:start -->
<!-- Generated by `mise run docs:coverage`. Do not edit by hand. -->

**On your machine** — one static binary, nothing else:
**On your machine** — one static binary, and a stack short enough to read whole:

```bash
brew install stephrobert/feint/feint
git clone https://github.com/stephrobert/feint
cd feint/examples/quickstart/scaleway
feint up # checks the host, starts the emulator, applies the Terraform
feint down # destroys what it created, then stops the emulator
```

What those four commands print:

```text
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
```

`feint up` reads the `feint.yaml` beside the Terraform: it checks the host, starts the emulator, exports what the official client needs, runs the engine and waits on the conditions the file declares. The stack is one server and one address, with no machine runtime — `examples/stacks/scaleway` is the one shaped like production, and it exists to break the emulator rather than to be read first.

**Already have a project?** Drive it by hand, which is what a `feint.yaml` saves you from writing:

```bash
feint start # detaches, waits until it answers
Expand Down Expand Up @@ -75,21 +107,17 @@ The image is control-plane only and carries no `latest` tag; [docs/install.md](d
It installs the released binary, **verifies its checksum before running it**, and waits until the emulator answers.
<!-- quickstart:end -->

```text
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.
```

![A GitHub Actions job pulling the image, pointing the official Scaleway CLI at it, and applying Terraform — no cloud account and no secret](docs/assets/ci.gif)

Copy-paste pipelines for both, with nothing to configure and no secret to add:
[**examples/**](examples/) — GitHub Actions, GitLab CI, and a `setup-feint`
action that verifies the binary before it runs it.

No account. No credentials. Nothing billed, and nothing left running anywhere but
your machine. The provider that produced that line is the real one from the
registry — this repository's whole argument is that it cannot tell the
difference, and [what you can validate](docs/confidence.md) says where that
claim stops.
your machine. The provider that printed the `Apply complete!` line above is the
real one from the registry — this repository's whole argument is that it cannot
tell the difference, and [what you can validate](docs/confidence.md) says where
that claim stops.

![Starting Feint, pointing the official Scaleway CLI at it, and applying a Terraform configuration against it](docs/assets/quickstart.gif)

Expand Down
Loading
Loading