Initial semaphore 2.19.8 package with service, container and postgresql subpackages
Semaphore UI packaged from the upstream community (MIT) release binaries, following the gitea packaging pattern: native systemd service and Podman quadlet variants (mutually exclusive), with an optional PostgreSQL backend via quadlet drop-ins. SQLite is the default database; session/encryption secrets are generated into /etc/semaphore/env on first install.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
# Downloaded upstream release tarballs (spectool -g semaphore.spec)
|
||||
semaphore_community_*_linux_amd64.tar.gz
|
||||
semaphore_community_*_linux_arm64.tar.gz
|
||||
@@ -0,0 +1,70 @@
|
||||
# semaphore
|
||||
|
||||
Semaphore UI (https://semaphoreui.com) packaged for InfoMaOS. Web UI and
|
||||
API for running Ansible, Terraform, OpenTofu, Terragrunt, Bash and
|
||||
PowerShell tasks.
|
||||
|
||||
Binaries are the upstream **community** (fully open source, MIT) release
|
||||
builds. Download the source tarballs with:
|
||||
|
||||
spectool -g semaphore.spec
|
||||
|
||||
## Subpackages
|
||||
|
||||
- `semaphore` — common base: `semaphore` user/group (uid/gid 127),
|
||||
`/etc/semaphore/config.json`, `/etc/semaphore/env` (secrets generated
|
||||
on first install), `/var/lib/semaphore` data directory.
|
||||
- `semaphore-service` — native binary + `semaphore.service` unit.
|
||||
Requires `git` and `ansible`. Conflicts with `semaphore-container`.
|
||||
- `semaphore-container` — Podman quadlet running
|
||||
`docker.io/semaphoreui/semaphore:v<version>` in a pod
|
||||
(`semaphore-pod`, web UI published on port 3000). The image bundles
|
||||
Ansible, Terraform, OpenTofu and Terragrunt. Conflicts with
|
||||
`semaphore-service`.
|
||||
- `semaphore-postgresql` — optional PostgreSQL backend as a
|
||||
`semaphore-db` quadlet container. With `-service` it runs standalone,
|
||||
published on `127.0.0.1:5432`; with `-container` it joins the
|
||||
semaphore pod. Without it, Semaphore uses SQLite at
|
||||
`/var/lib/semaphore/database.sqlite` (zero configuration).
|
||||
|
||||
## Database
|
||||
|
||||
Default dialect is SQLite (`config.json`). Installing
|
||||
`semaphore-postgresql` switches the dialect to PostgreSQL via
|
||||
`SEMAPHORE_DB_*` environment drop-ins:
|
||||
|
||||
- service mode: `/etc/systemd/system/semaphore.service.d/database.conf`
|
||||
- container mode: `/usr/share/containers/systemd/semaphore.container.d/database.conf`
|
||||
|
||||
Change the default `semaphore`/`semaphore` credentials in **both** the
|
||||
relevant `database.conf` and
|
||||
`/etc/containers/systemd/semaphore-db.container.d/credentials.conf`
|
||||
before first start.
|
||||
|
||||
## First start
|
||||
|
||||
```bash
|
||||
systemctl enable --now semaphore.service # service mode
|
||||
systemctl start semaphore.service # container mode (enabled via pod drop-in)
|
||||
|
||||
# Create the initial admin user
|
||||
semaphore user add --admin --login admin --name Admin \
|
||||
--email admin@example.org --password '<password>' \
|
||||
--config /etc/semaphore/config.json
|
||||
```
|
||||
|
||||
In container mode `/usr/bin/semaphore` is a shim that runs the CLI
|
||||
inside the running container.
|
||||
|
||||
Session/encryption secrets (`SEMAPHORE_COOKIE_HASH`,
|
||||
`SEMAPHORE_COOKIE_ENCRYPTION`, `SEMAPHORE_ACCESS_KEY_ENCRYPTION`) are
|
||||
generated into `/etc/semaphore/env` by the base package `%post` on first
|
||||
install. Back up this file — `SEMAPHORE_ACCESS_KEY_ENCRYPTION` encrypts
|
||||
the stored SSH keys and secrets, which are lost if it changes.
|
||||
|
||||
## Updating
|
||||
|
||||
1. Bump `Version` in the spec and the image tag in
|
||||
`semaphore.container`.
|
||||
2. `spectool -g semaphore.spec`
|
||||
3. `./build.sh`
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"dialect": "sqlite",
|
||||
"sqlite": {
|
||||
"host": "/var/lib/semaphore/database.sqlite"
|
||||
},
|
||||
"port": ":3000",
|
||||
"tmp_path": "/var/lib/semaphore/tmp"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Switches the semaphore container from the default SQLite database to
|
||||
# the semaphore-db PostgreSQL container running in the same pod.
|
||||
# Credentials must match
|
||||
# /etc/containers/systemd/semaphore-db.container.d/credentials.conf.
|
||||
[Unit]
|
||||
Wants=semaphore-db.service
|
||||
After=semaphore-db.service
|
||||
|
||||
[Container]
|
||||
Environment=SEMAPHORE_DB_DIALECT=postgres
|
||||
Environment=SEMAPHORE_DB_HOST=127.0.0.1:5432
|
||||
Environment=SEMAPHORE_DB_USER=semaphore
|
||||
Environment=SEMAPHORE_DB_PASS=semaphore
|
||||
Environment=SEMAPHORE_DB=semaphore
|
||||
@@ -0,0 +1,13 @@
|
||||
# Switches semaphore.service (native binary) from the default SQLite
|
||||
# database to the semaphore-db PostgreSQL container. Credentials must
|
||||
# match /etc/containers/systemd/semaphore-db.container.d/credentials.conf.
|
||||
[Unit]
|
||||
Wants=semaphore-db.service
|
||||
After=semaphore-db.service
|
||||
|
||||
[Service]
|
||||
Environment=SEMAPHORE_DB_DIALECT=postgres
|
||||
Environment=SEMAPHORE_DB_HOST=127.0.0.1:5432
|
||||
Environment=SEMAPHORE_DB_USER=semaphore
|
||||
Environment=SEMAPHORE_DB_PASS=semaphore
|
||||
Environment=SEMAPHORE_DB=semaphore
|
||||
@@ -0,0 +1,4 @@
|
||||
[Container]
|
||||
Environment=POSTGRES_USER=semaphore
|
||||
Environment=POSTGRES_PASSWORD=semaphore
|
||||
Environment=POSTGRES_DB=semaphore
|
||||
@@ -0,0 +1,2 @@
|
||||
[Container]
|
||||
Pod=semaphore.pod
|
||||
@@ -0,0 +1,6 @@
|
||||
# Used with semaphore-service: the database container runs standalone
|
||||
# (outside the pod), so it must publish its port on the loopback and run
|
||||
# directly as the host semaphore user to own the data directory.
|
||||
[Container]
|
||||
User=127:127
|
||||
PublishPort=127.0.0.1:5432:5432
|
||||
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
Description=Semaphore UI PostgreSQL Database
|
||||
|
||||
[Container]
|
||||
Image=docker.io/library/postgres:18.1-trixie
|
||||
ContainerName=semaphore-db
|
||||
User=1001:1001
|
||||
Volume=/var/lib/semaphore/postgresql:/var/lib/postgresql:z
|
||||
Exec=postgres -c shared_buffers=256MB -c min_wal_size=80MB
|
||||
HealthCmd=pg_isready -U semaphore
|
||||
HealthInterval=30s
|
||||
HealthTimeout=10s
|
||||
HealthRetries=5
|
||||
HealthStartPeriod=1m
|
||||
HealthOnFailure=kill
|
||||
LogDriver=journald
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
TimeoutStartSec=900
|
||||
@@ -0,0 +1,2 @@
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
exec podman exec -i semaphore semaphore "$@"
|
||||
@@ -0,0 +1,25 @@
|
||||
[Unit]
|
||||
Description=Semaphore UI - modern UI for Ansible, Terraform and OpenTofu
|
||||
|
||||
[Container]
|
||||
Image=docker.io/semaphoreui/semaphore:v2.19.8
|
||||
ContainerName=semaphore
|
||||
Pod=semaphore.pod
|
||||
Volume=/var/lib/semaphore:/var/lib/semaphore:z
|
||||
Volume=/etc/semaphore:/etc/semaphore:z
|
||||
Volume=/etc/localtime:/etc/localtime:ro
|
||||
EnvironmentFile=/etc/semaphore/env
|
||||
Environment=SEMAPHORE_CONFIG_PATH=/etc/semaphore
|
||||
Notify=healthy
|
||||
HealthCmd=curl --fail --silent --output /dev/null http://localhost:3000/api/ping
|
||||
HealthInterval=30s
|
||||
HealthTimeout=10s
|
||||
HealthRetries=5
|
||||
HealthStartPeriod=120s
|
||||
HealthOnFailure=kill
|
||||
LogDriver=journald
|
||||
|
||||
[Service]
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
TimeoutStartSec=900
|
||||
@@ -0,0 +1,6 @@
|
||||
# Environment for Semaphore UI (read by semaphore.service and the
|
||||
# semaphore quadlet container). Secrets are generated on first install.
|
||||
SEMAPHORE_CONFIG=/etc/semaphore/config.json
|
||||
SEMAPHORE_COOKIE_HASH=
|
||||
SEMAPHORE_COOKIE_ENCRYPTION=
|
||||
SEMAPHORE_ACCESS_KEY_ENCRYPTION=
|
||||
@@ -0,0 +1,5 @@
|
||||
[Pod]
|
||||
PodName=semaphore-pod
|
||||
PublishPort=3000:3000
|
||||
UIDMap=0:0:127 127:1001:1 128:128:873 1001:127:1 1002:1002:64535
|
||||
GIDMap=0:0:127 127:1001:1 128:128:873 1001:127:1 1002:1002:64535
|
||||
@@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=Semaphore UI - modern UI for Ansible, Terraform and OpenTofu
|
||||
Documentation=https://docs.semaphoreui.com/
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
User=semaphore
|
||||
Group=semaphore
|
||||
WorkingDirectory=/var/lib/semaphore
|
||||
EnvironmentFile=/etc/semaphore/env
|
||||
ExecStart=/usr/libexec/semaphore server --config /etc/semaphore/config.json
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
Name: semaphore
|
||||
Version: 2.19.8
|
||||
Release: 1%{?dist}
|
||||
Summary: Semaphore UI - modern UI for Ansible, Terraform, OpenTofu and other DevOps tools
|
||||
License: MIT
|
||||
Group: System Environment/Base
|
||||
URL: https://semaphoreui.com
|
||||
|
||||
# Upstream community (fully open source) release binaries, download with
|
||||
# spectool -g semaphore.spec
|
||||
Source0: https://github.com/semaphoreui/semaphore/releases/download/v%{version}/semaphore_community_%{version}_linux_amd64.tar.gz
|
||||
Source1: https://github.com/semaphoreui/semaphore/releases/download/v%{version}/semaphore_community_%{version}_linux_arm64.tar.gz
|
||||
Source10: semaphore.service
|
||||
Source11: semaphore.sysusers
|
||||
Source12: config.json
|
||||
Source13: semaphore.env
|
||||
Source14: semaphore-shim
|
||||
Source15: semaphore.container
|
||||
Source16: semaphore.pod
|
||||
Source17: semaphore-pod-enable.conf
|
||||
Source18: semaphore-database.conf
|
||||
Source19: semaphore-container-database.conf
|
||||
Source20: semaphore-db.container
|
||||
Source21: semaphore-db-pod.conf
|
||||
Source22: semaphore-db-publish.conf
|
||||
Source23: semaphore-db-credentials.conf
|
||||
|
||||
ExclusiveArch: x86_64 aarch64
|
||||
|
||||
BuildRequires: systemd-rpm-macros
|
||||
|
||||
%description
|
||||
Semaphore UI is a modern web interface and API for running Ansible
|
||||
playbooks, Terraform, OpenTofu, Terragrunt, Bash and PowerShell tasks
|
||||
with notifications, access control and audit trail.
|
||||
|
||||
%package service
|
||||
Summary: Semaphore UI systemd service (native binary)
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
Requires: git
|
||||
Requires: ansible
|
||||
Conflicts: %{name}-container
|
||||
%{?systemd_requires}
|
||||
|
||||
%description service
|
||||
Runs Semaphore UI as a native binary under systemd. Includes the
|
||||
semaphore binary and systemd unit. Uses the SQLite database by default.
|
||||
|
||||
%package container
|
||||
Summary: Semaphore UI container (Podman quadlet)
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
Requires: podman
|
||||
Requires: containers-common
|
||||
Conflicts: %{name}-service
|
||||
|
||||
%description container
|
||||
Runs Semaphore UI as a Podman container via quadlet. The upstream image
|
||||
bundles Ansible, Terraform, OpenTofu and Terragrunt.
|
||||
|
||||
%package postgresql
|
||||
Summary: PostgreSQL database support for Semaphore UI
|
||||
Requires: podman
|
||||
Requires: containers-common
|
||||
Requires: %{name} = %{version}-%{release}
|
||||
|
||||
%description postgresql
|
||||
Provides PostgreSQL database integration for Semaphore UI. When used
|
||||
with semaphore-service, runs a standalone PostgreSQL container
|
||||
(semaphore-db) published on the loopback. When used with
|
||||
semaphore-container, runs the PostgreSQL container in the semaphore pod.
|
||||
|
||||
%install
|
||||
%{__rm} -rf %{buildroot}
|
||||
|
||||
# Binary (for -service subpackage) → libexec
|
||||
install -d -m 755 %{buildroot}%{_libexecdir}
|
||||
%ifarch x86_64
|
||||
tar -xzf %{SOURCE0} -C . semaphore
|
||||
%endif
|
||||
|
||||
%ifarch aarch64
|
||||
tar -xzf %{SOURCE1} -C . semaphore
|
||||
%endif
|
||||
install -m 755 semaphore %{buildroot}%{_libexecdir}/semaphore
|
||||
|
||||
# Shim (for -container subpackage)
|
||||
install -m 755 %{SOURCE14} %{buildroot}%{_libexecdir}/semaphore-container
|
||||
|
||||
# Base: sysusers, config, env
|
||||
install -p -D -m 644 %{SOURCE11} %{buildroot}%{_sysusersdir}/semaphore.conf
|
||||
|
||||
install -d -m 750 %{buildroot}%{_sysconfdir}/semaphore
|
||||
install -m 640 %{SOURCE12} %{buildroot}%{_sysconfdir}/semaphore/config.json
|
||||
install -m 600 %{SOURCE13} %{buildroot}%{_sysconfdir}/semaphore/env
|
||||
|
||||
# Base: data dirs
|
||||
install -d -m 750 %{buildroot}%{_sharedstatedir}/semaphore
|
||||
install -d -m 750 %{buildroot}%{_sharedstatedir}/semaphore/tmp
|
||||
|
||||
# Service subpackage
|
||||
install -p -D -m 644 %{SOURCE10} %{buildroot}%{_unitdir}/semaphore.service
|
||||
install -p -D -m 644 %{SOURCE22} %{buildroot}%{_datadir}/containers/systemd/semaphore-db.container.d/publish.conf
|
||||
|
||||
# Container subpackage
|
||||
install -p -D -m 644 %{SOURCE15} %{buildroot}%{_datadir}/containers/systemd/semaphore.container
|
||||
install -p -D -m 644 %{SOURCE16} %{buildroot}%{_datadir}/containers/systemd/semaphore.pod
|
||||
install -p -D -m 644 %{SOURCE17} %{buildroot}%{_sysconfdir}/containers/systemd/semaphore.pod.d/enable.conf
|
||||
|
||||
# PostgreSQL subpackage
|
||||
install -d -m 750 %{buildroot}%{_sharedstatedir}/semaphore/postgresql
|
||||
install -p -D -m 644 %{SOURCE18} %{buildroot}%{_sysconfdir}/systemd/system/semaphore.service.d/database.conf
|
||||
install -p -D -m 644 %{SOURCE19} %{buildroot}%{_datadir}/containers/systemd/semaphore.container.d/database.conf
|
||||
install -p -D -m 644 %{SOURCE20} %{buildroot}%{_datadir}/containers/systemd/semaphore-db.container
|
||||
install -p -D -m 644 %{SOURCE21} %{buildroot}%{_datadir}/containers/systemd/semaphore-db.container.d/pod.conf
|
||||
install -p -D -m 644 %{SOURCE23} %{buildroot}%{_sysconfdir}/containers/systemd/semaphore-db.container.d/credentials.conf
|
||||
|
||||
%pre
|
||||
%sysusers_create_compat %{SOURCE11}
|
||||
|
||||
%post
|
||||
# Generate session/encryption secrets on first install (empty values only)
|
||||
for key in SEMAPHORE_COOKIE_HASH SEMAPHORE_COOKIE_ENCRYPTION SEMAPHORE_ACCESS_KEY_ENCRYPTION; do
|
||||
if grep -q "^${key}=$" %{_sysconfdir}/semaphore/env 2>/dev/null; then
|
||||
secret=$(head -c 32 /dev/urandom | base64 -w0)
|
||||
sed -i "s|^${key}=$|${key}=${secret}|" %{_sysconfdir}/semaphore/env
|
||||
fi
|
||||
done
|
||||
|
||||
%post service
|
||||
%systemd_post semaphore.service
|
||||
|
||||
%posttrans service
|
||||
ln -sf %{_libexecdir}/semaphore %{_bindir}/semaphore
|
||||
|
||||
%preun service
|
||||
%systemd_preun semaphore.service
|
||||
|
||||
%postun service
|
||||
%systemd_postun semaphore.service
|
||||
if [ $1 -eq 0 ]; then
|
||||
rm -f %{_bindir}/semaphore
|
||||
fi
|
||||
|
||||
%post container
|
||||
%systemd_post semaphore.service
|
||||
|
||||
%posttrans container
|
||||
ln -sf %{_libexecdir}/semaphore-container %{_bindir}/semaphore
|
||||
|
||||
%preun container
|
||||
%systemd_preun semaphore.service
|
||||
if [ $1 -eq 0 ]; then
|
||||
rm -f %{_bindir}/semaphore
|
||||
fi
|
||||
|
||||
%postun container
|
||||
%systemd_postun semaphore.service
|
||||
|
||||
%clean
|
||||
%{__rm} -rf %{buildroot}
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%{_sysusersdir}/semaphore.conf
|
||||
|
||||
%dir %attr(0750,semaphore,semaphore) %{_sysconfdir}/semaphore
|
||||
%config(noreplace) %attr(0640,semaphore,semaphore) %{_sysconfdir}/semaphore/config.json
|
||||
%config(noreplace) %attr(0600,root,root) %{_sysconfdir}/semaphore/env
|
||||
|
||||
%dir %attr(0750,semaphore,semaphore) %{_sharedstatedir}/semaphore
|
||||
%dir %attr(0750,semaphore,semaphore) %{_sharedstatedir}/semaphore/tmp
|
||||
|
||||
%files service
|
||||
%defattr(-,root,root,-)
|
||||
%{_libexecdir}/semaphore
|
||||
%ghost %{_bindir}/semaphore
|
||||
%attr(0644,root,root) %{_unitdir}/semaphore.service
|
||||
%dir %{_datadir}/containers/systemd/semaphore-db.container.d
|
||||
%{_datadir}/containers/systemd/semaphore-db.container.d/publish.conf
|
||||
|
||||
%files container
|
||||
%defattr(-,root,root,-)
|
||||
%attr(0755,root,root) %{_libexecdir}/semaphore-container
|
||||
%ghost %{_bindir}/semaphore
|
||||
%{_datadir}/containers/systemd/semaphore.container
|
||||
%{_datadir}/containers/systemd/semaphore.pod
|
||||
%dir %{_sysconfdir}/containers/systemd/semaphore.pod.d
|
||||
%config(noreplace) %{_sysconfdir}/containers/systemd/semaphore.pod.d/enable.conf
|
||||
|
||||
%files postgresql
|
||||
%defattr(-,root,root,-)
|
||||
%dir %attr(0750,semaphore,semaphore) %{_sharedstatedir}/semaphore/postgresql
|
||||
# For -service: systemd drop-in switching the native service to PostgreSQL
|
||||
%dir %{_sysconfdir}/systemd/system/semaphore.service.d
|
||||
%config(noreplace) %{_sysconfdir}/systemd/system/semaphore.service.d/database.conf
|
||||
# For -container: semaphore-db container + drop-in wiring it to semaphore
|
||||
%{_datadir}/containers/systemd/semaphore-db.container
|
||||
%dir %{_datadir}/containers/systemd/semaphore.container.d
|
||||
%config(noreplace) %{_datadir}/containers/systemd/semaphore.container.d/database.conf
|
||||
%dir %{_datadir}/containers/systemd/semaphore-db.container.d
|
||||
%{_datadir}/containers/systemd/semaphore-db.container.d/pod.conf
|
||||
%dir %{_sysconfdir}/containers/systemd/semaphore-db.container.d
|
||||
%config(noreplace) %{_sysconfdir}/containers/systemd/semaphore-db.container.d/credentials.conf
|
||||
|
||||
%changelog
|
||||
* Tue Aug 18 2026 Zoran Pericic <zpericic@netst.org> - 2.19.8-1
|
||||
- Initial package with service, container and postgresql subpackages
|
||||
@@ -0,0 +1,2 @@
|
||||
g semaphore 127
|
||||
u semaphore 127:127 "Semaphore UI" /var/lib/semaphore /sbin/nologin
|
||||
Reference in New Issue
Block a user