mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 14:20:56 +00:00
chore: Publish debian/rpm packages from GitHub directly (#8031)
This commit is contained in:
@@ -250,6 +250,8 @@ words:
|
||||
- Raphson
|
||||
- rcflags
|
||||
- replayer
|
||||
- repodata
|
||||
- repomd
|
||||
- rerandomize
|
||||
- rerandomization
|
||||
- rerandomized
|
||||
@@ -290,6 +292,7 @@ words:
|
||||
- sles
|
||||
- soci
|
||||
- socidb
|
||||
- Sonatype
|
||||
- sponsee
|
||||
- sponsees
|
||||
- SRPMS
|
||||
|
||||
44
.github/actions/generate-version/action.yml
vendored
44
.github/actions/generate-version/action.yml
vendored
@@ -1,44 +0,0 @@
|
||||
name: Generate build version number
|
||||
description: "Generate build version number."
|
||||
|
||||
outputs:
|
||||
version:
|
||||
description: "The generated build version number."
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# When a tag is pushed, the version is used as-is.
|
||||
- name: Generate version for tag event
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: bash
|
||||
env:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
run: echo "VERSION=${VERSION}" >>"${GITHUB_ENV}"
|
||||
|
||||
# When a tag is not pushed, then the version (e.g. 1.2.3-b0) is extracted
|
||||
# from the BuildInfo.cpp file and the shortened commit hash appended to it.
|
||||
# We use a plus sign instead of a hyphen because Conan recipe versions do
|
||||
# not support two hyphens.
|
||||
- name: Generate version for non-tag event
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'Extracting version from BuildInfo.cpp.'
|
||||
VERSION="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')"
|
||||
if [[ -z "${VERSION}" ]]; then
|
||||
echo 'Unable to extract version from BuildInfo.cpp.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo 'Appending shortened commit hash to version.'
|
||||
SHA='${{ github.sha }}'
|
||||
VERSION="${VERSION}+${SHA:0:7}"
|
||||
|
||||
echo "VERSION=${VERSION}" >>"${GITHUB_ENV}"
|
||||
|
||||
- name: Output version
|
||||
id: version
|
||||
shell: bash
|
||||
run: echo "version=${VERSION}" >>"${GITHUB_OUTPUT}"
|
||||
90
.github/actions/release-info/action.yml
vendored
Normal file
90
.github/actions/release-info/action.yml
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
name: Release info
|
||||
description: "Derive the version, release channel and package release number for this build."
|
||||
|
||||
outputs:
|
||||
version:
|
||||
description: "The build version number."
|
||||
value: ${{ steps.version.outputs.version }}
|
||||
channel:
|
||||
description: "The release channel this build belongs to."
|
||||
value: ${{ steps.channel.outputs.channel }}
|
||||
pkg_release:
|
||||
description: "The package release number: 1 for a tag, the run number otherwise."
|
||||
value: ${{ steps.pkg_release.outputs.pkg_release }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
# A tag names its own version. Anything else takes it from BuildInfo.cpp and
|
||||
# appends the commit hash as build metadata, joined with a plus sign because a
|
||||
# Conan version cannot contain two hyphens.
|
||||
- name: Determine version
|
||||
id: version
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
if [[ "${IS_TAG}" == "true" ]]; then
|
||||
version="${REF_NAME}"
|
||||
else
|
||||
version="$(awk -F'"' '/versionString =/ { print $2 }' src/libxrpl/protocol/BuildInfo.cpp)"
|
||||
if [[ -z "${version}" ]]; then
|
||||
echo "Unable to read versionString from BuildInfo.cpp." >&2
|
||||
exit 1
|
||||
fi
|
||||
version="${version}+${SHA:0:7}"
|
||||
fi
|
||||
|
||||
echo "version=${version}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
# Only a tag says how mature a build is: a push is a develop build whatever
|
||||
# its version, and a non-public codebase keeps its packages to itself.
|
||||
- name: Determine release channel
|
||||
id: channel
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
REF_NAME: ${{ github.ref_name }}
|
||||
VISIBILITY: ${{ github.event.repository.visibility }}
|
||||
run: |
|
||||
pre_release=""
|
||||
if [[ "${REF_NAME}" == *-* ]]; then
|
||||
pre_release="${REF_NAME#*-}"
|
||||
fi
|
||||
|
||||
if [[ "${VISIBILITY}" != "public" ]]; then
|
||||
channel=private
|
||||
elif [[ "${IS_TAG}" != "true" ]]; then
|
||||
channel=develop
|
||||
elif [[ -z "${pre_release}" ]]; then
|
||||
channel=stable
|
||||
elif [[ "${pre_release}" =~ ^rc[0-9]+(\+.*)?$ ]]; then
|
||||
channel=unstable
|
||||
elif [[ "${pre_release}" =~ ^b(0|[1-9][0-9]*)(\+.*)?$ ]]; then
|
||||
channel=experimental
|
||||
else
|
||||
echo "Unsupported pre-release in tag '${REF_NAME}'. Use bN or rcN." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "channel=${channel}" | tee -a "${GITHUB_OUTPUT}"
|
||||
|
||||
# A tag is packaged once, so its release number is fixed at 1. Develop builds
|
||||
# repeat the same version, so the run number is what makes each push an
|
||||
# upgrade rather than a reinstall.
|
||||
- name: Determine package release
|
||||
id: pkg_release
|
||||
shell: bash
|
||||
env:
|
||||
IS_TAG: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
RUN_NUMBER: ${{ github.run_number }}
|
||||
run: |
|
||||
if [[ "${IS_TAG}" == "true" ]]; then
|
||||
pkg_release=1
|
||||
else
|
||||
pkg_release="${RUN_NUMBER}"
|
||||
fi
|
||||
|
||||
echo "pkg_release=${pkg_release}" | tee -a "${GITHUB_OUTPUT}"
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -4,7 +4,7 @@ updates:
|
||||
directories:
|
||||
- /
|
||||
- .github/actions/build-deps/
|
||||
- .github/actions/generate-version/
|
||||
- .github/actions/release-info/
|
||||
- .github/actions/set-compiler-env/
|
||||
- .github/actions/setup-conan/
|
||||
schedule:
|
||||
|
||||
4
.github/scripts/strategy-matrix/linux.json
vendored
4
.github/scripts/strategy-matrix/linux.json
vendored
@@ -92,7 +92,7 @@
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-577d745"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-debian:sha-028ccea"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
"build_type": ["Release"],
|
||||
"arch": ["amd64"],
|
||||
"minimal": false,
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-577d745"
|
||||
"image": "ghcr.io/xrplf/xrpld/packaging-rhel:sha-028ccea"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
2
.github/workflows/on-pr.yml
vendored
2
.github/workflows/on-pr.yml
vendored
@@ -77,7 +77,7 @@ jobs:
|
||||
|
||||
# Keep the paths below in sync with those in `on-trigger.yml`.
|
||||
.github/actions/build-deps/**
|
||||
.github/actions/generate-version/**
|
||||
.github/actions/release-info/**
|
||||
.github/actions/setup-conan/**
|
||||
.github/scripts/strategy-matrix/**
|
||||
.github/workflows/reusable-build-test-config.yml
|
||||
|
||||
17
.github/workflows/on-tag.yml
vendored
17
.github/workflows/on-tag.yml
vendored
@@ -1,5 +1,9 @@
|
||||
# This workflow uploads the libxrpl recipe to the Conan remote and builds
|
||||
# release packages when a versioned tag is pushed.
|
||||
# When a versioned tag is pushed, this workflow:
|
||||
#
|
||||
# - uploads the libxrpl recipe to the Conan remote
|
||||
# - builds and tests the release binaries
|
||||
# - builds the DEB and RPM packages
|
||||
# - publishes those packages to the XRPLF package repositories
|
||||
name: Tag
|
||||
|
||||
on:
|
||||
@@ -24,7 +28,7 @@ jobs:
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
|
||||
build-test:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
if: ${{ github.repository_owner == 'XRPLF' }}
|
||||
uses: ./.github/workflows/reusable-build-test.yml
|
||||
strategy:
|
||||
fail-fast: true
|
||||
@@ -37,6 +41,11 @@ jobs:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
package:
|
||||
if: ${{ github.repository == 'XRPLF/rippled' }}
|
||||
if: ${{ github.repository_owner == 'XRPLF' }}
|
||||
needs: build-test
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
with:
|
||||
publish: true
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
|
||||
9
.github/workflows/on-trigger.yml
vendored
9
.github/workflows/on-trigger.yml
vendored
@@ -15,7 +15,7 @@ on:
|
||||
|
||||
# Keep the paths below in sync with those in `on-pr.yml`.
|
||||
- ".github/actions/build-deps/**"
|
||||
- ".github/actions/generate-version/**"
|
||||
- ".github/actions/release-info/**"
|
||||
- ".github/actions/setup-conan/**"
|
||||
- ".github/scripts/strategy-matrix/**"
|
||||
- ".github/workflows/reusable-build-test-config.yml"
|
||||
@@ -108,3 +108,10 @@ jobs:
|
||||
package:
|
||||
needs: build-test
|
||||
uses: ./.github/workflows/reusable-package.yml
|
||||
with:
|
||||
# Packages are built on every trigger; only develop pushes in XRPLF/rippled
|
||||
# publish them, matching upload-recipe above.
|
||||
publish: ${{ github.repository == 'XRPLF/rippled' && github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||
secrets:
|
||||
remote_username: ${{ secrets.NEXUS_REMOTE_USERNAME }}
|
||||
remote_password: ${{ secrets.NEXUS_REMOTE_PASSWORD }}
|
||||
|
||||
@@ -111,6 +111,9 @@ jobs:
|
||||
VOIDSTAR_ENABLED: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }}
|
||||
VALIDATOR_KEYS_ENABLED: ${{ contains(inputs.cmake_args, '-Dvalidator_keys=ON') }}
|
||||
SANITIZERS_ENABLED: ${{ inputs.sanitizers != '' }}
|
||||
# The binaries reusable-package.yml consumes. A private repository skips
|
||||
# them except on a tag push, which is what produces its release packages.
|
||||
PACKAGING_ARTIFACTS_ENABLED: ${{ github.event.repository.visibility == 'public' || startsWith(github.ref, 'refs/tags/') }}
|
||||
steps:
|
||||
- name: Cleanup workspace (macOS and Windows)
|
||||
if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }}
|
||||
@@ -222,7 +225,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Upload the binary (Linux)
|
||||
if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }}
|
||||
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: xrpld-${{ inputs.config_name }}
|
||||
@@ -236,7 +239,7 @@ jobs:
|
||||
run: ./validator-keys --unittest
|
||||
|
||||
- name: Upload the validator-keys binary
|
||||
if: ${{ github.event.repository.visibility == 'public' && env.VALIDATOR_KEYS_ENABLED == 'true' }}
|
||||
if: ${{ env.PACKAGING_ARTIFACTS_ENABLED == 'true' && env.VALIDATOR_KEYS_ENABLED == 'true' }}
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: validator-keys-${{ inputs.config_name }}
|
||||
|
||||
49
.github/workflows/reusable-package.yml
vendored
49
.github/workflows/reusable-package.yml
vendored
@@ -1,17 +1,34 @@
|
||||
# Build Linux packages (DEB and RPM) from pre-built binary artifacts (xrpld and
|
||||
# validator-keys). Discovers which configurations to package from linux.json
|
||||
# (configs in "package_configs") and fans out one job per distro. Only
|
||||
# linux/amd64 is supported; the runner is hardcoded in the job below.
|
||||
# Build Linux packages from the pre-built xrpld and validator-keys artifacts:
|
||||
#
|
||||
# - one job per distro, taken from "package_configs" in linux.json
|
||||
# - each job runs in that distro's container, which is what decides DEB or RPM
|
||||
# - with 'publish: true' a job also uploads what it built
|
||||
# (see package/publish_pkg.sh)
|
||||
#
|
||||
# Only linux/amd64 is supported; the runner is hardcoded in the job below.
|
||||
name: Package
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
pkg_release:
|
||||
description: "Package release number. Increment when repackaging the same executable."
|
||||
publish:
|
||||
description: "Whether to publish the packages after building them."
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
nexus_url:
|
||||
description: "The base URL of the Nexus instance hosting the deb and rpm repositories."
|
||||
required: false
|
||||
type: string
|
||||
default: "1"
|
||||
default: https://packages.xrplf.org
|
||||
|
||||
secrets:
|
||||
remote_username:
|
||||
description: "The username of a Nexus account with write access to the repositories."
|
||||
required: false
|
||||
remote_password:
|
||||
description: "The password or token for that Nexus account."
|
||||
required: false
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -41,7 +58,7 @@ jobs:
|
||||
|
||||
package:
|
||||
needs: [generate-matrix]
|
||||
if: ${{ github.event.repository.visibility == 'public' }}
|
||||
if: ${{ github.event.repository.visibility == 'public' || startsWith(github.ref, 'refs/tags/') }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
|
||||
@@ -71,9 +88,14 @@ jobs:
|
||||
- name: Make binaries executable
|
||||
run: chmod +x "${BUILD_DIR}/xrpld" "${BUILD_DIR}/validator-keys"
|
||||
|
||||
- name: Determine release info
|
||||
id: release_info
|
||||
uses: ./.github/actions/release-info
|
||||
|
||||
- name: Build package
|
||||
env:
|
||||
PKG_RELEASE: ${{ inputs.pkg_release }}
|
||||
PKG_RELEASE: ${{ steps.release_info.outputs.pkg_release }}
|
||||
PKG_CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
run: ./package/build_pkg.sh
|
||||
|
||||
- name: Upload package artifact
|
||||
@@ -85,3 +107,12 @@ jobs:
|
||||
${{ env.BUILD_DIR }}/debbuild/*.ddeb
|
||||
${{ env.BUILD_DIR }}/rpmbuild/RPMS/**/*.rpm
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Publish package
|
||||
if: ${{ inputs.publish }}
|
||||
env:
|
||||
CHANNEL: ${{ steps.release_info.outputs.channel }}
|
||||
NEXUS_URL: ${{ inputs.nexus_url }}
|
||||
NEXUS_USERNAME: ${{ secrets.remote_username }}
|
||||
NEXUS_PASSWORD: ${{ secrets.remote_password }}
|
||||
run: ./package/publish_pkg.sh "${CHANNEL}" "${BUILD_DIR}"
|
||||
|
||||
12
.github/workflows/reusable-upload-recipe.yml
vendored
12
.github/workflows/reusable-upload-recipe.yml
vendored
@@ -49,9 +49,9 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
|
||||
- name: Generate build version number
|
||||
id: version
|
||||
uses: ./.github/actions/generate-version
|
||||
- name: Determine release info
|
||||
id: release_info
|
||||
uses: ./.github/actions/release-info
|
||||
|
||||
- name: Set up Conan
|
||||
uses: ./.github/actions/setup-conan
|
||||
@@ -64,8 +64,8 @@ jobs:
|
||||
|
||||
- name: Upload Conan recipe (version)
|
||||
run: |
|
||||
conan export . --version=${{ steps.version.outputs.version }}
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.version.outputs.version }}
|
||||
conan export . --version=${{ steps.release_info.outputs.version }}
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/${{ steps.release_info.outputs.version }}
|
||||
|
||||
# When this workflow is triggered by a push event, it will always be when merging into the
|
||||
# 'develop' branch, see on-trigger.yml.
|
||||
@@ -92,4 +92,4 @@ jobs:
|
||||
conan upload --confirm --check --remote="${REMOTE_NAME}" xrpl/release
|
||||
|
||||
outputs:
|
||||
ref: xrpl/${{ steps.version.outputs.version }}
|
||||
ref: xrpl/${{ steps.release_info.outputs.version }}
|
||||
|
||||
@@ -8,7 +8,8 @@ a build configured with `-Dvalidator_keys=ON`.
|
||||
|
||||
```
|
||||
package/
|
||||
build_pkg.sh Staging and build script (called by the CMake `package` target and CI)
|
||||
build_pkg.sh Staging and build script (called by the CMake `package` target and CI)
|
||||
publish_pkg.sh Uploads built packages to the XRPLF Nexus repositories (called by CI)
|
||||
rpm/
|
||||
xrpld.spec RPM spec
|
||||
debian/ Debian control files (control, rules, copyright, xrpld.docs, xrpld.links, source/format)
|
||||
@@ -87,7 +88,7 @@ docker run --rm \
|
||||
./package/build_pkg.sh --pkg-release "${PKG_RELEASE}"
|
||||
|
||||
# Output:
|
||||
# build/debbuild/*.deb (DEB + dbgsym .ddeb)
|
||||
# build/debbuild/*.deb (DEB + dbgsym; Debian names both .deb)
|
||||
# build/rpmbuild/RPMS/x86_64/*.rpm
|
||||
```
|
||||
|
||||
@@ -120,6 +121,50 @@ The package version is not a CMake input on this path: `build_pkg.sh` derives it
|
||||
from the just-built `xrpld` binary's `xrpld --version` output. The package
|
||||
release defaults to 1 and is overridable with `-Dpkg_release=N`.
|
||||
|
||||
## Publishing packages
|
||||
|
||||
Packages are published to the XRPLF repositories on Sonatype Nexus at
|
||||
`https://packages.xrplf.org`. The `release-info` action decides the channel from
|
||||
the event, and `publish_pkg.sh` maps that channel to a repository pair:
|
||||
|
||||
| Event | Version | Channel | DEB repository | RPM repository |
|
||||
| ------------------------ | ----------------- | -------------- | ------------------ | ------------------ |
|
||||
| tag | `X.Y.Z` | `stable` | `deb-stable` | `rpm-stable` |
|
||||
| tag | `X.Y.Z-rcN` | `unstable` | `deb-unstable` | `rpm-unstable` |
|
||||
| tag | `X.Y.Z-bN` | `experimental` | `deb-experimental` | `rpm-experimental` |
|
||||
| push to `develop` | `xrpld --version` | `develop` | `deb-develop` | `rpm-develop` |
|
||||
| tag, non-public codebase | _any_ | `private` | `deb-private` | `rpm-private` |
|
||||
|
||||
Only a tag names a channel — do not extend that to `develop`, where
|
||||
`BuildInfo.cpp`'s `versionString` moves through `-bN`, `-rcN` and even the final
|
||||
version during a release cycle, which would send develop builds into `stable`.
|
||||
Versions sort in row order, so moving to a more mature channel never downgrades.
|
||||
|
||||
The action decides the package release number on the same split: a tag's version
|
||||
is unique, so its packages are release 1, while develop repeats the same version
|
||||
and takes `github.run_number` so each push supersedes the last. Both reach the
|
||||
packaging scripts as arguments, so neither script derives anything itself.
|
||||
|
||||
Publishing is the last step of each packaging job, uploading from the container
|
||||
that built the packages. It runs when the caller passes `publish: true`:
|
||||
`on-trigger.yml` for develop pushes in `XRPLF/rippled`, `on-tag.yml` for tags in
|
||||
any `XRPLF` repository, `on-pr.yml` never. Both authenticate with the
|
||||
`NEXUS_REMOTE_USERNAME` / `NEXUS_REMOTE_PASSWORD` secrets already used for the
|
||||
Conan remote.
|
||||
|
||||
Nexus owns the repository metadata; nothing here signs or indexes anything. Worth
|
||||
knowing:
|
||||
|
||||
- Each apt-hosted repository needs a distribution and a PGP signing keypair
|
||||
configured in Nexus, which rejects one created without a keypair.
|
||||
- yum metadata is rebuilt asynchronously, so a successful publish is not
|
||||
immediately installable.
|
||||
- Each job uploads only what it built, and uploads are not transactional, so a
|
||||
failure can leave one format published alone. Re-running is safe: both the apt
|
||||
POST and the yum PUT replace an existing asset.
|
||||
- The `develop` repositories gain a package per push, so they need a cleanup
|
||||
policy to stay bounded; tagged channels publish each version once.
|
||||
|
||||
## How `build_pkg.sh` works
|
||||
|
||||
`build_pkg.sh` derives the `xrpld` software version from
|
||||
@@ -151,10 +196,9 @@ With `PKG_RELEASE=1`, the package metadata becomes:
|
||||
| `3.2.0-b1` | `3.2.0~b1-1%{?dist}` | `3.2.0~b1-1` |
|
||||
| `3.2.0-rc1` | `3.2.0~rc1-1%{?dist}` | `3.2.0~rc1-1` |
|
||||
|
||||
The Debian changelog entry carries the repository component: final releases use
|
||||
`stable`, `b0` builds, including `b0+metadata`, use `develop`, and `bN`/`rcN`
|
||||
pre-releases use `unstable`.
|
||||
Build metadata on a final release, such as `3.2.0+abc123`, is rejected.
|
||||
The Debian changelog entry carries the channel passed as `--channel`
|
||||
(`PKG_CHANNEL`), defaulting to `unstable`. An unsupported pre-release, and build
|
||||
metadata on a final release such as `3.2.0+abc123`, are both rejected.
|
||||
|
||||
The RPM path intentionally uses `~` in `Version`, matching the Debian
|
||||
pre-release ordering convention, so RPM filenames/NVRs begin with forms like
|
||||
@@ -209,17 +253,20 @@ service restart.
|
||||
5. Generates a minimal `debian/changelog` using `${pkg_version}-${PKG_RELEASE}`,
|
||||
where `pkg_version` is derived from the binary-reported `xrpld` version.
|
||||
6. Runs `dpkg-buildpackage -b --no-sign -d` (`-d` skips the build-dependency check, since the binary is already built). `debian/rules` uses manual `install` commands.
|
||||
7. Output: `debbuild/*.deb` and `debbuild/*.ddeb` (dbgsym package)
|
||||
7. Output: `debbuild/*.deb`, the binary package and the `-dbgsym` package.
|
||||
Debian gives dbgsym packages a `.deb` extension; only Ubuntu uses `.ddeb`.
|
||||
|
||||
## Post-build verification
|
||||
|
||||
```bash
|
||||
# DEB
|
||||
dpkg-deb -c debbuild/*.deb | grep -E 'systemd|sysusers|tmpfiles'
|
||||
lintian -I debbuild/*.deb
|
||||
|
||||
# RPM
|
||||
rpm -qlp rpmbuild/RPMS/x86_64/*.rpm
|
||||
|
||||
# Optional, and not in the packaging image: apt-get install -y lintian
|
||||
lintian -I debbuild/*.deb
|
||||
```
|
||||
|
||||
## Reproducibility
|
||||
|
||||
@@ -16,6 +16,8 @@ Options (each can also be set via the env var shown):
|
||||
xrpld and validator-keys
|
||||
binaries [BUILD_DIR; default: ${PWD}/build]
|
||||
--pkg-release N package release iteration [PKG_RELEASE; default: 1]
|
||||
--channel NAME release channel, written
|
||||
to debian/changelog [PKG_CHANNEL; default: unstable]
|
||||
--source-date-epoch SECS reproducibility timestamp [SOURCE_DATE_EPOCH; latest git ctime; fallback: current time]
|
||||
-h, --help show this help and exit
|
||||
EOF
|
||||
@@ -32,6 +34,7 @@ need_arg() {
|
||||
SRC_DIR="${SRC_DIR:-}"
|
||||
BUILD_DIR="${BUILD_DIR:-}"
|
||||
PKG_RELEASE="${PKG_RELEASE:-1}"
|
||||
PKG_CHANNEL="${PKG_CHANNEL:-unstable}"
|
||||
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-}"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -51,6 +54,11 @@ while [[ $# -gt 0 ]]; do
|
||||
PKG_RELEASE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--channel)
|
||||
need_arg "$@"
|
||||
PKG_CHANNEL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--source-date-epoch)
|
||||
need_arg "$@"
|
||||
SOURCE_DATE_EPOCH="$2"
|
||||
@@ -198,7 +206,6 @@ stage_common() {
|
||||
|
||||
build_rpm() {
|
||||
local topdir="${BUILD_DIR}/rpmbuild"
|
||||
rm -rf "${topdir}"
|
||||
mkdir -p "${topdir}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
||||
|
||||
cp "${SRC_DIR}/package/rpm/xrpld.spec" "${topdir}/SPECS/xrpld.spec"
|
||||
@@ -214,7 +221,6 @@ build_rpm() {
|
||||
|
||||
build_deb() {
|
||||
local staging="${BUILD_DIR}/debbuild/source"
|
||||
rm -rf "${staging}"
|
||||
mkdir -p "${staging}"
|
||||
|
||||
stage_common "${staging}"
|
||||
@@ -225,25 +231,9 @@ build_deb() {
|
||||
cp "${staging}/xrpld.tmpfiles" "${staging}/debian/xrpld.tmpfiles"
|
||||
cp "${staging}/xrpld.logrotate" "${staging}/debian/xrpld.logrotate"
|
||||
|
||||
# Choose the Debian repository component for this package.
|
||||
# 3.2.0 -> stable, *-b0[+metadata] -> develop,
|
||||
# bN/rcN pre-releases -> unstable.
|
||||
local deb_component
|
||||
if [[ -z "${pre_release}" ]]; then
|
||||
deb_component="stable"
|
||||
elif [[ "${pre_release}" =~ ^b0(\+.*)?$ ]]; then
|
||||
deb_component="develop"
|
||||
elif [[ "${pre_release}" =~ ^(b[1-9][0-9]*|rc[0-9]+)(\+.*)?$ ]]; then
|
||||
deb_component="unstable"
|
||||
else
|
||||
echo "build_pkg.sh: unsupported xrpld pre-release '${pre_release}'." >&2
|
||||
echo "Use bN or rcN, e.g. 3.2.0-b1 or 3.2.0-rc2." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Debian version is <upstream>[~<pre>]-<pkg release>.
|
||||
cat >"${staging}/debian/changelog" <<EOF
|
||||
xrpld (${pkg_version}-${PKG_RELEASE}) ${deb_component}; urgency=medium
|
||||
xrpld (${pkg_version}-${PKG_RELEASE}) ${PKG_CHANNEL}; urgency=medium
|
||||
* Release ${xrpld_version}.
|
||||
|
||||
-- XRPL Foundation <contact@xrplf.org> ${CHANGELOG_DATE}
|
||||
@@ -255,4 +245,8 @@ EOF
|
||||
(cd "${staging}" && dpkg-buildpackage -b --no-sign -d)
|
||||
}
|
||||
|
||||
# Remove both build directories, because a package left from an earlier build
|
||||
# would otherwise be picked up and published alongside this one.
|
||||
rm -rf "${BUILD_DIR}/debbuild" "${BUILD_DIR}/rpmbuild"
|
||||
|
||||
"build_${pkg_type}"
|
||||
|
||||
106
package/publish_pkg.sh
Executable file
106
package/publish_pkg.sh
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Publish the DEB and RPM packages built by build_pkg.sh to the XRPLF package
|
||||
# repositories on Sonatype Nexus.
|
||||
#
|
||||
# Usage: publish_pkg.sh <channel> [package-dir]
|
||||
#
|
||||
# channel release channel, selecting the 'deb-<channel>' and
|
||||
# 'rpm-<channel>' repository pair
|
||||
# package-dir searched recursively for *.deb, *.ddeb and *.rpm ('build' by
|
||||
# default)
|
||||
#
|
||||
# NEXUS_USERNAME and NEXUS_PASSWORD are required. NEXUS_URL overrides the target
|
||||
# instance, and DRY_RUN=1 lists the uploads without performing them.
|
||||
|
||||
channel="${1:-}"
|
||||
pkg_dir="${2:-build}"
|
||||
nexus_url="${NEXUS_URL:-https://packages.xrplf.org}"
|
||||
|
||||
if [[ -z "${channel}" ]]; then
|
||||
echo "usage: publish_pkg.sh <channel> [package-dir]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
deb_repo="deb-${channel}"
|
||||
rpm_repo="rpm-${channel}"
|
||||
|
||||
if [[ -z "${DRY_RUN:-}" ]]; then
|
||||
: "${NEXUS_USERNAME:?is required}" "${NEXUS_PASSWORD:?is required}"
|
||||
fi
|
||||
|
||||
# Deliberate curl choices:
|
||||
#
|
||||
# - no --fail, which would hide the response body where Nexus explains what it
|
||||
# rejected
|
||||
# - no --location, since curl downgrades a redirected POST to GET and turns an
|
||||
# upload into a no-op that still answers 200
|
||||
# - credentials on stdin, to keep them out of the process list
|
||||
upload() {
|
||||
local url="$1"
|
||||
shift
|
||||
[[ -z "${DRY_RUN:-}" ]] || return 0
|
||||
|
||||
local body code status=0
|
||||
body="$(mktemp)"
|
||||
code="$(
|
||||
printf 'user = %s:%s\n' "${NEXUS_USERNAME}" "${NEXUS_PASSWORD}" |
|
||||
curl \
|
||||
--config - \
|
||||
--silent \
|
||||
--show-error \
|
||||
--retry 3 \
|
||||
--retry-delay 5 \
|
||||
--retry-all-errors \
|
||||
--output "${body}" \
|
||||
--write-out '%{http_code}' \
|
||||
"$@" \
|
||||
"${url}"
|
||||
)" || status=$?
|
||||
|
||||
if [[ ${status} -ne 0 || ! "${code}" =~ ^2[0-9][0-9]$ ]]; then
|
||||
echo "publish_pkg.sh: upload failed (curl ${status}, HTTP ${code}): ${url}" >&2
|
||||
cat "${body}" >&2
|
||||
echo >&2
|
||||
rm -f "${body}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "${body}"
|
||||
}
|
||||
|
||||
echo "Publishing ${pkg_dir} to ${deb_repo} and ${rpm_repo} on ${nexus_url}:"
|
||||
|
||||
count=0
|
||||
while IFS= read -r -d '' file; do
|
||||
name="${file##*/}"
|
||||
case "${name}" in
|
||||
# A raw body with a multipart Content-Type, POSTed to the repository root,
|
||||
# is the documented upload for a hosted apt repository:
|
||||
# https://help.sonatype.com/en/apt-repositories.html#deploying-packages-to-hosted-apt-repositories
|
||||
*.deb | *.ddeb)
|
||||
echo " ${name} -> ${deb_repo}"
|
||||
upload "${nexus_url}/repository/${deb_repo}/" \
|
||||
--header 'Content-Type: multipart/form-data' \
|
||||
--data-binary "@${file}"
|
||||
;;
|
||||
# yum repositories are addressed by path; the arch comes from the name.
|
||||
*.rpm)
|
||||
arch="${name%.rpm}"
|
||||
arch="${arch##*.}"
|
||||
echo " ${name} -> ${rpm_repo}/${arch}"
|
||||
upload "${nexus_url}/repository/${rpm_repo}/${arch}/${name}" \
|
||||
--upload-file "${file}"
|
||||
;;
|
||||
esac
|
||||
count=$((count + 1))
|
||||
done < <(find "${pkg_dir}" -type f \( -name '*.deb' -o -name '*.ddeb' -o -name '*.rpm' \) -print0)
|
||||
|
||||
# Uploading nothing would otherwise look like a successful publish.
|
||||
if [[ ${count} -eq 0 ]]; then
|
||||
echo "publish_pkg.sh: no packages found in ${pkg_dir}." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "${count} package(s) ${DRY_RUN:+would be }published."
|
||||
Reference in New Issue
Block a user