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
19 changes: 17 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
name: ci
name: CI

on:
pull_request:
push:
branches:
- production
- lifecycle-action-integration
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
validate:
name: validate action contract
Expand All @@ -34,3 +38,14 @@ jobs:
- name: Run validation
shell: bash
run: make test

lint-workflows:
name: lint GitHub Actions workflows
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Run actionlint
shell: bash
run: docker run --rm -v "$PWD:/repo" -w /repo rhysd/actionlint:1.7.12 -color
47 changes: 47 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Verify release

on:
release:
types:
- published

permissions:
contents: read

jobs:
verify:
name: verify action release
runs-on: ubuntu-latest
Comment thread
fqjony marked this conversation as resolved.
steps:
- name: Validate semantic release tag
shell: bash
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
set -euo pipefail
if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error title=Invalid release tag::Expected a semantic patch tag such as v1.0.3, got $RELEASE_TAG."
exit 1
fi

- name: Checkout release
uses: actions/checkout@v5
with:
ref: ${{ github.event.release.tag_name }}

- name: Install pinned yq
shell: bash
run: |
set -euo pipefail
version="v4.44.3"
sha256="a2c097180dd884a8d50c956ee16a9cec070f30a7947cf4ebf87d5f36213e9ed7"
install_dir="${RUNNER_TEMP}/rabbit-action-bin"
mkdir -p "$install_dir"
curl -fsSL "https://github.com/mikefarah/yq/releases/download/${version}/yq_linux_amd64" -o "$install_dir/yq"
printf '%s %s\n' "$sha256" "$install_dir/yq" | sha256sum --check --status
chmod +x "$install_dir/yq"
echo "$install_dir" >> "$GITHUB_PATH"

- name: Verify release contract
shell: bash
run: make test
17 changes: 16 additions & 1 deletion .rabbit/repo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ repository:
owner: udx
default_branch: production
branches:
- name: dependabot/github_actions/actions/checkout-7
rules: {}
- name: dependabot/github_actions/actions/github-script-9
rules: {}
- name: dependabot/github_actions/actions/upload-artifact-7
rules: {}
- name: fix/gcp-credential-mount
rules: {}
- name: infra-templates
rules: {}
- name: lifecycle-action-integration
Expand Down Expand Up @@ -60,6 +68,13 @@ workflows:
push:
branches:
- production
- lifecycle-action-integration
workflow_dispatch: {}
permissions:
contents: read
- path: .github/workflows/release.yml
triggers:
release:
types:
- published
permissions:
contents: read
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

Run `make test` before opening or updating a pull request.

## Releases

Keep public action changes backward compatible within `v1`. Release immutable
`v1.x.y` tags from `production`, publish the release to GitHub Marketplace,
then move the `v1` tag only after caller canary validation. Follow
[`docs/releasing.md`](docs/releasing.md); do not publish or move tags as part
of an ordinary pull-request update.

## Rabbit CI Context

`.rabbit/repo.yaml` is generated by `rabbit.ci`; do not edit it by hand.
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Changelog

All notable changes to this action are recorded here. Versions follow semantic
versioning; callers should normally use the maintained `v1` major tag.

## v1.0.2 - 2026-08-18

- Made lifecycle resolution and Rabbit configuration merging self-contained.
- Added caller-selectable lifecycle policy and configuration-root inputs.
- Kept cloud identity in the caller workflow; the action consumes prepared
runtime credentials only.

## v1.0.1 - 2026-04-30

- Initial GitHub Marketplace release.
26 changes: 24 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,21 +1,43 @@
.PHONY: test validate-shell validate-action validate-workflow
.PHONY: test validate-shell validate-shellcheck validate-action validate-workflow

test: validate-shell validate-action validate-workflow
test: validate-shell validate-shellcheck validate-action validate-workflow
tests/run-merge-tests.sh

validate-shell:
bash -n \
bin/merge-configs.sh \
bin/resolve-lifecycle.sh \
bin/render-plan-summary.sh \
bin/lib/config.sh \
bin/lib/discovery.sh \
bin/lib/github.sh \
bin/lib/lifecycle.sh \
bin/lib/validation.sh \
bin/lib/environment.sh \
bin/lib/logging.sh \
bin/lib/merge.sh \
bin/lib/output.sh \
tests/run-merge-tests.sh

validate-shellcheck:
shellcheck --external-sources --severity=error \
bin/merge-configs.sh \
bin/resolve-lifecycle.sh \
bin/render-plan-summary.sh \
bin/lib/config.sh \
bin/lib/discovery.sh \
bin/lib/environment.sh \
bin/lib/github.sh \
bin/lib/lifecycle.sh \
bin/lib/logging.sh \
bin/lib/merge.sh \
bin/lib/output.sh \
bin/lib/validation.sh \
tests/run-merge-tests.sh

validate-action:
yq eval '.' action.yml >/dev/null

