From 583e9f497b20a2cce6e63a1874d94781db9ea24f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zoran=20Peri=C4=8Di=C4=87?= Date: Wed, 8 Apr 2026 13:23:03 +0200 Subject: [PATCH] Initial package: turborepo-remote-cache 2.8.2 Self-hosted Turborepo remote cache server packaged as a Fedora RPM with the same base + -service + -container split used by the gitea package. - Base: dynamic sysusers turbo-cache user, /etc/turborepo-remote-cache config dir with config.env token template, /var/cache storage dir - -service: native Node.js systemd unit, app installed to %{nodejs_sitelib}/turborepo-remote-cache with pnpm-vendored production node_modules (built via fetch-sources.sh) - -container: Podman quadlet pinned to docker.io/ducktors/turborepo-remote-cache:2.8.2 - Listens on 127.0.0.1:3128; runners reach via host.containers.internal --- config.env | 29 +++++++ fetch-sources.sh | 42 +++++++++ turborepo-remote-cache.container | 24 ++++++ turborepo-remote-cache.service | 28 ++++++ turborepo-remote-cache.spec | 142 +++++++++++++++++++++++++++++++ turborepo-remote-cache.sysusers | 1 + 6 files changed, 266 insertions(+) create mode 100644 config.env create mode 100755 fetch-sources.sh create mode 100644 turborepo-remote-cache.container create mode 100644 turborepo-remote-cache.service create mode 100644 turborepo-remote-cache.spec create mode 100644 turborepo-remote-cache.sysusers diff --git a/config.env b/config.env new file mode 100644 index 0000000..3c02ca0 --- /dev/null +++ b/config.env @@ -0,0 +1,29 @@ +# Turborepo Remote Cache server configuration +# ============================================= +# +# This file is loaded by both the native systemd service and the +# Podman quadlet as an EnvironmentFile. Edit and restart the service: +# +# systemctl restart turborepo-remote-cache.service +# +# See https://github.com/ducktors/turborepo-remote-cache for all options. + +# Shared secret that clients (turbo CLI, CI runners) must present. +# Generate with: openssl rand -hex 32 +TURBO_TOKEN=CHANGE_ME + +# Storage provider. Keep "local" for filesystem-backed cache. +STORAGE_PROVIDER=local + +# Path inside the service/container where cached artifacts live. +# The native service writes here directly; the container mounts +# /var/cache/turborepo-remote-cache from the host to this path. +STORAGE_PATH=/var/cache/turborepo-remote-cache + +# Listen address and port. Bound to localhost by default — runners +# reach the host via host.containers.internal (Podman) on this port. +HOST=127.0.0.1 +PORT=3128 + +# Log level: fatal, error, warn, info, debug, trace +LOG_LEVEL=info diff --git a/fetch-sources.sh b/fetch-sources.sh new file mode 100755 index 0000000..9479519 --- /dev/null +++ b/fetch-sources.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Fetch upstream source tarball and vendor production node_modules. +# +# Produces two files next to the spec: +# turborepo-remote-cache-.tar.gz (upstream src) +# turborepo-remote-cache--node_modules_prod.tar.gz +# +# Requires: curl, tar, pnpm, node +# +# Run this before `rpmbuild` whenever VERSION changes. + +set -euo pipefail + +VERSION="${1:-2.8.2}" +PKG="turborepo-remote-cache" +UPSTREAM="https://github.com/ducktors/${PKG}" + +SPEC_DIR="$(cd "$(dirname "$0")" && pwd)" +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +SRC_TARBALL="${PKG}-${VERSION}.tar.gz" +DEPS_TARBALL="${PKG}-${VERSION}-node_modules_prod.tar.gz" + +echo ">>> Fetching ${UPSTREAM}/archive/refs/tags/v${VERSION}.tar.gz" +curl -fsSL -o "${SPEC_DIR}/${SRC_TARBALL}" \ + "${UPSTREAM}/archive/refs/tags/v${VERSION}.tar.gz" + +echo ">>> Extracting source to ${WORK}" +tar -C "${WORK}" -xzf "${SPEC_DIR}/${SRC_TARBALL}" +cd "${WORK}/${PKG}-${VERSION}" + +echo ">>> Installing production dependencies with pnpm" +pnpm install --prod --frozen-lockfile --ignore-scripts + +echo ">>> Packing node_modules into ${DEPS_TARBALL}" +tar -czf "${SPEC_DIR}/${DEPS_TARBALL}" node_modules + +echo +echo "Done:" +echo " ${SPEC_DIR}/${SRC_TARBALL}" +echo " ${SPEC_DIR}/${DEPS_TARBALL}" diff --git a/turborepo-remote-cache.container b/turborepo-remote-cache.container new file mode 100644 index 0000000..4bf1345 --- /dev/null +++ b/turborepo-remote-cache.container @@ -0,0 +1,24 @@ +[Unit] +Description=Turborepo Remote Cache (Podman quadlet) +Documentation=https://github.com/ducktors/turborepo-remote-cache +After=network-online.target +Wants=network-online.target + +[Container] +ContainerName=turborepo-remote-cache +Image=docker.io/ducktors/turborepo-remote-cache:2.8.2 +EnvironmentFile=/etc/turborepo-remote-cache/config.env +# Bind to loopback on the host; runners reach via host.containers.internal +PublishPort=127.0.0.1:3128:3000 +Volume=/var/cache/turborepo-remote-cache:/app/cache:Z +# Override STORAGE_PATH inside the container to match the bind mount +Environment=STORAGE_PATH=/app/cache +Environment=PORT=3000 +Environment=HOST=0.0.0.0 + +[Service] +Restart=on-failure +RestartSec=5s + +[Install] +WantedBy=multi-user.target default.target diff --git a/turborepo-remote-cache.service b/turborepo-remote-cache.service new file mode 100644 index 0000000..39069c5 --- /dev/null +++ b/turborepo-remote-cache.service @@ -0,0 +1,28 @@ +[Unit] +Description=Turborepo Remote Cache server +Documentation=https://github.com/ducktors/turborepo-remote-cache +After=network.target + +[Service] +Type=simple +User=turbo-cache +Group=turbo-cache +EnvironmentFile=/etc/turborepo-remote-cache/config.env +ExecStart=/usr/bin/node --enable-source-maps /usr/lib/node_modules/turborepo-remote-cache/dist/index.js +Restart=on-failure +RestartSec=5s + +# Hardening +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +PrivateDevices=yes +ProtectKernelTunables=yes +ProtectKernelModules=yes +ProtectControlGroups=yes +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +ReadWritePaths=/var/cache/turborepo-remote-cache + +[Install] +WantedBy=multi-user.target diff --git a/turborepo-remote-cache.spec b/turborepo-remote-cache.spec new file mode 100644 index 0000000..3b4a0ef --- /dev/null +++ b/turborepo-remote-cache.spec @@ -0,0 +1,142 @@ +%global npm_name turborepo-remote-cache +%global __brp_mangle_shebangs_exclude_from ^%{nodejs_sitelib}/%{npm_name}/.*$ + +Name: turborepo-remote-cache +Version: 2.8.2 +Release: 1%{?dist} +Summary: Self-hosted Turborepo remote cache server +License: MIT +Group: Development/Tools +URL: https://github.com/ducktors/turborepo-remote-cache + +# Fetch both with ./fetch-sources.sh before rpmbuild +Source0: %{name}-%{version}.tar.gz +Source1: %{name}-%{version}-node_modules_prod.tar.gz + +Source10: %{name}.sysusers +Source11: %{name}.service +Source12: %{name}.container +Source13: config.env + +BuildArch: noarch + +BuildRequires: systemd-rpm-macros +BuildRequires: nodejs-devel +BuildRequires: nodejs-npm +BuildRequires: pnpm + +%description +Turborepo Remote Cache is a self-hosted implementation of the Vercel +Remote Cache API used by Turborepo and turbo-compatible build tools. +It lets CI runners share build artifacts without relying on Vercel's +hosted cache. + +This base package ships the sysusers.d drop-in, the configuration +directory with a token template, and the cache storage directory. +Install either turborepo-remote-cache-service (native Node.js) or +turborepo-remote-cache-container (Podman quadlet) to actually run +the server. + +%package service +Summary: Turborepo Remote Cache as a native Node.js systemd service +Requires: %{name} = %{version}-%{release} +Requires: nodejs >= 1:20 +Conflicts: %{name}-container +%{?systemd_requires} + +%description service +Runs turborepo-remote-cache as a native Node.js process under systemd, +using the pnpm-vendored production dependencies shipped with this +package. Listens on 127.0.0.1:3128 by default. + +%package container +Summary: Turborepo Remote Cache as a Podman quadlet +Requires: %{name} = %{version}-%{release} +Requires: podman +Requires: containers-common +Conflicts: %{name}-service + +%description container +Runs turborepo-remote-cache as a Podman container via quadlet, +pulling docker.io/ducktors/turborepo-remote-cache:%{version}. +Listens on 127.0.0.1:3128 by default; cache storage is bind-mounted +from /var/cache/turborepo-remote-cache on the host. + +%prep +%setup -q -n %{name}-%{version} +# Drop the vendored node_modules from Source1 into the source tree +tar -xzf %{SOURCE1} + +%build +# Compile TypeScript → dist/. node_modules is already vendored. +pnpm build + +%install +%{__rm} -rf %{buildroot} + +# Base: sysusers, config dir, cache dir +install -p -D -m 644 %{SOURCE10} %{buildroot}%{_sysusersdir}/%{name}.conf + +install -d -m 750 %{buildroot}%{_sysconfdir}/%{name} +install -m 640 %{SOURCE13} %{buildroot}%{_sysconfdir}/%{name}/config.env + +install -d -m 750 %{buildroot}%{_localstatedir}/cache/%{name} + +# -service: install app tree to %{nodejs_sitelib}/turborepo-remote-cache +install -d -m 755 %{buildroot}%{nodejs_sitelib}/%{npm_name} +cp -pr dist node_modules package.json \ + %{buildroot}%{nodejs_sitelib}/%{npm_name}/ + +# CLI symlink + shebang fix +install -d -m 755 %{buildroot}%{_bindir} +ln -s %{nodejs_sitelib}/%{npm_name}/dist/cli.js \ + %{buildroot}%{_bindir}/%{name} +sed -i -e '1s|^#!.*node.*|#!/usr/bin/node|' \ + %{buildroot}%{nodejs_sitelib}/%{npm_name}/dist/cli.js || : +chmod 755 %{buildroot}%{nodejs_sitelib}/%{npm_name}/dist/cli.js + +# -service: systemd unit +install -p -D -m 644 %{SOURCE11} %{buildroot}%{_unitdir}/%{name}.service + +# -container: quadlet +install -p -D -m 644 %{SOURCE12} \ + %{buildroot}%{_datadir}/containers/systemd/%{name}.container + +%pre +%sysusers_create_package %{name} %{SOURCE10} + +%post service +%systemd_post %{name}.service + +%preun service +%systemd_preun %{name}.service + +%postun service +%systemd_postun_with_restart %{name}.service + +%files +%defattr(-,root,root,-) +%{_sysusersdir}/%{name}.conf +%dir %attr(0750,root,turbo-cache) %{_sysconfdir}/%{name} +%config(noreplace) %attr(0640,root,turbo-cache) %{_sysconfdir}/%{name}/config.env +%dir %attr(0750,turbo-cache,turbo-cache) %{_localstatedir}/cache/%{name} + +%files service +%defattr(-,root,root,-) +%{_unitdir}/%{name}.service +%dir %{nodejs_sitelib}/%{npm_name} +%{nodejs_sitelib}/%{npm_name}/dist +%{nodejs_sitelib}/%{npm_name}/node_modules +%{nodejs_sitelib}/%{npm_name}/package.json +%{_bindir}/%{name} + +%files container +%defattr(-,root,root,-) +%{_datadir}/containers/systemd/%{name}.container + +%changelog +* Wed Apr 08 2026 Zoran Pericic - 2.8.2-1 +- Initial package for ducktors/turborepo-remote-cache 2.8.2 +- Split into -service (native Node.js) and -container (Podman quadlet) +- Dynamic sysusers turbo-cache user +- Listens on 127.0.0.1:3128; storage at /var/cache/turborepo-remote-cache diff --git a/turborepo-remote-cache.sysusers b/turborepo-remote-cache.sysusers new file mode 100644 index 0000000..c8be731 --- /dev/null +++ b/turborepo-remote-cache.sysusers @@ -0,0 +1 @@ +u turbo-cache - "Turborepo Remote Cache" /var/cache/turborepo-remote-cache /sbin/nologin