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
36 changes: 35 additions & 1 deletion .github/workflows/container-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ on:
type: string
required: false
default: ''
supersede_run_id:
description: Prior successful full publication run to explicitly correct (advanced recovery only)
type: string
required: false
default: ''
release:
types:
- published
Expand All @@ -63,6 +68,7 @@ jobs:
core_version: ${{ steps.release.outputs.core_version }}
studio_version: ${{ steps.release.outputs.studio_version }}
extensions_version: ${{ steps.release.outputs.extensions_version }}
supersede_run_id: ${{ steps.release.outputs.supersede_run_id }}
images: ${{ steps.release.outputs.images }}
matrix: ${{ steps.release.outputs.matrix }}
steps:
Expand All @@ -85,6 +91,7 @@ jobs:
STUDIO_VERSION: ${{ inputs.studio_version || '' }}
EXTENSIONS_VERSION: ${{ inputs.extensions_version || '' }}
EXPECTED_COMMIT: ${{ inputs.expected_commit || '' }}
SUPERSEDE_RUN_ID: ${{ inputs.supersede_run_id || '' }}
run: |
python3 scripts/container_release.py validate-run \
--event "$GITHUB_EVENT_NAME" \
Expand All @@ -95,10 +102,20 @@ jobs:
--studio-version "$STUDIO_VERSION" \
--extensions-version "$EXTENSIONS_VERSION" \
--expected-commit "$EXPECTED_COMMIT" \
--supersede-run-id "$SUPERSEDE_RUN_ID" \
--release-tag "$RELEASE_EVENT_TAG" \
--ref "$GITHUB_REF" \
--commit "$GITHUB_SHA"

- name: Require correction source to be in main history
if: inputs.supersede_run_id != ''
run: |
git fetch --no-tags origin main:refs/remotes/origin/main
git merge-base --is-ancestor "$GITHUB_SHA" refs/remotes/origin/main || {
echo "A correction must use a source commit already included in main." >&2
exit 1
}

- name: Require published tag to be present in main history
if: steps.release.outputs.publish == 'true' && startsWith(github.ref, 'refs/tags/')
run: |
Expand Down Expand Up @@ -148,15 +165,21 @@ jobs:
CORE_VERSION: ${{ needs.prepare.outputs.core_version }}
STUDIO_VERSION: ${{ needs.prepare.outputs.studio_version }}
EXTENSIONS_VERSION: ${{ needs.prepare.outputs.extensions_version }}
SUPERSEDE_RUN_ID: ${{ needs.prepare.outputs.supersede_run_id }}
run: |
PLAN_OPTIONS=()
if [[ -n "$SUPERSEDE_RUN_ID" ]]; then
PLAN_OPTIONS+=(--skip-version-tag)
fi
python3 scripts/container_release.py plan-image \
--repository "$IMAGE_REPOSITORY" \
--version "$RELEASE_VERSION" \
--commit "$SOURCE_COMMIT" \
--source-ref "$SOURCE_REF" \
--core-version "$CORE_VERSION" \
--studio-version "$STUDIO_VERSION" \
--extensions-version "$EXTENSIONS_VERSION"
--extensions-version "$EXTENSIONS_VERSION" \
"${PLAN_OPTIONS[@]}"

- name: Build and push immutable source tag
if: needs.prepare.outputs.publish == 'true' && steps.plan.outputs.reuse != 'true'
Expand Down Expand Up @@ -270,6 +293,9 @@ jobs:

finalize:
name: Publish version tags and write receipt
permissions:
actions: read
contents: read
needs:
- prepare
- build-and-smoke
Expand All @@ -279,6 +305,11 @@ jobs:
uses: actions/checkout@v4
with:
ref: ${{ needs.prepare.outputs.source_commit }}
fetch-depth: 0

- name: Fetch main for correction ancestry validation
if: needs.prepare.outputs.supersede_run_id != ''
run: git fetch --no-tags origin main:refs/remotes/origin/main

- name: Download image evidence
uses: actions/download-artifact@v4
Expand Down Expand Up @@ -315,10 +346,12 @@ jobs:
INPUT_CORE_VERSION: ${{ inputs.core_version || '' }}
INPUT_STUDIO_VERSION: ${{ inputs.studio_version || '' }}
INPUT_EXTENSIONS_VERSION: ${{ inputs.extensions_version || '' }}
SUPERSEDE_RUN_ID: ${{ needs.prepare.outputs.supersede_run_id }}
EVENT_NAME: ${{ github.event_name }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
GH_TOKEN: ${{ github.token }}
run: |
python3 scripts/container_release.py finalize \
--publication "$PUBLICATION" \
Expand All @@ -338,6 +371,7 @@ jobs:
--input-core-version "$INPUT_CORE_VERSION" \
--input-studio-version "$INPUT_STUDIO_VERSION" \
--input-extensions-version "$INPUT_EXTENSIONS_VERSION" \
--supersede-run-id "$SUPERSEDE_RUN_ID" \
--run-id "$GITHUB_RUN_ID" \
--run-attempt "$GITHUB_RUN_ATTEMPT" \
--artifact-dir artifacts/image-evidence \
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<ItemGroup>
<PackageVersion Include="Elsa" Version="$(ElsaVersion)"/>
<PackageVersion Include="Elsa.Api.Client" Version="$(ElsaVersion)"/>
<PackageVersion Include="Elsa.Dashboard.Api" Version="$(ElsaVersion)"/>
<PackageVersion Include="Elsa.Expressions.CSharp" Version="$(ElsaVersion)"/>
<PackageVersion Include="Elsa.Persistence.EFCore" Version="$(ElsaVersion)"/>
<PackageVersion Include="Elsa.Persistence.EFCore.MySql" Version="$(ElsaVersion)"/>
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Each Elsa Apps release publishes an exact version tag and a source-specific tag

The shared [container image workflow](.github/workflows/container-images.yml) builds all six images for pull-request validation without pushing. A manual run defaults to build-only. Publishing requires `publish: true`, a reviewed `main` or matching release-tag ref, the pinned full `expected_commit`, exact package versions for every selected image family, and either `all` or a comma-separated subset of these profile names: `server`, `studio-server`, `studio-wasm`, `studio-wasm-standalone`, `server-studio-server`, and `server-studio-wasm`. A published GitHub release tag builds and publishes the full image set automatically. The workflow validates every selected image's platform manifests and restored package assets, starts the image on each platform, verifies Blazor browser assets, and tests the server login and bearer-authenticated API on server hosts before creating version tags and the machine-readable receipt artifact.

For a correction to a completed publication, first merge the fix to `main`, then manually dispatch this workflow from `main` with the exact version and all three package versions, `images: all`, `publish: true`, the full reviewed `expected_commit`, and `supersede_run_id` set to the prior successful publication's run ID. The workflow verifies that run and its receipt artifact, builds and smokes all six profiles, and checks all eight existing version tags against the prior receipt before promoting. It allows only tags matching the prior receipt or the already-verified correction candidate, so an interrupted correction can be retried; a missing or unexpected tag blocks the correction. The new receipt links the superseded run, artifact digest, source commit, and prior image digests. The prior receipt remains intact, and this path never updates Elsa 4 or `latest` tags. Leave `supersede_run_id` blank for normal immutable publication, where conflicting version tags continue to be rejected.

Studio deployments should set the backend URL using `Backend__Url`, which is injected into the hosted client configuration as `window.elsaConfig.backendUrl`.

## License
Expand Down
Loading
Loading