diff --git a/CHANGELOG.md b/CHANGELOG.md index 4da179f..8138d58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ ## Unreleased -- `rootform run form.json` and comparisons of saved Forms draw provider icons and labels when the Form records the Dialects shipped with the same executable. - -## 0.2.0-rc.1 +## 0.2.0 - Analyze Terraform and OpenTofu plan and state exports into portable Forms, with stage architectures, comparisons, drift, evidence and explicit limits. - Explore architectures locally or through a self-contained HTML export. + A reopened Form keeps provider icons and labels when it records the + Dialects embedded in the same release. - Explain instances, Rules and recorded Policy outcomes with their provenance. - Evaluate selected Policies and Policy Packs against plans, states or comparison Forms, with text, Markdown, JSON and SARIF reports. diff --git a/distribution/installers/README.md b/distribution/installers/README.md index 68de691..0c96919 100644 --- a/distribution/installers/README.md +++ b/distribution/installers/README.md @@ -12,7 +12,7 @@ Generate from exact final release assets: ```sh bun scripts/generate-installation.ts \ - --version 0.2.0-rc.1 \ + --version 0.2.0 \ --release build/release \ --output build/installers ``` diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index 11681c7..38cac5d 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -67,8 +67,8 @@ from. ```sh mkdir rootform-quickstart && cd rootform-quickstart -curl -fsSLO https://raw.githubusercontent.com/rootform-dev/rootform/v0.2.0-rc.1/examples/playground/commerce-platform/head/plan.json -curl -fsSLO https://raw.githubusercontent.com/rootform-dev/rootform/v0.2.0-rc.1/examples/playground/commerce-platform/head/plan.tfplan +curl -fsSLO https://raw.githubusercontent.com/rootform-dev/rootform/v0.2.0/examples/playground/commerce-platform/head/plan.json +curl -fsSLO https://raw.githubusercontent.com/rootform-dev/rootform/v0.2.0/examples/playground/commerce-platform/head/plan.tfplan ``` Analyze the plan. Rootform reads both files, prints a summary, and opens the diff --git a/docs/installation.md b/docs/installation.md index 2890b07..b401c35 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -61,26 +61,18 @@ Invoke-RestMethod https://rootform.dev/install.ps1 | Invoke-Expression rootform version ``` -**Other options** - -WinGet: - -```shell -winget install --id Rootform.Rootform --exact -``` - **Recommended** ```sh -docker pull ghcr.io/rootform-dev/rootform:0.2.0-rc.1 +docker pull ghcr.io/rootform-dev/rootform:0.2.0 ``` **Verify** ```sh -docker run --rm ghcr.io/rootform-dev/rootform:0.2.0-rc.1 rootform version +docker run --rm ghcr.io/rootform-dev/rootform:0.2.0 rootform version ``` [Container usage](integrations/oci-image.md) @@ -99,25 +91,25 @@ provide a product installation. | Platform | Archive | | --- | --- | -| macOS, Apple silicon | `rootform_0.2.0-rc.1_darwin_arm64.tar.gz` | -| macOS, Intel | `rootform_0.2.0-rc.1_darwin_amd64.tar.gz` | -| Linux, x86-64 | `rootform_0.2.0-rc.1_linux_amd64.tar.gz` | -| Linux, ARM64 | `rootform_0.2.0-rc.1_linux_arm64.tar.gz` | -| Windows, x86-64 | `rootform_0.2.0-rc.1_windows_amd64.zip` | +| macOS, Apple silicon | `rootform_0.2.0_darwin_arm64.tar.gz` | +| macOS, Intel | `rootform_0.2.0_darwin_amd64.tar.gz` | +| Linux, x86-64 | `rootform_0.2.0_linux_amd64.tar.gz` | +| Linux, ARM64 | `rootform_0.2.0_linux_arm64.tar.gz` | +| Windows, x86-64 | `rootform_0.2.0_windows_amd64.zip` | Release archives include binary license and third-party notices. Match the archive against its `SHA256SUMS` entry before extraction: ```sh title="macOS" -shasum -a 256 rootform_0.2.0-rc.1_darwin_arm64.tar.gz +shasum -a 256 rootform_0.2.0_darwin_arm64.tar.gz ``` ```sh title="Linux" -sha256sum rootform_0.2.0-rc.1_linux_amd64.tar.gz +sha256sum rootform_0.2.0_linux_amd64.tar.gz ``` ```powershell title="Windows" -Get-FileHash .\rootform_0.2.0-rc.1_windows_amd64.zip -Algorithm SHA256 +Get-FileHash .\rootform_0.2.0_windows_amd64.zip -Algorithm SHA256 ``` Extract the `.tar.gz` or `.zip`, then place `rootform` or `rootform.exe` in @@ -133,22 +125,20 @@ described in [Locks and vendored content](offline-security.md). Update package-managed installations with their package managers: - Homebrew: `brew upgrade rootform-dev/tap/rootform` -- WinGet: `winget upgrade --id Rootform.Rootform --exact` - Shell installer: run the recommended install command again. It replaces the executable in the same installation directory. - Manual archive: replace the executable with the binary from the new release, then verify it with `rootform version`. - Container: pull the selected release tag again, for example - `docker pull ghcr.io/rootform-dev/rootform:0.2.0-rc.1`. + `docker pull ghcr.io/rootform-dev/rootform:0.2.0`. Remove package-managed installations with: - Homebrew: `brew uninstall rootform-dev/tap/rootform` -- WinGet: `winget uninstall --id Rootform.Rootform --exact` - Shell installer: remove `~/.local/bin/rootform` by default, or the file under the custom `ROOTFORM_INSTALL_DIR`. - Manual archive: remove `rootform` or `rootform.exe` from its `PATH` directory. -- Container: `docker image rm ghcr.io/rootform-dev/rootform:0.2.0-rc.1`. +- Container: `docker image rm ghcr.io/rootform-dev/rootform:0.2.0`. These steps remove the executable or image. `rootform uninstall` removes installed Dialects or Policy Packs from `$ROOTFORM_HOME`; it does not remove diff --git a/docs/integrations/ci/README.md b/docs/integrations/ci/README.md index da40ffe..5b14c0c 100644 --- a/docs/integrations/ci/README.md +++ b/docs/integrations/ci/README.md @@ -210,7 +210,7 @@ Keep the workflow, these scripts, `ROOTFORM_MODE=check`, and the selected Dialec ## Script settings -The portable script reads these settings from the environment. The installation wrapper also accepts `ROOTFORM_VERSION` (default `0.1.0`) and `ROOTFORM_HOME` for a prepared home; these are wrapper settings, not CLI flags. +The portable script reads these settings from the environment. The installation wrapper also accepts `ROOTFORM_VERSION` (default `0.2.0`) and `ROOTFORM_HOME` for a prepared home; these are wrapper settings, not CLI flags. | Variable | Default | Use | | --- | --- | --- | diff --git a/docs/integrations/ci/azure-pipelines.yml b/docs/integrations/ci/azure-pipelines.yml index 3c4083a..61f9a69 100644 --- a/docs/integrations/ci/azure-pipelines.yml +++ b/docs/integrations/ci/azure-pipelines.yml @@ -15,7 +15,7 @@ steps: terraform -chdir=infra init -input=false terraform -chdir=infra plan -input=false -out="$plan_dir/plan.tfplan" terraform -chdir=infra show -json "$plan_dir/plan.tfplan" > "$plan_dir/plan.json" - ROOTFORM_MODE=check ROOTFORM_VERSION=0.1.0 ROOTFORM_PROJECT=./infra ROOTFORM_INPUT="$plan_dir/plan.json" ROOTFORM_PLAN_FILE="$plan_dir/plan.tfplan" ROOTFORM_OUTPUT_DIR="$(Build.SourcesDirectory)/.rootform-ci-$(Build.BuildId)" sh ./ci/generic-ci.sh + ROOTFORM_MODE=check ROOTFORM_VERSION=0.2.0 ROOTFORM_PROJECT=./infra ROOTFORM_INPUT="$plan_dir/plan.json" ROOTFORM_PLAN_FILE="$plan_dir/plan.tfplan" ROOTFORM_OUTPUT_DIR="$(Build.SourcesDirectory)/.rootform-ci-$(Build.BuildId)" sh ./ci/generic-ci.sh displayName: Review architecture and check Policies - publish: $(Build.SourcesDirectory)/.rootform-ci-$(Build.BuildId) artifact: rootform-results diff --git a/docs/integrations/ci/generic-ci.sh b/docs/integrations/ci/generic-ci.sh index 1954c79..cc9b664 100755 --- a/docs/integrations/ci/generic-ci.sh +++ b/docs/integrations/ci/generic-ci.sh @@ -6,7 +6,7 @@ set -eu umask 077 tools=$(mktemp -d "${TMPDIR:-/tmp}/rootform-ci-tools.XXXXXXXX") trap 'rm -rf -- "$tools"' 0 -version=${ROOTFORM_VERSION:-0.1.0} +version=${ROOTFORM_VERSION:-0.2.0} if [ -z "${ROOTFORM_BIN:-}" ]; then curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \ https://rootform.dev/install > "$tools/install.sh" diff --git a/docs/integrations/ci/github-actions-plan.yml b/docs/integrations/ci/github-actions-plan.yml index 20c4229..c903071 100644 --- a/docs/integrations/ci/github-actions-plan.yml +++ b/docs/integrations/ci/github-actions-plan.yml @@ -29,7 +29,7 @@ jobs: - uses: rootform-dev/action@17cf0cbefae80ac388b54e0329dd40f3cd0f659e # v1 id: rootform with: - version: 0.1.0 + version: 0.2.0 project: ./infra input: ${{ runner.temp }}/plan.json plan-file: ${{ runner.temp }}/plan.tfplan diff --git a/docs/integrations/ci/gitlab-ci.yml b/docs/integrations/ci/gitlab-ci.yml index 1e52d29..d83c2f7 100644 --- a/docs/integrations/ci/gitlab-ci.yml +++ b/docs/integrations/ci/gitlab-ci.yml @@ -4,7 +4,7 @@ rootform-review: stage: review variables: ROOTFORM_MODE: check - ROOTFORM_VERSION: 0.1.0 + ROOTFORM_VERSION: 0.2.0 ROOTFORM_PROJECT: ./infra ROOTFORM_OUTPUT_DIR: .rootform-ci-${CI_JOB_ID} script: diff --git a/docs/integrations/github-actions.md b/docs/integrations/github-actions.md index a05b5d4..3e7965c 100644 --- a/docs/integrations/github-actions.md +++ b/docs/integrations/github-actions.md @@ -42,7 +42,7 @@ state JSON or a saved Form. Choose an exact [published Rootform version](https:/ - uses: rootform-dev/action@v1 id: rootform with: - version: 0.2.0-rc.1 + version: 0.2.0 input: ${{ runner.temp }}/plan.json plan-file: ${{ runner.temp }}/plan.tfplan ``` @@ -74,7 +74,7 @@ To compare two revisions, give the same Action both operands instead of - uses: rootform-dev/action@v1 id: rootform with: - version: 0.2.0-rc.1 + version: 0.2.0 before: before/form.json after: after/form.json policy-pack: ./policies @@ -142,7 +142,7 @@ jobs: # Checkout and export your input before this step. - uses: rootform-dev/action@v1 with: - version: 0.2.0-rc.1 + version: 0.2.0 input: ${{ runner.temp }}/plan.json plan-file: ${{ runner.temp }}/plan.tfplan comment: true diff --git a/docs/integrations/github-actions/action.md b/docs/integrations/github-actions/action.md index df924d9..84b2265 100644 --- a/docs/integrations/github-actions/action.md +++ b/docs/integrations/github-actions/action.md @@ -20,7 +20,7 @@ reinterpreting the original exports. - uses: rootform-dev/action@v1 id: rootform with: - version: 0.2.0-rc.1 + version: 0.2.0 input: ${{ runner.temp }}/plan.json plan-file: ${{ runner.temp }}/plan.tfplan policy-pack: ./policies/team diff --git a/docs/integrations/github-actions/analyze.md b/docs/integrations/github-actions/analyze.md index 17bdef0..cb43f09 100644 --- a/docs/integrations/github-actions/analyze.md +++ b/docs/integrations/github-actions/analyze.md @@ -11,7 +11,7 @@ and prepares required project content itself; setup and init are optional. - uses: rootform-dev/action/analyze@v1 id: analysis with: - version: 0.2.0-rc.1 + version: 0.2.0 input: ${{ runner.temp }}/plan.json plan-file: ${{ runner.temp }}/plan.tfplan ``` diff --git a/docs/integrations/github-actions/check.md b/docs/integrations/github-actions/check.md index 3169de9..a9bce99 100644 --- a/docs/integrations/github-actions/check.md +++ b/docs/integrations/github-actions/check.md @@ -11,7 +11,7 @@ required. A supplied Form or Comparison Form is reused without new analysis. - uses: rootform-dev/action/check@v1 id: checks with: - version: 0.2.0-rc.1 + version: 0.2.0 input: ${{ runner.temp }}/plan.json plan-file: ${{ runner.temp }}/plan.tfplan policy-pack: ./policies/team diff --git a/docs/integrations/github-actions/compare.md b/docs/integrations/github-actions/compare.md index f3553f9..8fc87a9 100644 --- a/docs/integrations/github-actions/compare.md +++ b/docs/integrations/github-actions/compare.md @@ -11,7 +11,7 @@ setup, init and separate analyze steps are optional. - uses: rootform-dev/action/compare@v1 id: comparison with: - version: 0.2.0-rc.1 + version: 0.2.0 before: ${{ runner.temp }}/before/plan.json before-plan-file: ${{ runner.temp }}/before/plan.tfplan after: ${{ runner.temp }}/after/plan.json diff --git a/docs/integrations/github-actions/init.md b/docs/integrations/github-actions/init.md index e4e730a..e8c9c8e 100644 --- a/docs/integrations/github-actions/init.md +++ b/docs/integrations/github-actions/init.md @@ -11,7 +11,7 @@ when needed, so a separate init step is optional. ```yaml title="Prepare an existing locked project" - uses: rootform-dev/action/init@v1 with: - version: 0.2.0-rc.1 + version: 0.2.0 project: ./infra locked: true ``` diff --git a/docs/integrations/github-actions/setup.md b/docs/integrations/github-actions/setup.md index 0dbe909..2ebc80d 100644 --- a/docs/integrations/github-actions/setup.md +++ b/docs/integrations/github-actions/setup.md @@ -11,7 +11,7 @@ is optional. - uses: rootform-dev/action/setup@v1 id: installation with: - version: 0.2.0-rc.1 + version: 0.2.0 - run: rootform version ``` diff --git a/docs/integrations/oci-image.md b/docs/integrations/oci-image.md index 9ce8c5d..b6a68cd 100644 --- a/docs/integrations/oci-image.md +++ b/docs/integrations/oci-image.md @@ -3,12 +3,12 @@ title: "Container image" description: "Analyze a mounted plan or state export with the official Rootform image, persistent package storage, and offline inputs." --- -The official image runs the same `rootform` CLI as the release archive for `linux/amd64` and `linux/arm64`. It contains the embedded RF Vocabulary and Dialects, not Terraform, OpenTofu, providers, project source, or external selections. Produce a plan or state JSON before starting the container; OpenTofu users replace `terraform` with `tofu` in those commands, and [Plan inputs](../inputs/plans.md#produce-the-accepted-json) gives the procedure. Use an exact version tag such as `ghcr.io/rootform-dev/rootform:0.2.0-rc.1` or the reviewed multi-platform index digest; there is no moving `latest` tag. The image has no entrypoint, so put `rootform` after the image reference in every command. +The official image runs the same `rootform` CLI as the release archive for `linux/amd64` and `linux/arm64`. It contains the embedded RF Vocabulary and Dialects, not Terraform, OpenTofu, providers, project source, or external selections. Produce a plan or state JSON before starting the container; OpenTofu users replace `terraform` with `tofu` in those commands, and [Plan inputs](../inputs/plans.md#produce-the-accepted-json) gives the procedure. Use an exact version tag such as `ghcr.io/rootform-dev/rootform:0.2.0` or the reviewed multi-platform index digest; there is no moving `latest` tag. The image has no entrypoint, so put `rootform` after the image reference in every command. ## Check the CLI version ```sh -docker run --rm ghcr.io/rootform-dev/rootform:0.2.0-rc.1 rootform version +docker run --rm ghcr.io/rootform-dev/rootform:0.2.0 rootform version ``` The version must match the release your team reviewed. To pin exact image bytes, replace the version tag with `ghcr.io/rootform-dev/rootform@sha256:`, using the complete reviewed index digest. For disconnected use, the image must already be present locally. @@ -21,7 +21,7 @@ Mount the directory containing `plan.json` read-only and run a one-shot analysis docker run --rm \ --volume "$PWD:/workspace:ro" \ --workdir /workspace \ - ghcr.io/rootform-dev/rootform:0.2.0-rc.1 \ + ghcr.io/rootform-dev/rootform:0.2.0 \ rootform run plan.json --no-serve ``` @@ -33,7 +33,7 @@ To keep the Form while the project mount stays read-only, ask for JSON on standa docker run --rm \ --volume "$PWD:/workspace:ro" \ --workdir /workspace \ - ghcr.io/rootform-dev/rootform:0.2.0-rc.1 \ + ghcr.io/rootform-dev/rootform:0.2.0 \ rootform run plan.json --no-serve --format json > analysis.json ``` @@ -51,7 +51,7 @@ docker run --rm \ --volume "$PWD:/workspace:ro" \ --volume "$PWD/reports:/reports" \ --workdir /workspace \ - ghcr.io/rootform-dev/rootform:0.2.0-rc.1 \ + ghcr.io/rootform-dev/rootform:0.2.0 \ rootform run plan.json --no-serve \ -o /reports/analysis.json -o /reports/report.md -o /reports/explorer.html ``` @@ -68,7 +68,7 @@ docker run --rm \ --volume "$PWD:/workspace:ro" \ --volume rootform-home:/home/rootform/.rootform \ --workdir /workspace \ - ghcr.io/rootform-dev/rootform:0.2.0-rc.1 \ + ghcr.io/rootform-dev/rootform:0.2.0 \ rootform init . --locked --no-input ``` @@ -91,7 +91,7 @@ docker run --rm \ --tmpfs /home/rootform/.rootform:uid=65532,gid=65532,mode=0700 \ --volume "$PWD:/workspace:ro" \ --workdir /workspace \ - ghcr.io/rootform-dev/rootform:0.2.0-rc.1 \ + ghcr.io/rootform-dev/rootform:0.2.0 \ rootform run plan.json --locked --no-serve --format json > analysis.json ``` @@ -108,7 +108,7 @@ docker run --rm \ --volume rootform-home:/home/rootform/.rootform \ --env DOCKER_CONFIG=/run/docker-config \ --workdir /workspace \ - ghcr.io/rootform-dev/rootform:0.2.0-rc.1 \ + ghcr.io/rootform-dev/rootform:0.2.0 \ rootform init . --locked --no-input ``` diff --git a/docs/integrations/vscode.md b/docs/integrations/vscode.md index 7af68f9..0c9bcfa 100644 --- a/docs/integrations/vscode.md +++ b/docs/integrations/vscode.md @@ -11,8 +11,20 @@ help you navigate a Dialect or Policy Pack. ## Start editing 1. [Install Rootform](../installation.md) and confirm `rootform version` works. -2. Open **Extensions**, search for **Rootform**, and install the extension - published by **rootform-dev**. Use VS Code 1.133 or later. +2. Package the extension from the + [editors repository](https://github.com/rootform-dev/editors). Packaging + needs Bun and Node.js 22 or later with npm: + + ```sh + git clone https://github.com/rootform-dev/editors.git + cd editors + bun install --frozen-lockfile + cd vscode + bun run package + ``` + + In VS Code 1.133 or later, run **Extensions: Install from VSIX...** and + select the `.vsix` file created in `vscode`. 3. Open a trusted workspace containing `.rf.hcl` files. Select the **Rootform** language mode if VS Code has not selected it automatically. diff --git a/docs/integrations/zed.md b/docs/integrations/zed.md index 9513474..0928af5 100644 --- a/docs/integrations/zed.md +++ b/docs/integrations/zed.md @@ -10,7 +10,9 @@ indentation and folding keep Dialects and Policy Packs readable. ## Start editing 1. [Install Rootform](../installation.md) and confirm `rootform version` works. -2. Open **Extensions**, search for **Rootform**, and install it. +2. Clone the [editors repository](https://github.com/rootform-dev/editors). + In Zed, run **zed: install dev extension** and select its `zed` directory. + Zed builds the extension with Rust installed through rustup. 3. Open a `.rf.hcl` file. The extension finds `rootform` on the workspace `PATH` and starts the language diff --git a/examples/playground/forms/commerce-platform-analysis.json b/examples/playground/forms/commerce-platform-analysis.json index 3eb5da4..09a8ad7 100644 --- a/examples/playground/forms/commerce-platform-analysis.json +++ b/examples/playground/forms/commerce-platform-analysis.json @@ -3,7 +3,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", diff --git a/examples/playground/forms/commerce-platform-comparison.json b/examples/playground/forms/commerce-platform-comparison.json index 0414681..3a98b4a 100644 --- a/examples/playground/forms/commerce-platform-comparison.json +++ b/examples/playground/forms/commerce-platform-comparison.json @@ -3,7 +3,7 @@ "kind": "comparison", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "before": { "form": { @@ -11,7 +11,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", @@ -80959,7 +80959,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", diff --git a/examples/playground/forms/event-driven-platform-analysis.json b/examples/playground/forms/event-driven-platform-analysis.json index 655831b..ad69a5f 100644 --- a/examples/playground/forms/event-driven-platform-analysis.json +++ b/examples/playground/forms/event-driven-platform-analysis.json @@ -3,7 +3,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", diff --git a/examples/playground/forms/event-driven-platform-comparison.json b/examples/playground/forms/event-driven-platform-comparison.json index 1e5c60f..67f09fc 100644 --- a/examples/playground/forms/event-driven-platform-comparison.json +++ b/examples/playground/forms/event-driven-platform-comparison.json @@ -3,7 +3,7 @@ "kind": "comparison", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "before": { "form": { @@ -11,7 +11,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", @@ -68952,7 +68952,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", diff --git a/examples/playground/forms/interactive.json b/examples/playground/forms/interactive.json index 4bde8b7..16ff281 100644 --- a/examples/playground/forms/interactive.json +++ b/examples/playground/forms/interactive.json @@ -1,14 +1,14 @@ { "format_version": "1", "files": { - "commerce-platform-analysis.json": "40010b680ace3e54291ea46e6f8cf04748899f8d6f00287dd8481e50ead41b69", - "commerce-platform-comparison.json": "801360dc0077a1a768322ac07ffd3be45f50d10c964c3dd536c96f755281a625", + "commerce-platform-analysis.json": "6be055b0c3404a9e6c1f27d34c7fb955c6363f531c59009a14c823560b788d2a", + "commerce-platform-comparison.json": "61dceae0f178f9d2d3c0ff8b472c09717ba465889c4d7bb94811e8beaf420b27", "commerce-platform-presentation.json": "1673648dc5553d27d814689266a88515f2262e1713f01c294470316f9cb49f49", - "event-driven-platform-analysis.json": "627cbb1b2fb8797720e8fe7cba1875490c4f6ca031745186b5660e4f65ce97a0", - "event-driven-platform-comparison.json": "b3e55735f44b20f337cb95815a696efea9a9475f8fc8d50a9b667131da60bbaa", + "event-driven-platform-analysis.json": "617e537a0bd34c9b60d2121a68ec7af947aab9716ddac6e7269c8407190c0639", + "event-driven-platform-comparison.json": "8916f3c1a5ea66f8e236995b66cdccd08e436d73ad472107f09418a57ac04bcb", "event-driven-platform-presentation.json": "1673648dc5553d27d814689266a88515f2262e1713f01c294470316f9cb49f49", - "shared-data-platform-analysis.json": "a163e64765fab08577bdf512ff557308993e1785166e84242abcd49f4371877e", - "shared-data-platform-comparison.json": "8c9235e37df707403baab2aaced3a37f9e3b870a29e8e5ccb7124e353cfa1bbe", + "shared-data-platform-analysis.json": "7319299f19d3ee5032da81a2e56467c2630f7d8125c3ad2e3fbbb1875945f643", + "shared-data-platform-comparison.json": "5249891a5ba1dae4cc01759f9bb5d6d59538dedcc36a6e227da212eddb952b19", "shared-data-platform-presentation.json": "1673648dc5553d27d814689266a88515f2262e1713f01c294470316f9cb49f49" } } diff --git a/examples/playground/forms/manifest.json b/examples/playground/forms/manifest.json index e738e6d..c1599a3 100644 --- a/examples/playground/forms/manifest.json +++ b/examples/playground/forms/manifest.json @@ -1,8 +1,8 @@ { "format_version": "1", "binary": { - "version": "rootform 0.2.0-rc.1", - "sha256": "055c5546d69180c9bec1fb0ed6ca04acf25a9891cd89cfbc9d66e3dc9ed88fb2" + "version": "rootform 0.2.0", + "sha256": "65e59793486c95015b62298989a4b7c737cad0cc834496f0dca01ef58a993241" }, "examples": { "commerce-platform": { @@ -23,8 +23,8 @@ } }, "documents": { - "commerce-platform-analysis.json": "40010b680ace3e54291ea46e6f8cf04748899f8d6f00287dd8481e50ead41b69", - "commerce-platform-comparison.json": "801360dc0077a1a768322ac07ffd3be45f50d10c964c3dd536c96f755281a625", + "commerce-platform-analysis.json": "6be055b0c3404a9e6c1f27d34c7fb955c6363f531c59009a14c823560b788d2a", + "commerce-platform-comparison.json": "61dceae0f178f9d2d3c0ff8b472c09717ba465889c4d7bb94811e8beaf420b27", "commerce-platform-presentation.json": "1673648dc5553d27d814689266a88515f2262e1713f01c294470316f9cb49f49" } }, @@ -46,8 +46,8 @@ } }, "documents": { - "event-driven-platform-analysis.json": "627cbb1b2fb8797720e8fe7cba1875490c4f6ca031745186b5660e4f65ce97a0", - "event-driven-platform-comparison.json": "b3e55735f44b20f337cb95815a696efea9a9475f8fc8d50a9b667131da60bbaa", + "event-driven-platform-analysis.json": "617e537a0bd34c9b60d2121a68ec7af947aab9716ddac6e7269c8407190c0639", + "event-driven-platform-comparison.json": "8916f3c1a5ea66f8e236995b66cdccd08e436d73ad472107f09418a57ac04bcb", "event-driven-platform-presentation.json": "1673648dc5553d27d814689266a88515f2262e1713f01c294470316f9cb49f49" } }, @@ -69,8 +69,8 @@ } }, "documents": { - "shared-data-platform-analysis.json": "a163e64765fab08577bdf512ff557308993e1785166e84242abcd49f4371877e", - "shared-data-platform-comparison.json": "8c9235e37df707403baab2aaced3a37f9e3b870a29e8e5ccb7124e353cfa1bbe", + "shared-data-platform-analysis.json": "7319299f19d3ee5032da81a2e56467c2630f7d8125c3ad2e3fbbb1875945f643", + "shared-data-platform-comparison.json": "5249891a5ba1dae4cc01759f9bb5d6d59538dedcc36a6e227da212eddb952b19", "shared-data-platform-presentation.json": "1673648dc5553d27d814689266a88515f2262e1713f01c294470316f9cb49f49" } } diff --git a/examples/playground/forms/shared-data-platform-analysis.json b/examples/playground/forms/shared-data-platform-analysis.json index 8fb4afc..6df9430 100644 --- a/examples/playground/forms/shared-data-platform-analysis.json +++ b/examples/playground/forms/shared-data-platform-analysis.json @@ -3,7 +3,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", diff --git a/examples/playground/forms/shared-data-platform-comparison.json b/examples/playground/forms/shared-data-platform-comparison.json index 099a0b4..36dc28f 100644 --- a/examples/playground/forms/shared-data-platform-comparison.json +++ b/examples/playground/forms/shared-data-platform-comparison.json @@ -3,7 +3,7 @@ "kind": "comparison", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "before": { "form": { @@ -11,7 +11,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", @@ -69540,7 +69540,7 @@ "kind": "plan", "generator": { "name": "rootform", - "version": "0.2.0-rc.1" + "version": "0.2.0" }, "evidence": { "origin": "plan", diff --git a/package.json b/package.json index 9a00c2e..346ffca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rootform/distribution", - "version": "0.2.0-rc.1", + "version": "0.2.0", "private": true, "description": "Rootform public contracts and distribution metadata", "license": "Apache-2.0", diff --git a/scripts/prepare-release.test.ts b/scripts/prepare-release.test.ts index 3f1dfda..7c84dde 100644 --- a/scripts/prepare-release.test.ts +++ b/scripts/prepare-release.test.ts @@ -142,3 +142,46 @@ test("the check names every unprepared place", () => { "public-export.json: no exact Engine commit", ]); }); + +test("CI recipes and their guide install the released binary", () => { + const root = repository(); + const recipes: Record = { + "docs/integrations/ci/README.md": + "installs the exact `ROOTFORM_VERSION` into a directory.\naccepts `ROOTFORM_VERSION` (default `0.1.0`) and\n", + "docs/integrations/ci/azure-pipelines.yml": + " ROOTFORM_MODE=check ROOTFORM_VERSION=0.1.0 sh ./ci/generic-ci.sh\n", + "docs/integrations/ci/generic-ci.sh": `version=\${ROOTFORM_VERSION:-0.1.0}\nROOTFORM_VERSION="$version" sh install.sh\n`, + "docs/integrations/ci/github-actions-plan.yml": + " terraform_version: 1.16.4\n - uses: rootform-dev/action@v1\n with:\n version: 0.1.0\n", + "docs/integrations/ci/gitlab-ci.yml": " ROOTFORM_VERSION: 0.1.0\n", + }; + for (const [path, body] of Object.entries(recipes)) { + mkdirSync(dirname(join(root, path)), { recursive: true }); + writeFileSync(join(root, path), body); + } + const manifest = readFileSync(join(root, "package.json"), "utf8"); + writeFileSync(join(root, "package.json"), manifest.replace('"0.1.0"', '"0.1.2"')); + expect(checkRelease(root).problems).toContain( + "docs/integrations/ci/generic-ci.sh:1: names Rootform 0.1.0, not 0.1.2", + ); + writeFileSync(join(root, "package.json"), manifest); + expect(prepareRelease(root, "0.1.2")).toEqual([ + "CHANGELOG.md", + "package.json", + "distribution/installers/README.md", + "docs/installation.md", + "docs/integrations/ci/azure-pipelines.yml", + "docs/integrations/ci/generic-ci.sh", + "docs/integrations/ci/github-actions-plan.yml", + "docs/integrations/ci/gitlab-ci.yml", + "docs/integrations/ci/README.md", + "docs/integrations/github-actions.md", + "README.md", + ]); + for (const [path, body] of Object.entries(recipes)) { + const text = readFileSync(join(root, path), "utf8"); + expect(text).not.toContain("0.1.0"); + expect(text).toBe(body.replaceAll("0.1.0", "0.1.2")); + } + expect(checkRelease(root).problems).toEqual([]); +}); diff --git a/scripts/prepare-release.ts b/scripts/prepare-release.ts index 609a6cd..35d566b 100644 --- a/scripts/prepare-release.ts +++ b/scripts/prepare-release.ts @@ -1,6 +1,6 @@ #!/usr/bin/env bun -import { readFileSync, writeFileSync } from "node:fs"; +import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { documentationFiles, @@ -42,19 +42,39 @@ const MENTIONS: ReadonlyArray<{ applies: (file: string) => boolean; pattern: Reg { applies: () => true, pattern: new RegExp(`(\\[Rootform v)(${VERSION})(?=\\])`, "gu") }, { // The version input of the GitHub Action examples. - applies: (file) => file.startsWith("docs/integrations/github-actions"), + applies: (file) => + file.startsWith("docs/integrations/github-actions") || + file === "docs/integrations/ci/github-actions-plan.yml", pattern: new RegExp(`^(\\s*version: )(${VERSION})$`, "gmu"), }, + { + // The CI recipes and their guide install the released binary by version. + applies: (file) => file.startsWith("docs/integrations/ci/"), + pattern: new RegExp(`(ROOTFORM_VERSION(?::-|: |=|\` \\(default \`))(${VERSION})`, "gu"), + }, { applies: (file) => file === "distribution/installers/README.md", pattern: new RegExp(`(--version )(${VERSION})`, "gu"), }, ]; +// The CI recipes beside their guide are scripts and pipeline files. +const RECIPES = "docs/integrations/ci"; + +function recipeFiles(root: string): string[] { + const directory = join(root, RECIPES); + if (!existsSync(directory)) return []; + return readdirSync(directory, { withFileTypes: true }) + .filter((entry) => entry.isFile() && /\.(?:sh|ya?ml)$/u.test(entry.name)) + .map((entry) => `${RECIPES}/${entry.name}`); +} + export function releaseDocuments(root: string): string[] { - return [...documentationFiles(root), "distribution/installers/README.md"].sort((left, right) => - left.localeCompare(right, "en"), - ); + return [ + ...documentationFiles(root), + ...recipeFiles(root), + "distribution/installers/README.md", + ].sort((left, right) => left.localeCompare(right, "en")); } export function versionMentions(file: string, text: string): Mention[] {