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
26 changes: 25 additions & 1 deletion module-ci-action/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ triggering event:
| Event | Mode | What it does |
|-------|------|--------------|
| `pull_request` opened / synchronize / reopened | **preview** | Creates each changed output type the Control Plane does not have yet, then validates each changed module and registers a **feature-branch preview** pinned to the PR head commit. Optionally upserts a PR comment. |
| `push` (to your default branch) | **publish** | Applies each changed output type, then uploads each changed module and **publishes** it (PREVIEW → PUBLISHED). |
| `push` (to your default branch) | **publish** | Applies each changed output type, then uploads each changed module and **publishes** it (PREVIEW → PUBLISHED), declaring compatibility from the registry state — see **Compatibility declarations** below. |
| `pull_request` **closed without merge** | **cleanup** | Deletes the preview each changed module owns — **only if** the preview belongs to a commit from this PR. |
| `pull_request` **closed by merge** | **no-op** | Skipped: the concurrent `push` (publish) run re-uploads and publishes the module at the merge commit, so it owns the slot. Running cleanup here would be redundant and could race the publish. |

Expand Down Expand Up @@ -50,6 +50,30 @@ committed.
If your trees are not at the repo root, set `path-prefix` (e.g. `infra/` when they
live at `infra/modules/...` and `infra/outputs/...`).

## Compatibility declarations

`raptor publish` refuses to publish over an already-published version without an
explicit `--backward-compatible yes`, and refuses a new contract version without
`--versionupgrade` (raptor ≥ v0.1.106). A bare publish therefore fails for every
module a repository has already shipped.

In a repo-first flow the author has already declared, in git:

| What the author did | What CI passes |
|---|---|
| Edited `modules/{intent}/{flavor}/0.2` in place | `--backward-compatible yes` |
| Added a new `modules/{intent}/{flavor}/0.3` directory | `--versionupgrade` |
| Added the first module of a type/flavor | nothing |

The action reads the published versions of that `intent/flavor` back off the
Control Plane and passes the matching flag. Version **ordering** stays raptor's
job: a "bump" that is not actually greater than every published version is
rejected there, and should be.

If the listing cannot be read, the module fails rather than publishing with a
guessed declaration — reading a failed count as "nothing is published" would send
a first-publication for a module that already has one.

## Output types

A module names its output type by reference (`@namespace/name`). The Control Plane
Expand Down
46 changes: 45 additions & 1 deletion module-ci-action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -584,8 +584,52 @@ runs:
echo "::endgroup::"; continue
fi

# raptor refuses to publish over an already-published version without an
# explicit compatibility declaration, and refuses a NEW version without
# --versionupgrade. A bare publish therefore fails for every module a
# repository has already shipped.
#
# In a repo-first flow the author HAS declared, in git: editing
# modules/{intent}/{flavor}/0.2 in place says "compatible"; adding a 0.3
# directory says "breaking". Read that choice back off the registry and
# pass the matching flag. Version ORDERING stays raptor's job — a bump
# that is not actually greater is rejected there, and must be.
if ! MODULES_JSON="$(raptor get iac-module -o json 2>/dev/null)"; then
echo "::error::Could not list modules to determine the compatibility declaration for ${REF}"
echo "${REF} (publish)" >> "$FAILURES"
echo "::endgroup::"; continue
fi
family() {
printf '%s' "$MODULES_JSON" | jq -r --arg t "$TYPE" --arg f "$FLAVOR" --arg v "$1" \
'(if type=="array" then . else [] end)
| map(select(.intent==$t and .flavor==$f and .stage=="PUBLISHED"
and ($v=="" or .version==$v)))
| length' 2>/dev/null || echo ""
}
PUBLISHED_ANY="$(family "")"
PUBLISHED_TARGET="$(family "$VERSION")"
if [ -z "$PUBLISHED_ANY" ] || [ -z "$PUBLISHED_TARGET" ]; then
# A failed count is not "nothing is published" — that reading would send
# a first-publication (no flag) for a module that already has one.
echo "::error::Could not read published versions of ${TYPE}/${FLAVOR} for ${REF}"
echo "${REF} (publish)" >> "$FAILURES"
echo "::endgroup::"; continue
fi

DECLARATION=""
if [ "$PUBLISHED_TARGET" != "0" ]; then
DECLARATION="--backward-compatible yes"
echo "${VERSION} is already published: declaring a compatible update."
elif [ "$PUBLISHED_ANY" != "0" ]; then
DECLARATION="--versionupgrade"
echo "${VERSION} is a new contract version for ${TYPE}/${FLAVOR}: declaring a version upgrade."
else
echo "First publication of ${TYPE}/${FLAVOR}: no declaration needed."
fi

echo "Publishing ${REF}..."
if ! raptor publish iac-module "$REF"; then
# shellcheck disable=SC2086 # $DECLARATION is a deliberate empty-or-flag word list
if ! raptor publish iac-module "$REF" $DECLARATION; then
echo "::error::Publish failed for ${REF}"
echo "${REF} (publish)" >> "$FAILURES"
echo "::endgroup::"; continue
Expand Down