validate-workflow:
yq eval '.' .github/workflows/ci.yml >/dev/null
yq eval '.' .github/workflows/release.yml >/dev/null
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ schema, prerequisites, examples, and outputs.
Services deploy in ascending module order; destroy operations use the reverse
order.

## Releases

Use the maintained `@v1` major tag in caller workflows. Patch releases are
published as immutable `v1.x.y` GitHub releases, then `v1` moves to the tested
compatible release. See [the release guide](docs/releasing.md) and
[changelog](CHANGELOG.md).

---

## Configuration Reference
Expand Down
22 changes: 20 additions & 2 deletions bin/lib/lifecycle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,27 @@ lifecycle_detect_environments() {
fi

# Standard lifecycle directories (production, staging, development)
if [[ " ${ALL_LIFECYCLES[@]} " =~ " ${dir_name} " ]]; then
local configured_lifecycle=false
local lifecycle
for lifecycle in "${ALL_LIFECYCLES[@]}"; do
if [[ "$lifecycle" == "$dir_name" ]]; then
configured_lifecycle=true
break
fi
done

if [[ "$configured_lifecycle" == true ]]; then
# Add once
if [[ ! " ${found_lifecycles[@]} " =~ " ${dir_name} " ]]; then
local already_found=false
local found_lifecycle
for found_lifecycle in "${found_lifecycles[@]}"; do
if [[ "$found_lifecycle" == "$dir_name" ]]; then
already_found=true
break
fi
done

if [[ "$already_found" == false ]]; then
envs_ref+=("$dir_name")
found_lifecycles+=("$dir_name")
dbg "Detected $dir_name at: $dir" >&2
Expand Down
42 changes: 42 additions & 0 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Releasing Rabbit Automation Action

The action is released from `production`. Patch releases are immutable
`v1.x.y` tags; `v1` is the movable compatibility tag that callers use.

## Before publishing

1. Merge a focused, reviewed pull request into `production`.
2. Confirm the CI action-contract and workflow-lint jobs pass.
3. Test the exact `production` commit from a caller's non-production
environment. Use `@production` only for that canary.
4. Add a concise, user-facing entry to `CHANGELOG.md` when the behavior
changes.

## Publish the release

1. Create a semantic GitHub release from the tested `production` commit, for
example `v1.0.3`.
2. In the release form, select **Publish this Action to the GitHub
Marketplace**. GitHub requires this UI step and may require 2FA; a release
created only through the REST or CLI release API is not enough.
3. Keep `Deployment` as the primary Marketplace category and `Security` as the
secondary category unless the action's public purpose changes.
4. Verify the Marketplace listing shows the new version, current `action.yml`
metadata, and current README before changing any caller references.

Publishing triggers `Verify release`, which checks the semantic tag and runs
the action contract from that tag. It confirms the published artifact; it does
not replace the pre-release caller canary or the Marketplace UI verification.

## Promote callers

1. Move the `v1` tag to the tested immutable release commit.
2. Confirm `v1` and the patch tag resolve to the same commit with
`git ls-remote --tags origin 'v1*'`.
3. Update reusable workflows and callers from `@production` to `@v1`.
4. Run a non-production caller plan using `@v1` before merging the consumer
change.

Use a new major tag for breaking input, output, safety, or lifecycle-contract
changes. Keep `v1` on the latest compatible patch release; do not rewrite a
patch release tag.
17 changes: 17 additions & 0 deletions tests/run-merge-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,23 @@ run_resolve "$SOURCE" "dev-alice" "$subdir_out"
assert_eq "$(read_output "$subdir_out" lifecycle)" "development" "Development subdirectory resolves in-repo"
assert_eq "$(read_output "$subdir_out" resolution_reason)" "environment_subdirectory" "Subdirectory reason is recorded"

scenario "Scenario: lifecycle discovery recognizes configured roots once"
detected_lifecycles="$TEMP_DIR/detected-lifecycles.out"
(
GITHUB_WORKSPACE="$ROOT"
INPUT_SOURCE_DIR="$SOURCE"
INPUT_ENV_NAME=""
INPUT_LIFECYCLE_POLICY_PATH=""
source "$PROJECT_ROOT/bin/lib/config.sh"
source "$PROJECT_ROOT/bin/lib/lifecycle.sh"
detected=()
lifecycle_detect_environments "$SOURCE" detected
printf '%s\n' "${detected[@]}"
) > "$detected_lifecycles"
assert_eq "$(grep -cx 'production' "$detected_lifecycles")" "1" "Production lifecycle is discovered once"
assert_eq "$(grep -cx 'staging' "$detected_lifecycles")" "1" "Staging lifecycle is discovered once"
assert_eq "$(grep -cx 'development' "$detected_lifecycles")" "1" "Development lifecycle is discovered once"

scenario "Scenario: resolver uses a caller lifecycle policy for both policy metadata and merge"
policy_out="$TEMP_DIR/policy.out"
run_resolve "$SOURCE" "dev-alice" "$policy_out" ".rabbit/lifecycle-policy.yaml"
Expand Down