Build and verify development RPMs with Gitea Actions
Build development RPM / rpm (push) Failing after 17s
Build development RPM / rpm (push) Failing after 17s
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
name: Build development RPM
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['main']
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rpm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: registry.fedoraproject.org/fedora:43
|
||||||
|
steps:
|
||||||
|
- name: Install build tools
|
||||||
|
run: |
|
||||||
|
dnf -y install rpm-build python3 resource-agents git tar gzip diffutils findutils coreutils bash nodejs
|
||||||
|
- name: Check out source
|
||||||
|
uses: https://github.com/actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
- name: Build and check RPMs
|
||||||
|
run: bash scripts/build-rpm.sh
|
||||||
|
- name: Test installation in the disposable job container
|
||||||
|
run: |
|
||||||
|
dnf -y install ./dist/*.noarch.rpm
|
||||||
|
python3 tests/verify_installed_rpm.py
|
||||||
|
# v3 uses the artifact protocol supported by older act_runner instances.
|
||||||
|
- name: Save development packages
|
||||||
|
uses: https://github.com/actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ocf-managed-container-fedora43
|
||||||
|
path: dist/
|
||||||
|
if-no-files-found: error
|
||||||
|
retention-days: 14
|
||||||
|
# No registry uploads, release creation, or deployment while licensing is pending.
|
||||||
@@ -13,3 +13,7 @@ __pycache__/
|
|||||||
secrets/
|
secrets/
|
||||||
*.pem
|
*.pem
|
||||||
*.key
|
*.key
|
||||||
|
|
||||||
|
# RPM build trees and development artifacts
|
||||||
|
.build/
|
||||||
|
dist/
|
||||||
|
|||||||
@@ -42,3 +42,11 @@ the frozen reference or automatically run destructive HA tests.
|
|||||||
```bash
|
```bash
|
||||||
git clone ssh://git@gitea.loxthelion.com:2222/david/ocf-managed-container.git
|
git clone ssh://git@gitea.loxthelion.com:2222/david/ocf-managed-container.git
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## RPM builds
|
||||||
|
|
||||||
|
Gitea Actions builds development RPMs on `main` pushes and pull requests using
|
||||||
|
an `ubuntu-latest` runner with a Fedora 43 job container. See
|
||||||
|
[packaging instructions](packaging/README.md) for local builds and artifacts.
|
||||||
|
[License provenance remains pending](packaging/PROVENANCE.md); release publishing
|
||||||
|
and deployment are not configured.
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
FROM registry.fedoraproject.org/fedora:43
|
||||||
|
RUN dnf -y install rpm-build python3 resource-agents git tar gzip diffutils findutils \
|
||||||
|
coreutils bash nodejs && dnf clean all
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# Licensing and provenance status
|
||||||
|
|
||||||
|
License selection is pending. No MIT, Apache, or GPL license has been granted
|
||||||
|
for this project by this document. `LicenseRef-Provenance-Pending` in the draft
|
||||||
|
RPM spec is a local status marker, not an assertion of an approved license.
|
||||||
|
Builds are development artifacts; this workflow does not publish releases or
|
||||||
|
upload to an RPM registry.
|
||||||
|
|
||||||
|
The original development conversation is:
|
||||||
|
https://chatgpt.com/share/6aa4e495-3e6c-83e9-a13c-fc65a972146b
|
||||||
|
|
||||||
|
The relevant discussion references the OCF developer guide and ClusterLabs'
|
||||||
|
`heartbeat/podman`, `heartbeat/docker`, `heartbeat/anything`, and
|
||||||
|
`heartbeat/pgsql` agents. One early proposal describes Podman as a model/base;
|
||||||
|
other passages discuss combining concepts from the container and PostgreSQL
|
||||||
|
agents. These establish influence, not proof of copied implementation.
|
||||||
|
|
||||||
|
On 2026-09-12 a preliminary comparison of the frozen reference against the
|
||||||
|
current upstream main versions of those four agents found only short exact
|
||||||
|
runs after trimming whitespace and omitting blank lines (maximum 3–4 lines).
|
||||||
|
The inspected matches were ordinary XML and shell control-flow fragments.
|
||||||
|
This is not a historical-version audit and does not exclude adapted code.
|
||||||
|
|
||||||
|
Source references:
|
||||||
|
|
||||||
|
- https://github.com/ClusterLabs/resource-agents/blob/main/doc/dev-guides/ra-dev-guide.asc
|
||||||
|
- https://github.com/ClusterLabs/resource-agents/blob/main/heartbeat/podman
|
||||||
|
- https://github.com/ClusterLabs/resource-agents/blob/main/heartbeat/docker
|
||||||
|
- https://github.com/ClusterLabs/resource-agents/blob/main/heartbeat/anything
|
||||||
|
- https://github.com/ClusterLabs/resource-agents/blob/main/heartbeat/pgsql
|
||||||
|
|
||||||
|
The inspected podman, docker and anything headers specify GPL version 2.
|
||||||
|
The inspected pgsql header says GPL without a version. The developer guide
|
||||||
|
explains that LGPL-licensed ocf-shellfuncs can be used by non-GPL agents.
|
||||||
|
The RPM depends on the system OCF library and does not bundle that library.
|
||||||
|
|
||||||
|
Before a licensed release, resolve the provenance of any adapted material,
|
||||||
|
retain required notices, select an appropriate license for project-owned
|
||||||
|
contributions, add the actual license text, and replace the draft RPM marker.
|
||||||
|
The private development conversation itself is not bundled in source archives.
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
# Development RPM builds
|
||||||
|
|
||||||
|
The Gitea workflow selects the `ubuntu-latest` runner and runs the build inside
|
||||||
|
Fedora 43, matching the saved lab package context. The workstation currently
|
||||||
|
reports Fedora 44; this first workflow does not claim a Fedora 44 build test.
|
||||||
|
It runs on main pushes, pull requests and manual dispatch.
|
||||||
|
|
||||||
|
The build installs only the OCF executable and nine modules, plus documentation.
|
||||||
|
It includes no Quadlet, secrets, databases or cluster configuration. There are
|
||||||
|
no RPM lifecycle scriptlets. Installation does not start, stop or restart a
|
||||||
|
service or change maintenance mode. Host PostgreSQL packages are not required.
|
||||||
|
|
||||||
|
The draft LicenseRef-Provenance-Pending field is a status marker, not a license
|
||||||
|
grant. See PROVENANCE.md. Packages are unsigned development artifacts. The
|
||||||
|
workflow does not publish a release or upload to the Gitea RPM registry.
|
||||||
|
|
||||||
|
## Local build
|
||||||
|
|
||||||
|
In a disposable Fedora 43 build environment, install the packages listed in
|
||||||
|
Containerfile, then run from a Git checkout:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/build-rpm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Artifacts are written to dist/: a noarch binary RPM, a source RPM and SHA256SUMS.
|
||||||
|
Build trees remain under .build/ for inspection. The script packages the current
|
||||||
|
working tree; use a clean checkout for a build identified by its Git revision.
|
||||||
|
Only explicitly listed project directories enter the source archive. Lab
|
||||||
|
snapshots, local agent configuration, .git and secrets are excluded.
|
||||||
|
|
||||||
|
Version comes from packaging/VERSION. Development Release values are
|
||||||
|
`0.<UTC commit timestamp>.git<12-character commit>`, plus the Fedora dist suffix.
|
||||||
|
A future release with Release 1 will sort after these development builds of the
|
||||||
|
same version. SOURCE_REVISION and SOURCE_DATE_EPOCH can supply build identity
|
||||||
|
when testing an exported source tree; RPM_OUTPUT_DIR selects the output path.
|
||||||
|
|
||||||
|
%check runs the offline differential suite and checks the staged executable's
|
||||||
|
metadata from both the build directory and /tmp. No live HA actions run.
|
||||||
|
Actual target-node validate-all and HA transitions remain separate lab tests.
|
||||||
|
|
||||||
|
## Gitea requirements
|
||||||
|
|
||||||
|
Enable repository Actions. The runner must support container jobs and reach
|
||||||
|
Gitea, GitHub (for actions), the Fedora image registry, and Fedora repositories.
|
||||||
|
Checkout uses the job token over HTTPS; no SSH private key is required.
|
||||||
|
|
||||||
|
The workflow uses upload-artifact v3 for compatibility with older act_runner
|
||||||
|
installations. Newer Gitea runners may support v4; check the installed server
|
||||||
|
and runner versions before changing the artifact action. Artifact upload and
|
||||||
|
runner integration must be verified by the first actual Gitea run.
|
||||||
|
|
||||||
|
Before release publishing, resolve licensing, add its text to the package,
|
||||||
|
choose signing and version-tag policy, and configure registry authentication.
|
||||||
|
|
||||||
|
## Validation performed
|
||||||
|
|
||||||
|
On 2026-09-12, the working-tree candidate was built with Fedora 43 / RPM 6.0.2
|
||||||
|
in a disposable rootless Podman container. Binary and source RPM builds passed,
|
||||||
|
including all 22 offline differential scenarios and staged metadata checks.
|
||||||
|
DNF successfully resolved dependencies and installed the binary RPM inside that
|
||||||
|
container. tests/verify_installed_rpm.py verified the installed architecture,
|
||||||
|
RPM integrity, exact agent bytes, root ownership, 0755/0644 modes, allowed file
|
||||||
|
paths, absence of lifecycle scriptlets, and metadata from an unrelated cwd.
|
||||||
|
The source RPM rebuilt with the same version/release without Git metadata.
|
||||||
|
|
||||||
|
Fedora's automatic shebang rewriting is disabled for this shell-only package
|
||||||
|
so the entrypoint remains byte-identical to the tested source. The Corosync
|
||||||
|
runtime dependency is the corosync package (its cmapctl binary is in /usr/bin
|
||||||
|
on Fedora 43). No package was installed on either lab host.
|
||||||
|
|
||||||
|
The Gitea server reports 1.27.3. Its Actions API returned HTTP 401 without
|
||||||
|
authentication, so a successful local build does not establish runner checkout
|
||||||
|
or artifact upload success. Check the first run in the repository's Actions tab.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
0.1.0
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
%global ocf_provider_dir /usr/lib/ocf/resource.d/loxthelion
|
||||||
|
# Preserve the proven entrypoint byte-for-byte, including its /bin/bash shebang.
|
||||||
|
%global __brp_mangle_shebangs %{nil}
|
||||||
|
%{!?package_version:%global package_version 0.1.0}
|
||||||
|
%{!?package_release:%global package_release 0.dev}
|
||||||
|
|
||||||
|
Name: ocf-managed-container
|
||||||
|
Version: %{package_version}
|
||||||
|
Release: %{package_release}%{?dist}
|
||||||
|
Summary: PostgreSQL HA agent for a persistent rootless container
|
||||||
|
# Development status only; see PROVENANCE.md. No release publishing yet.
|
||||||
|
License: LicenseRef-Provenance-Pending
|
||||||
|
URL: https://gitea.loxthelion.com/david/ocf-managed-container
|
||||||
|
Source0: %{name}-%{version}.tar.gz
|
||||||
|
BuildArch: noarch
|
||||||
|
|
||||||
|
BuildRequires: bash
|
||||||
|
BuildRequires: python3
|
||||||
|
BuildRequires: resource-agents
|
||||||
|
BuildRequires: hostname
|
||||||
|
BuildRequires: diffutils
|
||||||
|
BuildRequires: coreutils
|
||||||
|
Requires: bash
|
||||||
|
Requires: /usr/lib/ocf/lib/heartbeat/ocf-shellfuncs
|
||||||
|
Requires: podman
|
||||||
|
Requires: systemd
|
||||||
|
Requires: /usr/sbin/runuser
|
||||||
|
Requires: /usr/bin/getent
|
||||||
|
Requires: corosync
|
||||||
|
Requires: coreutils
|
||||||
|
Requires: gawk
|
||||||
|
Requires: sed
|
||||||
|
Requires: hostname
|
||||||
|
|
||||||
|
%description
|
||||||
|
OCF agent managing PostgreSQL through a persistent rootless systemd/Quadlet
|
||||||
|
container, with role-aware monitoring, promotion, demotion and rejoin.
|
||||||
|
This development package has unresolved license provenance. It does not
|
||||||
|
configure the cluster or container and does not start or stop services.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%autosetup
|
||||||
|
cp packaging/README.md RPM-BUILD.md
|
||||||
|
|
||||||
|
%build
|
||||||
|
# Shell modules require no compilation.
|
||||||
|
|
||||||
|
%install
|
||||||
|
install -d %{buildroot}%{ocf_provider_dir}/managed-container.d
|
||||||
|
install -m 0755 refactored/managed-container %{buildroot}%{ocf_provider_dir}/managed-container
|
||||||
|
install -m 0644 refactored/managed-container.d/*.sh %{buildroot}%{ocf_provider_dir}/managed-container.d/
|
||||||
|
|
||||||
|
%check
|
||||||
|
python3 tests/verify_refactor.py
|
||||||
|
# Check the actual staged layout, including resolution from an unrelated cwd.
|
||||||
|
agent="%{buildroot}%{ocf_provider_dir}/managed-container"
|
||||||
|
"$agent" meta-data > staged-metadata.xml
|
||||||
|
reference/managed-container meta-data > reference-metadata.xml
|
||||||
|
cmp reference-metadata.xml staged-metadata.xml
|
||||||
|
(cd /tmp && "$agent" meta-data) > other-cwd-metadata.xml
|
||||||
|
cmp reference-metadata.xml other-cwd-metadata.xml
|
||||||
|
|
||||||
|
%files
|
||||||
|
%defattr(-,root,root,-)
|
||||||
|
%doc README.md packaging/PROVENANCE.md RPM-BUILD.md
|
||||||
|
%dir %{ocf_provider_dir}
|
||||||
|
%attr(0755,root,root) %{ocf_provider_dir}/managed-container
|
||||||
|
%dir %{ocf_provider_dir}/managed-container.d
|
||||||
|
%attr(0644,root,root) %{ocf_provider_dir}/managed-container.d/*.sh
|
||||||
Executable
+43
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
project_dir="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$project_dir"
|
||||||
|
package_version="$(cat packaging/VERSION)"
|
||||||
|
[[ "$package_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo 'Invalid packaging/VERSION' >&2; exit 1; }
|
||||||
|
source_revision="${SOURCE_REVISION:-$(git rev-parse HEAD)}"
|
||||||
|
[[ "$source_revision" =~ ^[0-9a-f]{40,64}$ ]] || { echo 'Invalid SOURCE_REVISION' >&2; exit 1; }
|
||||||
|
export SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git show -s --format=%ct HEAD)}"
|
||||||
|
[[ "$SOURCE_DATE_EPOCH" =~ ^[0-9]+$ ]] || { echo 'Invalid SOURCE_DATE_EPOCH' >&2; exit 1; }
|
||||||
|
package_release="0.$(date -u -d "@$SOURCE_DATE_EPOCH" +%Y%m%d%H%M%S).git${source_revision:0:12}"
|
||||||
|
output_dir="${RPM_OUTPUT_DIR:-$project_dir/dist}"
|
||||||
|
mkdir -p "$output_dir" "$project_dir/.build"
|
||||||
|
output_dir="$(CDPATH= cd -- "$output_dir" && pwd)"
|
||||||
|
build_dir="$(mktemp -d "$project_dir/.build/rpm.XXXXXXXX")"
|
||||||
|
mkdir -p "$build_dir"/{SOURCES,SPECS,BUILD,BUILDROOT,RPMS,SRPMS}
|
||||||
|
|
||||||
|
# Explicit source list excludes lab snapshots, local configuration and secrets.
|
||||||
|
# Include the frozen reference and test harness so the SRPM runs the same checks.
|
||||||
|
tar --sort=name --mtime="@$SOURCE_DATE_EPOCH" --owner=0 --group=0 --numeric-owner \
|
||||||
|
--exclude='__pycache__' --exclude='*.pyc' \
|
||||||
|
--transform="s,^,ocf-managed-container-$package_version/," \
|
||||||
|
-czf "$build_dir/SOURCES/ocf-managed-container-$package_version.tar.gz" \
|
||||||
|
README.md HANDOFF.md refactored reference tests packaging scripts
|
||||||
|
# Freeze identity into the SRPM spec so rpmbuild --rebuild needs no Git metadata
|
||||||
|
# or external macro definitions and retains the same version and release.
|
||||||
|
sed -e "s/^Version:.*/Version: $package_version/" \
|
||||||
|
-e "s/^Release:.*/Release: $package_release%{?dist}/" \
|
||||||
|
packaging/ocf-managed-container.spec > "$build_dir/SPECS/ocf-managed-container.spec"
|
||||||
|
rpmbuild -ba --define "_topdir $build_dir" \
|
||||||
|
"$build_dir/SPECS/ocf-managed-container.spec"
|
||||||
|
|
||||||
|
for package in "$build_dir"/RPMS/noarch/*.rpm "$build_dir"/SRPMS/*.rpm; do
|
||||||
|
cp "$package" "$output_dir/"
|
||||||
|
done
|
||||||
|
rpm -qp --qf '%{NAME} %{VERSION}-%{RELEASE} %{ARCH}\nLicense: %{LICENSE}\n' \
|
||||||
|
"$build_dir"/RPMS/noarch/*.rpm
|
||||||
|
rpm -qpl "$build_dir"/RPMS/noarch/*.rpm
|
||||||
|
# The package must never run service or cluster lifecycle scriptlets.
|
||||||
|
[[ -z "$(rpm -qp --scripts "$build_dir"/RPMS/noarch/*.rpm)" ]]
|
||||||
|
(cd "$output_dir" && sha256sum ./*.rpm > SHA256SUMS)
|
||||||
|
printf 'Development RPMs: %s\nBuild tree: %s\n' "$output_dir" "$build_dir"
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Read-only checks after installing the RPM in a disposable build container."""
|
||||||
|
from pathlib import Path
|
||||||
|
import stat
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
PROVIDER = Path('/usr/lib/ocf/resource.d/loxthelion')
|
||||||
|
AGENT = PROVIDER / 'managed-container'
|
||||||
|
PACKAGE = 'ocf-managed-container'
|
||||||
|
|
||||||
|
|
||||||
|
def output(*args):
|
||||||
|
return subprocess.check_output(args, text=True)
|
||||||
|
|
||||||
|
|
||||||
|
assert output('rpm', '-q', '--qf', '%{ARCH}', PACKAGE) == 'noarch'
|
||||||
|
assert output('rpm', '-q', '--scripts', PACKAGE) == ''
|
||||||
|
subprocess.run(['rpm', '-V', PACKAGE], check=True)
|
||||||
|
expected = {AGENT: ROOT / 'refactored/managed-container'}
|
||||||
|
for path in (ROOT / 'refactored/managed-container.d').glob('*.sh'):
|
||||||
|
expected[PROVIDER / 'managed-container.d' / path.name] = path
|
||||||
|
assert len(expected) == 10
|
||||||
|
for path, source in expected.items():
|
||||||
|
assert path.read_bytes() == source.read_bytes(), path
|
||||||
|
info = path.stat()
|
||||||
|
assert (info.st_uid, info.st_gid) == (0, 0), path
|
||||||
|
assert stat.S_IMODE(info.st_mode) == (0o755 if path == AGENT else 0o644), path
|
||||||
|
|
||||||
|
owned = set(output('rpm', '-ql', PACKAGE).splitlines())
|
||||||
|
allowed = {str(path) for path in expected} | {str(PROVIDER), str(PROVIDER / 'managed-container.d')}
|
||||||
|
unexpected = {path for path in owned - allowed if not path.startswith('/usr/share/doc/' + PACKAGE)}
|
||||||
|
assert not unexpected, unexpected
|
||||||
|
|
||||||
|
reference = output(str(ROOT / 'reference/managed-container'), 'meta-data')
|
||||||
|
installed = subprocess.check_output([str(AGENT), 'meta-data'], cwd='/tmp', text=True)
|
||||||
|
assert installed == reference
|
||||||
|
print('PASS installed RPM: noarch, file integrity, root ownership, modes, payload, no scriptlets, metadata/module loading')
|
||||||
Reference in New Issue
Block a user