CI: restructure release workflows
What changed, and why it matters
This commit is a housekeeping change to the project's automated release pipelines on GitHub. It splits the existing release workflow into separate 'build' and 'publish' steps, adds a version/tag consistency check, and fans out Docker builds across multiple platforms. There is no change to the Core Lightning software itself, its network behavior, wallet handling, or cryptographic code.
No security action required. Reviewers may optionally verify that the new reusable workflows preserve intended repository restrictions (e.g., github.repository == 'ElementsProject/lightning' checks remain in pypi-publish.yml) and that the tag pattern broadening is intentional.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff restructures GitHub Actions workflows under .github/workflows. It deletes monolithic docker-release.yml and pypi.yml workflows and introduces reusable workflow_call workflows: check-release-tag.yml, docker-build.yml, docker-publish.yml, pypi-build.yml, pypi-publish.yml, release-build.yml, release-publish.yml. The top-level release.yml orchestrates these with an explicit gate job so publishing occurs only after all builds succeed. Notable additions include a tag-vs-.version validation step, matrix-based per-platform Docker builds using push-by-digest, and a new tag pattern ‘v[0-9]+.[0-9]+.[0-9]+[0-9a-z]+’. No application source code is modified.
Changed components
.github/workflows/check-release-tag.yml.github/workflows/docker-build.yml.github/workflows/docker-publish.yml.github/workflows/docker-release.yml.github/workflows/pypi-build.yml.github/workflows/pypi-publish.yml.github/workflows/pypi.yml.github/workflows/release-build.yml.github/workflows/release-publish.yml.github/workflows/release.ymlInspect captured patch +536 / −396
diff --git a/.github/workflows/check-release-tag.yml b/.github/workflows/check-release-tag.yml
new file mode 100644
index 00000000..1b2b3aa1
--- /dev/null
+++ b/.github/workflows/check-release-tag.yml
@@ -0,0 +1,56 @@
+name: Check release tag
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ required: false
+ skip_validation:
+ type: boolean
+ default: false
+ outputs:
+ version:
+ description: "Resolved release version"
+ value: ${{ jobs.check.outputs.version }}
+
+jobs:
+ check:
+ name: Validate tag against .version file and release content
+ runs-on: ubuntu-24.04
+ outputs:
+ version: ${{ steps.resolve.outputs.version }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ - name: Validate tag matches .version file
+ id: resolve
+ run: |
+ if [[ "${{ github.ref_type }}" != "tag" ]]; then
+ echo "::notice::Not triggered by a tag push (ref_type=${{ github.ref_type }}); skipping .version check."
+ echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
+ exit 0
+ fi
+
+ if [[ ! -f .version ]]; then
+ echo "::error::.version file not found in repository root."
+ exit 1
+ fi
+
+ FILE_VERSION="$(tr -d '[:space:]' < .version)"
+ TAG_VERSION="${{ github.ref_name }}"
+
+ if [[ "$TAG_VERSION" != "$FILE_VERSION" ]]; then
+ echo "::error::Tag '$TAG_VERSION' does not match version in .version file ('$FILE_VERSION'). Refusing to release."
+ exit 1
+ fi
+
+ echo "Tag '$TAG_VERSION' matches .version file."
+ echo "version=$TAG_VERSION" >> "$GITHUB_OUTPUT"
+
+ - name: Validate release
+ if: ${{ !inputs.skip_validation }}
+ run: tools/check-release.sh --version=${{ steps.resolve.outputs.version }}
diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml
new file mode 100644
index 00000000..6b247930
--- /dev/null
+++ b/.github/workflows/docker-build.yml
@@ -0,0 +1,86 @@
+name: Docker build
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ required: true
+ dh-repository-owner:
+ type: string
+ required: true
+ platforms-to-build:
+ type: string
+ required: true
+
+jobs:
+ # Turn the comma-separated platforms-to-build input into a JSON matrix array,
+ # so we can fan out per-platform without hardcoding the list twice.
+ plan:
+ name: Plan platform matrix
+ runs-on: ubuntu-24.04
+ outputs:
+ platforms: ${{ steps.plan.outputs.platforms }}
+ steps:
+ - id: plan
+ run: |
+ IFS=',' read -ra ARR <<< "${{ inputs.platforms-to-build }}"
+ JSON=$(printf '%s\n' "${ARR[@]}" | jq -R . | jq -sc .)
+ echo "platforms=$JSON" >> "$GITHUB_OUTPUT"
+
+ build:
+ name: Build ${{ matrix.target == 'lightningd-vls-signer' && 'vls' || 'normal' }} / ${{ matrix.platform }}
+ needs: plan
+ strategy:
+ fail-fast: false
+ matrix:
+ target:
+ - lightningd
+ - lightningd-vls-signer
+ platform: ${{ fromJson(needs.plan.outputs.platforms) }}
+ runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04' }}
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ - name: Set up QEMU
+ if: matrix.platform == 'linux/arm/v7'
+ uses: docker/setup-qemu-action@v4
+
+ - name: Setup Docker Buildx
+ uses: docker/setup-buildx-action@v4
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v4
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+
+ - name: Build single-platform image, push by digest
+ id: build
+ uses: docker/build-push-action@v7
+ with:
+ context: .
+ file: ./Dockerfile
+ target: ${{ matrix.target }}
+ platforms: ${{ matrix.platform }}
+ build-args: |
+ VERSION=${{ inputs.version }}
+ outputs: type=image,name=${{ inputs.dh-repository-owner }}/lightningd,push-by-digest=true,name-canonical=true,push=true
+ cache-from: type=gha,scope=${{ matrix.target }}-${{ matrix.platform }}
+ cache-to: type=gha,mode=max,scope=${{ matrix.target }}-${{ matrix.platform }}
+
+ - name: Save digest
+ run: |
+ mkdir -p /tmp/digests
+ echo "${{ steps.build.outputs.digest }}" > /tmp/digests/digest.txt
+
+ - name: Upload digest
+ uses: actions/upload-artifact@v7
+ with:
+ name: digest_${{ matrix.target }}_${{ strategy.job-index }}
+ path: /tmp/digests/digest.txt
+ if-no-files-found: error
+ retention-days: 1
diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml
new file mode 100644
index 00000000..78bcaf64
--- /dev/null
+++ b/.github/workflows/docker-publish.yml
@@ -0,0 +1,67 @@
+name: Docker publish
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ required: true
+ dh-repository-owner:
+ type: string
+ required: true
+ push-latest:
+ type: string
+ required: true
+
+jobs:
+ publish:
+ name: Tag and publish ${{ matrix.target }}
+ runs-on: ubuntu-22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - target: lightningd
+ tag_suffix: ''
+ - target: lightningd-vls-signer
+ tag_suffix: '-vls'
+ steps:
+ - name: Setup Docker Buildx
+ uses: docker/setup-buildx-action@v4
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v4
+ with:
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
+
+ - name: Download platform digests for this target
+ uses: actions/download-artifact@v8
+ with:
+ pattern: digest_${{ matrix.target }}_*
+ path: /tmp/digests
+ merge-multiple: false
+
+ - name: Set up values
+ run: |
+ DIGEST_REFS=""
+ for f in /tmp/digests/*/digest.txt; do
+ D=$(cat "$f")
+ DIGEST_REFS="$DIGEST_REFS ${{ inputs.dh-repository-owner }}/lightningd@${D}"
+ done
+ echo "DIGEST_REFS=$DIGEST_REFS" >> $GITHUB_ENV
+
+ if [[ "${{ inputs.push-latest }}" == "true" ]] ||
+ ([[ "${{ github.ref_type }}" == "tag" ]] && [[ ! "${{ inputs.version }}" =~ rc ]]); then
+ echo "PUSHLATEST=true" >> $GITHUB_ENV
+ else
+ echo "PUSHLATEST=false" >> $GITHUB_ENV
+ fi
+
+ - name: Create and push real tag(s)
+ run: |
+ TAG_ARGS=(-t "${{ inputs.dh-repository-owner }}/lightningd:${{ inputs.version }}${{ matrix.tag_suffix }}")
+ if [[ "$PUSHLATEST" == "true" ]]; then
+ TAG_ARGS+=(-t "${{ inputs.dh-repository-owner }}/lightningd:latest${{ matrix.tag_suffix }}")
+ fi
+ docker buildx imagetools create "${TAG_ARGS[@]}" $DIGEST_REFS
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
deleted file mode 100644
index 73863499..00000000
--- a/.github/workflows/docker-release.yml
+++ /dev/null
@@ -1,134 +0,0 @@
-name: Publish multi-platform docker images
-
-on:
- push:
- tags:
- - 'v[0-9]+.[0-9]+'
- - 'v[0-9]+.[0-9]+.[0-9]+'
- - 'v[0-9]+.[0-9]+[0-9a-z]+'
- workflow_dispatch:
- inputs:
- version:
- description: 'Release version'
- required: true
-
- repository-name:
- description: 'Docker repository name'
- default: 'elementsproject'
- required: false
-
- platforms-to-build:
- description: 'List of platforms to build'
- default: 'linux/amd64,linux/arm64,linux/arm/v7'
- required: false
-
- push-latest:
- description: 'Push the latest tag also?'
- default: 'false'
- required: false
-
-jobs:
- build:
- runs-on: ubuntu-22.04
- strategy:
- fail-fast: false # Let each tag finish.
- matrix:
- include:
- - target: lightningd
- tag_suffix: ''
- - target: lightningd-vls-signer
- tag_suffix: '-vls'
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v6
- with:
- ref: ${{ github.ref }} # Ensures the branch triggering the workflow is checked out
- fetch-depth: 0
-
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v4
-
- - name: Setup Docker Buildx
- uses: docker/setup-buildx-action@v4
-
- - name: Log in to Docker Hub
- uses: docker/login-action@v4
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
-
- - name: Set up values for ${{ matrix.target }}
- id: set-values
- run: |
- if [[ "${{ github.event.inputs.version }}" != "" ]]; then
- echo "Input version provided"
- VERSION=${{ github.event.inputs.version }}
- elif [[ ${{ github.ref_type }} == "tag" ]]; then
- echo "This is a tag event"
- VERSION=${{ github.ref_name }}
- else
- echo "No version provided and no tag found."
- exit 1
- fi
- echo "VERSION=$VERSION" >> $GITHUB_ENV
-
- if [[ "${{ github.event.inputs.repository-name }}" != "" ]]; then
- REPONAME=${{ github.event.inputs.repository-name }}
- else
- REPONAME="elementsproject"
- fi
- echo "REPONAME=$REPONAME" >> $GITHUB_ENV
-
- if [[ "${{ github.event.inputs.platforms-to-build }}" != "" ]]; then
- PLATFORMS=${{ github.event.inputs.platforms-to-build }}
- else
- PLATFORMS="linux/amd64,linux/arm64,linux/arm/v7"
- fi
- echo "PLATFORMS=$PLATFORMS" >> $GITHUB_ENV
-
- if [[ "${{ github.event.inputs.push-latest }}" == "true" ]] ||
- ([[ "${{ github.ref_type }}" == "tag" ]] && [[ ! "$VERSION" =~ rc ]]); then
- echo "Latest true"
- PUSHLATEST="true"
- else
- echo "Latest false"
- PUSHLATEST="false"
- fi
- echo "PUSHLATEST=$PUSHLATEST" >> $GITHUB_ENV
-
- TAGS="$REPONAME/lightningd:$VERSION${{ matrix.tag_suffix }}"
- if [[ "$PUSHLATEST" == "true" ]]; then
- TAGS="$TAGS,$REPONAME/lightningd:latest${{ matrix.tag_suffix }}"
- fi
- echo "TAGS=$TAGS" >> $GITHUB_ENV
-
- - name: Print GitHub Ref Values
- run: |
- echo "TARGET: ${{ matrix.target }}"
- echo "TAG SUFFIX: ${{ matrix.tag_suffix }}"
- echo "GITHUB REF TYPE: ${{ github.ref_type }}"
- echo "GITHUB REF NAME: ${{ github.ref_name }}"
- echo "EVENT INPUT VERSION: ${{ github.event.inputs.version }}"
- echo "EVENT INPUT REPO: ${{ github.event.inputs.repository-name }}"
- echo "EVENT INPUT PLATFORMS: ${{ github.event.inputs.platforms-to-build }}"
- echo "EVENT INPUT PUSH LATEST: ${{ github.event.inputs.push-latest }}"
- echo "ENV VERSION: ${{ env.VERSION }}"
- echo "ENV REPO NAME: ${{ env.REPONAME }}"
- echo "ENV PLATFORMS: ${{ env.PLATFORMS }}"
- echo "ENV PUSH LATEST: ${{ env.PUSHLATEST }}"
- echo "ENV TAGS: ${{ env.TAGS }}"
-
- - name: Build and push Docker tag - ${{ env.TAGS }}
- uses: docker/build-push-action@v7
- with:
- context: .
- file: ./Dockerfile
- target: ${{ matrix.target }}
- push: true
- platforms: ${{ env.PLATFORMS }}
- tags: ${{ env.TAGS }}
- build-args: |
- VERSION=${{ env.VERSION }}
- cache-from: type=gha
- cache-to: type=gha,mode=max
diff --git a/.github/workflows/pypi-build.yml b/.github/workflows/pypi-build.yml
new file mode 100644
index 00000000..83f80eb7
--- /dev/null
+++ b/.github/workflows/pypi-build.yml
@@ -0,0 +1,48 @@
+name: PyPI build
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ required: true
+
+jobs:
+ build:
+ name: Build ${{ matrix.PACKAGE }} 🐍
+ runs-on: ubuntu-22.04
+ timeout-minutes: 120
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - PACKAGE: pyln-client
+ - PACKAGE: pyln-testing
+ - PACKAGE: pyln-proto
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ - name: Set up Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.10'
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v8.1.0
+
+ - name: Update pyln versions
+ run: |
+ make update-pyln-versions NEW_VERSION=${{ inputs.version }}
+
+ - name: Build distribution 📦
+ run: uv build --package ${{ matrix.PACKAGE }}
+
+ - name: Upload distribution artifact
+ uses: actions/upload-artifact@v7
+ with:
+ name: dist-${{ matrix.PACKAGE }}
+ path: dist/
+ if-no-files-found: error
diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml
new file mode 100644
index 00000000..6244891c
--- /dev/null
+++ b/.github/workflows/pypi-publish.yml
@@ -0,0 +1,58 @@
+name: PyPI publish
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ required: true
+ dist-location:
+ type: string
+ required: true
+
+jobs:
+ publish:
+ name: Publish ${{ matrix.PACKAGE }} 🐍
+ runs-on: ubuntu-22.04
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - PACKAGE: pyln-client
+ - PACKAGE: pyln-testing
+ - PACKAGE: pyln-proto
+ steps:
+ - name: Download built distribution
+ uses: actions/download-artifact@v8
+ with:
+ name: dist-${{ matrix.PACKAGE }}
+ path: dist/
+
+ - name: Determine PyPI distribution location
+ id: set-values
+ run: |
+ if [[ "${{ inputs.dist-location }}" != "" ]]; then
+ DISTLOCATION="${{ inputs.dist-location }}"
+ elif [[ "${{ github.ref_type }}" == "tag" ]] && [[ ! "${{ inputs.version }}" =~ rc ]]; then
+ DISTLOCATION="prod"
+ else
+ DISTLOCATION="test"
+ fi
+ echo "DISTLOCATION=$DISTLOCATION" >> "$GITHUB_OUTPUT"
+ echo "EVENT DISTLOCATION: ${{ github.event.inputs.dist-location }}"
+ echo "Resolved dist-location: $DISTLOCATION"
+
+ - name: Install uv
+ uses: astral-sh/setup-uv@v8.1.0
+
+ - name: Publish distribution 📦 to Test PyPI
+ if: github.repository == 'ElementsProject/lightning' && steps.set-values.outputs.DISTLOCATION == 'test'
+ env:
+ UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
+ run: uv publish --publish-url https://test.pypi.org/legacy/ --check-url https://test.pypi.org/simple/ dist/*
+
+ - name: Publish distribution 📦 to PyPI
+ if: github.repository == 'ElementsProject/lightning' && steps.set-values.outputs.DISTLOCATION == 'prod'
+ env:
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
+ run: uv publish dist/*
diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml
deleted file mode 100644
index 346c413f..00000000
--- a/.github/workflows/pypi.yml
+++ /dev/null
@@ -1,96 +0,0 @@
-name: Publish Python 🐍 distributions 📦 to PyPI and TestPyPI
-
-on:
- push:
- tags:
- - 'v[0-9]+.[0-9]+'
- - 'v[0-9]+.[0-9]+.[0-9]+'
- - 'v[0-9]+.[0-9]+[0-9a-z]+'
- workflow_dispatch:
- inputs:
- dist-location:
- description: 'Distribution location (test/prod)'
- default: 'test'
- required: false
-
-jobs:
- deploy:
- name: Build and publish ${{ matrix.package }} 🐍
- runs-on: ubuntu-22.04
- timeout-minutes: 120
- strategy:
- fail-fast: true
- matrix:
- include:
- - PACKAGE: pyln-client
- WORKDIR: contrib/pyln-client
- - PACKAGE: pyln-testing
- WORKDIR: contrib/pyln-testing
- - PACKAGE: pyln-proto
- WORKDIR: contrib/pyln-proto
- steps:
- - name: Checkout repository
- uses: actions/checkout@v6
- with:
- # Need to fetch entire history in order to locate the version tag
- fetch-depth: 0
-
- - name: Check version tag
- run: >-
- git describe --tags --always --dirty=-modded --abbrev=7
-
- - name: Setup Version
- env:
- WORKDIR: ${{ matrix.WORKDIR }}
- run: |
- echo "VERSION=$(git describe --tags --abbrev=0).post$(git describe --tags --abbrev=1 | awk -F "-" '{print $2}')" >> $GITHUB_ENV
-
- - name: Set up values
- id: set-values
- run: |
- if [[ "${{ github.event.inputs.dist-location }}" != "" ]]; then
- DISTLOCATION=${{ github.event.inputs.dist-location }}
- elif [[ "${{ github.ref_type }}" == "tag" ]] && [[ ! "${{ github.ref_name }}" =~ rc ]]; then
- DISTLOCATION="prod"
- else
- DISTLOCATION="test"
- fi
- echo "DISTLOCATION=$DISTLOCATION" >> $GITHUB_OUTPUT
- echo "EVENT DISTLOCATION: ${{ github.event.inputs.dist-location }}"
- echo "DISTRIBUTION LOCATION: $DISTLOCATION"
-
- - name: Set up Python
- uses: actions/setup-python@v6
- with:
- python-version: '3.10'
-
- - name: Install uv
- uses: astral-sh/setup-uv@v8.1.0
-
- - name: Update pyln versions
- id: update-versions
- run: |
- export VERSION=$(git describe --tags --abbrev=0)
- echo "Pyln VERSION: $VERSION"
- make update-pyln-versions NEW_VERSION=$VERSION
-
- - name: Publish distribution 📦 to Test PyPI
- if: github.repository == 'ElementsProject/lightning' && steps.set-values.outputs.DISTLOCATION == 'test'
- env:
- UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
- WORKDIR: ${{ matrix.WORKDIR }}
- run: |
- echo "Pyln VERSION: $VERSION"
- uv build --package ${{ matrix.PACKAGE }}
- uv publish --publish-url https://test.pypi.org/legacy/ --check-url https://test.pypi.org/simple/
-
- - name: Publish distribution 📦 to PyPI
- if: github.repository == 'ElementsProject/lightning' && steps.set-values.outputs.DISTLOCATION == 'prod'
- env:
- UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
- WORKDIR: ${{ matrix.WORKDIR }}
- run: |
- echo "UV VERSION PUBLISH: $(uv --version)"
- echo "Pyln VERSION: $VERSION"
- uv build --package ${{ matrix.PACKAGE }}
- uv publish
diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml
new file mode 100644
index 00000000..b99965dc
--- /dev/null
+++ b/.github/workflows/release-build.yml
@@ -0,0 +1,67 @@
+name: Release binaries build
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ required: true
+ skip_validation:
+ type: boolean
+ required: true
+
+jobs:
+ releases:
+ name: Releases
+ runs-on: ubuntu-24.04
+ strategy:
+ fail-fast: false
+ matrix:
+ target:
+ - 'bin-Fedora'
+ - 'bin-Ubuntu-jammy'
+ - 'bin-Ubuntu-noble'
+ - 'bin-Ubuntu-resolute'
+ steps:
+ - name: Git checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ - name: Build environment setup
+ run: |
+ distribution=$(echo ${{ matrix.target }} | cut -d'-' -f3)
+ sudo docker run --rm -v $(pwd):/build ubuntu:${distribution} bash -c "\
+ apt-get update && \
+ apt-get install -y debootstrap && \
+ debootstrap ${distribution} /build/${distribution}"
+ sudo tar -C ${distribution} -c . | docker import - ${distribution}
+ docker build -t cl-repro-${distribution} - < contrib/reprobuild/Dockerfile.${distribution}
+ if: contains(matrix.target, 'Ubuntu')
+
+ - name: Build release
+ run: |
+ if [[ "${{ inputs.skip_validation }}" == "true" ]]; then
+ tools/build-release.sh ${{ matrix.target }} --force-version "${{ inputs.version }}" --force-unclean --force-mtime "$(date +%Y-%m-%d)"
+ else
+ tools/build-release.sh ${{ matrix.target }}
+ fi
+
+ - name: Upload target artifacts
+ uses: actions/upload-artifact@v7
+ with:
+ path: release/
+ name: ${{ matrix.target }}
+ if-no-files-found: error
+
+ artifact:
+ name: Construct release artifact
+ needs: releases
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Merge artifacts
+ uses: actions/upload-artifact/merge@v7
+ with:
+ name: c-lightning-${{ inputs.version }}
+ pattern: bin-*
+ delete-merged: true
diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml
new file mode 100644
index 00000000..be7146ab
--- /dev/null
+++ b/.github/workflows/release-publish.yml
@@ -0,0 +1,69 @@
+name: Release publish
+
+on:
+ workflow_call:
+ inputs:
+ version:
+ type: string
+ required: true
+ create_release:
+ type: boolean
+ required: true
+
+jobs:
+ release:
+ name: Sign and prepare release draft
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Git checkout
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ - name: Download artifact
+ uses: actions/download-artifact@v8
+ with:
+ name: c-lightning-${{ inputs.version }}
+ path: release/
+
+ - name: Import GPG keys
+ id: gpg
+ uses: crazy-max/ghaction-import-gpg@v7
+ with:
+ gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+ passphrase: ${{ secrets.GPG_PASSPHRASE }}
+ trust_level: 5
+
+ - name: Set default GPG key
+ run: echo "default-key ${{ steps.gpg.outputs.keyid }}" >> ~/.gnupg/gpg.conf
+
+ - name: Sign release
+ run: tools/build-release.sh --without-zip sign
+
+ - name: Upload signed artifact
+ uses: actions/upload-artifact@v7
+ with:
+ name: c-lightning-${{ inputs.version }}
+ overwrite: true
+ path: release/
+
+ - name: Determine release data
+ id: release_data
+ run: |
+ CHANGELOG_VERSION=${VERSION#v}
+ VERSION="${{ inputs.version }}"
+ CHANGELOG_VERSION=${VERSION#v}
+ CHANGELOG_TITLE=$(grep "## \[${CHANGELOG_VERSION}\]" CHANGELOG.md)
+ RELEASE_TITLE=$(echo $CHANGELOG_TITLE | cut -d'"' -f2)
+ echo "release_title=$RELEASE_TITLE" >> "$GITHUB_OUTPUT"
+
+ - name: Prepare release draft
+ if: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.create_release) }}
+ uses: softprops/action-gh-release@v3
+ with:
+ name: "${{ inputs.version }} ${{ steps.release_data.outputs.release_title }}"
+ tag_name: ${{ inputs.version }}
+ draft: true
+ prerelease: contains(inputs.version, "-rc")
+ files: release/*
+ fail_on_unmatched_files: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 82426484..7bb09de6 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -7,6 +7,7 @@ on:
- 'v[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+[0-9a-z]+'
+ - 'v[0-9]+.[0-9]+.[0-9]+[0-9a-z]+'
workflow_dispatch:
inputs:
version:
@@ -20,173 +21,91 @@ on:
description: Create a draft release
default: false
type: boolean
+ dh-repository-owner:
+ description: 'Dockerhub repository owner'
+ default: 'elementsproject'
+ platforms-to-build:
+ description: 'List of platforms to build'
+ default: 'linux/amd64,linux/arm64,linux/arm/v7'
+ push-latest:
+ description: 'Push the latest Docker tag'
+ type: boolean
+ default: false
+ dist-location:
+ description: 'PyPI distribution location'
+ type: choice
+ options:
+ - test
+ - prod
+ default: 'test'
jobs:
- check:
- name: Check
- outputs:
- version: ${{ steps.capture.outputs.version }}
- runs-on: ubuntu-24.04
- steps:
- - name: Git checkout
- uses: actions/checkout@v6
- with:
- ref: ${{ github.ref }}
- fetch-depth: 0
-
- - name: Determine version
- run: |
- if [[ "${{ github.event.inputs.version }}" != "" ]]; then
- VERSION="${{ github.event.inputs.version }}"
- elif [ "${{ github.ref_type }}" == "tag" ]; then
- VERSION="${{ github.ref_name }}"
- else
- echo "No release version provided and no tag found."
- exit 1
- fi
- echo "VERSION=$VERSION" >> "$GITHUB_ENV"
- echo "Determined version: $VERSION"
-
- - name: Validate release
- if: ${{ !(github.event_name == 'workflow_dispatch' && github.event.inputs.skip_validation) }}
- run: tools/check-release.sh --version=${VERSION}
-
- - name: Catpure version output
- id: capture
- run: echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
-
- releases:
- name: Releases
- needs: check
- env:
- version: ${{ needs.check.outputs.version }}
+ check-version:
+ name: Verify release tag
+ uses: ./.github/workflows/check-release-tag.yml
+ with:
+ version: ${{ github.event.inputs.version }}
+ skip_validation: ${{ github.event.inputs.skip_validation == 'true' }}
+
+
+ docker-build:
+ name: Docker build
+ needs: check-version
+ uses: ./.github/workflows/docker-build.yml
+ with:
+ version: ${{ needs.check-version.outputs.version }}
+ dh-repository-owner: ${{ github.event.inputs.dh-repository-owner || 'elementsproject' }}
+ platforms-to-build: ${{ github.event.inputs.platforms-to-build || 'linux/amd64,linux/arm64,linux/arm/v7' }}
+ secrets: inherit
+
+ pypi-build:
+ name: PyPI build
+ needs: check-version
+ uses: ./.github/workflows/pypi-build.yml
+ with:
+ version: ${{ needs.check-version.outputs.version }}
+ secrets: inherit
+
+ release-build:
+ name: Release binaries build
+ needs: check-version
+ uses: ./.github/workflows/release-build.yml
+ with:
+ version: ${{ needs.check-version.outputs.version }}
+ skip_validation: ${{ github.event.inputs.skip_validation == 'true' }}
+ secrets: inherit
+
+ gate:
+ name: All builds succeeded
+ needs: [docker-build, pypi-build, release-build]
runs-on: ubuntu-24.04
- strategy:
- fail-fast: false # Let each build finish.
- matrix:
- target:
- - 'bin-Fedora'
- - 'bin-Ubuntu-jammy'
- - 'bin-Ubuntu-noble'
- - 'bin-Ubuntu-resolute'
steps:
- - name: Git checkout
- uses: actions/checkout@v6
- with:
- ref: ${{ github.ref }}
- fetch-depth: 0
-
- - name: Build environment setup
- run: |
- distribution=$(echo ${{ matrix.target }} | cut -d'-' -f3)
- echo "Building base image for ${distribution}"
- sudo docker run --rm -v $(pwd):/build ubuntu:${distribution} bash -c "\
- apt-get update && \
- apt-get install -y debootstrap && \
- debootstrap ${distribution} /build/${distribution}"
- sudo tar -C ${distribution} -c . | docker import - ${distribution}
-
- # Build Docker image
- docker build -t cl-repro-${distribution} - < contrib/reprobuild/Dockerfile.${distribution}
- if: contains(matrix.target, 'Ubuntu')
-
- - name: Build release
- run: |
- # Allow build release to execute if manually triggered for testing
- if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.skip_validation }}" == "true" ]]; then
- tools/build-release.sh ${{ matrix.target }} --force-version "${{ env.version }}" --force-unclean --force-mtime "$(date +%Y-%m-%d)"
- else
- tools/build-release.sh ${{ matrix.target }}
- fi
-
- - name: Upload target artifacts
- uses: actions/upload-artifact@v7
- with:
- path: release/
- name: ${{ matrix.target }}
- if-no-files-found: error
-
- artifact:
- name: Construct release artifact
- needs:
- - check
- - releases
- env:
- version: ${{ needs.check.outputs.version }}
- runs-on: ubuntu-24.04
- steps:
- - name: Merge artifacts
- uses: actions/upload-artifact/merge@v7
- with:
- name: c-lightning-${{ env.version }}
- pattern: bin-*
- delete-merged: true
-
- release:
- name: Sign and prepare release draft
- needs:
- - check
- - artifact
- env:
- version: ${{ needs.check.outputs.version }}
- runs-on: ubuntu-24.04
- steps:
- - name: Git checkout
- uses: actions/checkout@v6
- with:
- ref: ${{ github.ref }}
- fetch-depth: 0
-
- - name: Download artifact
- uses: actions/download-artifact@v8
- with:
- name: c-lightning-${{ env.version }}
- path: release/
-
- - name: Import GPG keys
- id: gpg
- uses: crazy-max/ghaction-import-gpg@v7
- with:
- gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
- passphrase: ${{ secrets.GPG_PASSPHRASE }}
- trust_level: 5
-
- - name: Set default GPG key
- run: echo "default-key ${{ steps.gpg.outputs.keyid }}" >> ~/.gnupg/gpg.conf
-
- - name: Sign release
- run: tools/build-release.sh --without-zip sign
-
- - name: Upload signed artifact
- uses: actions/upload-artifact@v7
- with:
- name: c-lightning-${{ env.version }}
- overwrite: true
- path: release/
-
- - name: Determine release data
- id: release_data
- run: |
- VERSION=${{ env.version }}
- CHANGELOG_VERSION=${VERSION#v}
- echo "CHANGELOG_VERSION=$CHANGELOG_VERSION"
- echo "changelog_version=$CHANGELOG_VERSION" >> "$GITHUB_OUTPUT"
-
- CHANGELOG_TITLE=$(grep "## \[${CHANGELOG_VERSION}\]" CHANGELOG.md)
- echo "CHANGELOG_TITLE=$CHANGELOG_TITLE"
- echo "changelog_title=$CHANGELOG_TITLE" >> "$GITHUB_OUTPUT"
-
- RELEASE_TITLE=$(echo $CHANGELOG_TITLE | cut -d'"' -f2)
- echo "RELEASE_TITLE=$RELEASE_TITLE"
- echo "release_title=$RELEASE_TITLE" >> "$GITHUB_OUTPUT"
-
- - name: Prepare release draft
- if: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.create_release) }}
- uses: softprops/action-gh-release@v3
- with:
- name: "${{ env.version }} ${{ steps.release_data.outputs.release_title }}"
- tag_name: ${{ env.version }}
- draft: true
- prerelease: contains(env.version, "-rc")
- files: release/*
- fail_on_unmatched_files: true
+ - run: echo "All three build paths succeeded — proceeding to publish."
+
+ docker-publish:
+ name: Docker publish
+ needs: [check-version, gate]
+ uses: ./.github/workflows/docker-publish.yml
+ with:
+ version: ${{ needs.check-version.outputs.version }}
+ dh-repository-owner: ${{ github.event.inputs.dh-repository-owner || 'elementsproject' }}
+ push-latest: ${{ github.event.inputs.push-latest }}
+ secrets: inherit
+
+ pypi-publish:
+ name: PyPI publish
+ needs: [check-version, gate]
+ uses: ./.github/workflows/pypi-publish.yml
+ with:
+ version: ${{ needs.check-version.outputs.version }}
+ dist-location: ${{ github.event.inputs.dist-location }}
+ secrets: inherit
+
+ release-publish:
+ name: Release sign + draft
+ needs: [check-version, gate]
+ uses: ./.github/workflows/release-publish.yml
+ with:
+ version: ${{ needs.check-version.outputs.version }}
+ create_release: ${{ github.event.inputs.create_release == 'true' }}
+ secrets: inherit
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.