Compare commits

...

23 Commits

Author SHA1 Message Date
jbrechtel 26df70f9f5 Remove converge-wake service and system-sleep hook
Build / build (push) Successful in 17m6s
2026-07-07 21:26:03 -04:00
jbrechtel 96e66be50e Bumps version
Build / build (push) Successful in 16m12s
2026-06-06 16:11:11 -04:00
jbrechtel c0b566fc70 Don't block on starting the service after sleep
Build / build (push) Has been cancelled
2026-06-06 16:09:04 -04:00
jbrechtel bdcb3d1d20 Bump version to 0.1.0.1
Build / build (push) Successful in 11m29s
2026-05-29 14:10:58 -04:00
jbrechtel cc2df21796 Fix Arch package: remove subdirectory path from source=() array
Build / build (push) Successful in 11m34s
makepkg cannot resolve 'src/system-sleep/converge-wake' in source=() because
it looks for just the basename. The file is still installed correctly by the
package() function which accesses it directly via cd $startdir + relative path.
2026-05-29 13:53:35 -04:00
jbrechtel f546ee450e Add converge-wake.service and systemd-sleep hook for wake-from-sleep sync
Build / build (push) Failing after 12m9s
Creates a oneshot user service (converge-wake.service) that runs
converge --sync after the network is online, and a systemd-sleep hook
(/usr/lib/systemd/system-sleep/converge-wake) that triggers the service
for each logged-in user on resume.

Uses After=network-online.target for proper network timing on
NetworkManager/systemd-networkd setups, with a 5-second ExecStartPre
delay as a safety net for netctl and other environments where
network-online.target may not gate on resume.

Updated PKGBUILD and AGENTS.md accordingly.
2026-05-23 22:38:55 -04:00
jbrechtel 6d165a3912 converge: auto-commit at 2026-05-15 09:14:48 from zendo.brechtel
Build / build (push) Successful in 1m25s
2026-05-15 09:14:48 -04:00
jbrechtel 3edb5c33ee converge: auto-commit at 2026-05-14 23:36:30 from zendo.brechtel
Build / build (push) Failing after 1m8s
2026-05-14 23:36:30 -04:00
jbrechtel ab87e943f3 converge: auto-commit at 2026-05-14 23:26:25 from zendo.brechtel
Build / build (push) Failing after 1m21s
2026-05-14 23:26:25 -04:00
jbrechtel 39f85a3900 converge: auto-commit at 2026-05-14 23:19:34 from zendo.brechtel
Build / build (push) Failing after 57s
2026-05-14 23:19:34 -04:00
jbrechtel af0f03758a converge: auto-commit at 2026-05-14 23:12:04 from zendo.brechtel
Build / build (push) Failing after 1m23s
2026-05-14 23:12:04 -04:00
jbrechtel dee7c95542 converge: auto-commit at 2026-05-14 23:07:12 from zendo.brechtel
Build / build (push) Failing after 1m8s
2026-05-14 23:07:12 -04:00
jbrechtel c7a3690129 converge: auto-commit at 2026-05-14 22:58:49 from zendo.brechtel
Build / build (push) Failing after 1m41s
2026-05-14 22:58:49 -04:00
jbrechtel 0d8cddb9be converge: auto-commit at 2026-05-14 22:53:35 from zendo.brechtel
Build / build (push) Failing after 1m56s
2026-05-14 22:53:35 -04:00
jbrechtel 9bca886629 converge: auto-commit at 2026-05-14 22:46:10 from zendo.brechtel
Build / build (push) Failing after 1m45s
2026-05-14 22:46:10 -04:00
jbrechtel edc6d5acf3 Ignores arch package files etc 2026-05-14 22:32:35 -04:00
jbrechtel 4473dc9164 Fixes contact and name
Build / build (push) Successful in 1m22s
2026-05-14 22:20:46 -04:00
jbrechtel 29a984934d converge: auto-commit at 2026-05-14 22:15:21 from zendo.brechtel
Build / build (push) Successful in 1m4s
2026-05-14 22:15:21 -04:00
jbrechtel ef49f30c88 Add Arch Linux PKGBUILD and package build step
Build / build (push) Successful in 58s
- PKGBUILD: builds converge package, uses pre-built binary from
  build/converge when available (from scripts/build), falls back to
  stack build from source. Installs binary, systemd user service,
  example config, and README.
- scripts/build: added final step to run makepkg -f on the host
  (conditional, skipped if makepkg not found). Produces .pkg.tar.zst
  alongside the binary.
- scripts/makepkg: convenience wrapper for standalone makepkg runs.
2026-05-14 19:03:07 -04:00
jbrechtel 89e7729f2c converge: disable response timeout on relay SSE connection
Build / build (push) Successful in 48s
tlsManagerSettings defaults to a 30s response timeout, which kills
long-lived SSE connections between push events. Set it to
responseTimeoutNone so the relay watcher waits indefinitely.
2026-05-14 18:10:02 -04:00
jbrechtel 1ad993b9d1 Fix: downgrade upload-artifact to v3 (Gitea/GHES compat)
Build / build (push) Successful in 12m2s
2026-05-14 16:10:42 -04:00
jbrechtel 27f4e1fd2c Fix: use absolute path for STACK_ROOT (Stack 3 requirement)
Build / build (push) Failing after 15m57s
2026-05-14 15:51:00 -04:00
jbrechtel 657855bd41 Fix CI: remove Docker-in-Docker, install Stack directly
Build / build (push) Failing after 9s
The ./hs Docker wrapper runs docker inside the Gitea runner container.
Since project paths inside the runner container don't exist on the Docker
host, the volume mount creates an empty /work directory, and Stack finds
no packages (error S-8506).

Fix: install Stack directly via get.haskellstack.org, use STACK_ROOT for
caching, and build with stack directly (no Docker-in-Docker).

Also add symlink for gitea-actions.sh convenience script.
2026-05-14 15:50:09 -04:00
15 changed files with 314 additions and 20 deletions
+57 -9
View File
@@ -9,24 +9,26 @@ jobs:
build:
runs-on: ubuntu-latest
env:
CONVERGE_HASKELL_TOOLS_IMAGE: ghcr.io/flipstone/haskell-tools:debian-ghc-9.10.3-5d6640d
STACK_ROOT: ${{ github.workspace }}/.stack-root
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: .
- name: Install Stack
run: |
curl -sSL https://get.haskellstack.org/ | sh
- name: Cache Stack
uses: actions/cache@v4
with:
path: .stack-root
key: stack-${{ runner.os }}-${{ hashFiles('stack.yaml', 'package.yaml') }}
restore-keys: stack-${{ runner.os }}-
key: stack-v2-${{ runner.os }}-${{ hashFiles('stack.yaml', 'package.yaml') }}
restore-keys: stack-v2-${{ runner.os }}-
- name: Build
run: |
mkdir -p .stack-root build
./hs stack build --copy-bins --local-bin-path /work/build
mkdir -p build
stack build --copy-bins --local-bin-path ./build
- name: Compress with UPX
run: |
@@ -39,9 +41,55 @@ jobs:
fi
${UPX_DIR}/upx build/converge
- name: Build Arch package
run: |
# Build the package inside an Arch Linux container.
# Pipe workspace via stdin to avoid Docker-in-Docker volume mount issues.
# All build output goes to stderr (visible in logs); only the package
# tar goes to stdout so it can be captured cleanly.
tar cf - --exclude=.stack-root --exclude=.stack-work . | \
docker run --rm -i -w /build archlinux:latest bash -c "
mkdir -p /build &&
cd /build &&
tar xf - &&
pacman -Syu --noconfirm >&2 &&
pacman -S --noconfirm git fakeroot debugedit >&2 &&
useradd -m build &&
chown -R build:build . &&
runuser -u build -- makepkg --noconfirm --nodeps --nocheck >&2 &&
tar cf /dev/stdout converge-*.pkg.tar.zst
" > /tmp/packages.tar
tar xf /tmp/packages.tar
echo "pkgfile=$(ls converge-[0-9]*.pkg.tar.zst)" >> "$GITHUB_ENV"
- name: Publish to Arch repo
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
env:
SSH_AUTH_SOCK: /dev/null
run: |
# Set up SSH
mkdir -p "$HOME/.ssh"
echo "${{ secrets.ARCH_REPO_SSH_KEY }}" > "$HOME/.ssh/id_ed25519"
chmod 600 "$HOME/.ssh/id_ed25519"
ssh-keyscan -H "${{ vars.ARCH_REPO_HOST }}" >> "$HOME/.ssh/known_hosts" 2>/dev/null
PKGFILE="${{ env.pkgfile }}"
echo "Uploading $PKGFILE to ${{ vars.ARCH_REPO_HOST }}..."
# Upload the package
scp -v -i "$HOME/.ssh/id_ed25519" \
"$PKGFILE" \
"${{ vars.ARCH_REPO_USER }}@${{ vars.ARCH_REPO_HOST }}:${{ vars.ARCH_REPO_PATH }}/"
# Add to repo database
ssh -v -i "$HOME/.ssh/id_ed25519" \
"${{ vars.ARCH_REPO_USER }}@${{ vars.ARCH_REPO_HOST }}" \
"cd '${{ vars.ARCH_REPO_PATH }}' && \
repo-add '${{ vars.ARCH_REPO_DB }}' '$PKGFILE' && \
rm -f '$PKGFILE.old'"
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v3
with:
name: converge
path: build/converge
compression-level: 0
+2
View File
@@ -12,3 +12,5 @@ dist-newstyle/
.pi/
converge-relay/.go-cache/
converge-relay/build/
*.pkg.tar
pkg/
+3 -3
View File
@@ -18,9 +18,9 @@ test/TestHelper.hs # Shared test utilities (temp repos, sentinel files)
test/GitCommitSpec.hs # Commit behaviour tests
test/GitPullSpec.hs # Pull/rebase tests
test/GitSafetySpec.hs # Operation-in-progress detection tests
SPEC.md # Human-readable specifications
converge.example.yaml # Example config file
converge.service # systemd unit file
SPEC.md # Human-readable specifications
converge.example.yaml # Example config file
converge.service # systemd user service (daemon mode)
```
## Architecture
+68
View File
@@ -0,0 +1,68 @@
# Maintainer: James Brechtel <james@leonard-brechtel.com.com>
# Contributor: James Brechtel <james@leonard-brechtel.com.com>
pkgname=converge
_pkgver=0.2.0.0
pkgver=${_pkgver//-/_}
pkgrel=1
pkgdesc="Auto-sync git repositories by watching for changes, committing, pulling, and pushing"
arch=('x86_64')
url="https://github.com/jbrechtel/converge"
license=('BSD')
makedepends=('stack' 'git')
depends=('git' 'libnotify' 'gcc-libs')
optdepends=('converge-relay: realtime push notifications via SSE relay server')
backup=('etc/converge/config.example.yaml')
source=(
"converge.service"
"converge.example.yaml"
)
md5sums=(
'SKIP'
'SKIP'
)
build() {
cd "$startdir"
echo "$startdir"
# Use pre-built binary if available (e.g. from scripts/build), otherwise build from source
if [[ -x build/converge ]]; then
msg "Using pre-built binary from build/converge"
install -Dm755 build/converge "$srcdir/bin/converge"
else
export STACK_ROOT="$srcdir/.stack-root"
msg "Building converge with stack (resolver: $(awk '/^resolver:/ {print $2}' stack.yaml))..."
stack build \
--copy-bins \
--local-bin-path "$srcdir/bin" \
--ghc-options '-O2'
fi
}
check() {
cd "$startdir"
export STACK_ROOT="$srcdir/.stack-root"
msg "Running tests..."
stack test
}
package() {
cd "$startdir"
# Binary
install -Dm755 "$srcdir/bin/converge" \
"$pkgdir/usr/bin/converge"
# User systemd services
install -Dm644 converge.service \
"$pkgdir/usr/lib/systemd/user/converge.service"
# Example config
install -Dm644 converge.example.yaml \
"$pkgdir/etc/converge/config.example.yaml"
# Documentation
install -Dm644 README.md \
"$pkgdir/usr/share/doc/converge/README.md"
}
+1 -1
View File
@@ -17,7 +17,7 @@
## Supports logging to a file instead of stderr via --log-file or log_file config
## Can notify on pull: per-repo notify_on_pull setting sends a desktop notification when new commits are pulled from the remote
## --sync / -s flag runs a single sync cycle (commit + pull + push) and exits, useful for wake-from-sleep hooks
## --sync / -s flag runs a single sync cycle (commit + pull + push) and exits
## Can connect to a converge-relay SSE endpoint and automatically pull when push events arrive
### Configured via --relay-url CLI flag or relay_url config file entry
+127
View File
@@ -0,0 +1,127 @@
cabal-version: 2.2
-- This file has been generated from package.yaml by hpack version 0.38.1.
--
-- see: https://github.com/sol/hpack
name: converge
version: 0.1.0.1
synopsis: Auto-sync a git repository by watching for changes, committing, and pulling
description: Converge watches a git repository for file changes, automatically
commits them, and pulls from the remote. It notifies the user via
libnotify/notify-send when merge conflicts arise.
author: James Leonard-Brechtel
maintainer: james@leonard-brechtel.com
copyright: 2026 James Leonard-Brechtel
license: BSD-3-Clause
build-type: Simple
library
exposed-modules:
Converge
other-modules:
Paths_converge
autogen-modules:
Paths_converge
hs-source-dirs:
src
default-extensions:
OverloadedStrings
RecordWildCards
ScopedTypeVariables
TupleSections
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints
build-depends:
aeson
, async
, base >=4.7 && <5
, bytestring
, directory
, filepath
, fsnotify
, http-client
, http-client-tls
, http-types
, optparse-applicative
, process
, text
, time
, unix
, yaml
default-language: Haskell2010
executable converge
main-is: Main.hs
other-modules:
Paths_converge
autogen-modules:
Paths_converge
hs-source-dirs:
app
default-extensions:
OverloadedStrings
RecordWildCards
ScopedTypeVariables
TupleSections
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
build-depends:
aeson
, async
, base >=4.7 && <5
, bytestring
, converge
, directory
, filepath
, fsnotify
, http-client
, http-client-tls
, http-types
, optparse-applicative
, process
, text
, time
, unix
, yaml
default-language: Haskell2010
test-suite converge-test
type: exitcode-stdio-1.0
main-is: Spec.hs
other-modules:
GitCommitSpec
GitPullSpec
GitSafetySpec
RelaySpec
TestHelper
Paths_converge
autogen-modules:
Paths_converge
hs-source-dirs:
test
default-extensions:
OverloadedStrings
RecordWildCards
ScopedTypeVariables
TupleSections
ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-export-lists -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N
build-depends:
aeson
, async
, base >=4.7 && <5
, bytestring
, converge
, directory
, filepath
, fsnotify
, hspec
, http-client
, http-client-tls
, http-types
, optparse-applicative
, process
, temporary
, text
, time
, unix
, yaml
default-language: Haskell2010
+1 -1
View File
@@ -6,7 +6,7 @@ Wants=network-online.target
[Service]
Type=simple
ExecStart=%h/.local/bin/converge --config %h/.config/converge/config.yaml
ExecStart=/usr/bin/converge --config %h/.config/converge/config.yaml
Restart=on-failure
RestartSec=10
+4 -4
View File
@@ -1,13 +1,13 @@
name: converge
version: 0.1.0.0
version: 0.1.0.1
synopsis: Auto-sync a git repository by watching for changes, committing, and pulling
description: |
Converge watches a git repository for file changes, automatically
commits them, and pulls from the remote. It notifies the user via
libnotify/notify-send when merge conflicts arise.
author: James Brechtel
maintainer: james@flipstone.com
copyright: 2026 James Brechtel
author: James Leonard-Brechtel
maintainer: james@leonard-brechtel.com
copyright: 2026 James Leonard-Brechtel
license: BSD-3-Clause
default-extensions:
+12
View File
@@ -20,3 +20,15 @@ echo "Compressing with UPX..."
'
echo "Done: $(ls -lh build/converge | awk '{print $5}')"
# Build Arch Linux package (if makepkg is available)
if command -v makepkg &>/dev/null; then
echo "Building Arch package with makepkg..."
if makepkg -f 2>&1; then
echo "Package: $(ls -1 converge-*.pkg.tar.zst 2>/dev/null)"
else
echo "WARNING: makepkg failed (see above)" >&2
fi
else
echo "Skipping package build (makepkg not found)"
fi
+1
View File
@@ -0,0 +1 @@
../.pi/skills/gitea-actions/scripts/gitea-actions.sh
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# Build an Arch Linux package for converge using makepkg.
#
# Usage:
# ./scripts/makepkg # Build the package (no install)
# ./scripts/makepkg -i # Build and install
# ./scripts/makepkg -s # Build and install missing deps
#
# The resulting .pkg.tar.zst will be placed in the project root.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.."
makepkg -f "$@"
+4 -2
View File
@@ -447,7 +447,9 @@ gitCommitAll logger repoPath = do
then pure addResult
else do
hostname <- init <$> readProcess "hostname" [] ""
loggedGit logger repoPath ["commit", "-m", "converge: auto-commit from " <> hostname]
timestamp <- formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S" <$> getCurrentTime
let msg = "converge: auto-commit at " <> timestamp <> " from " <> hostname
loggedGit logger repoPath ["commit", "-m", msg]
-- | Pull from the configured remote, rebasing onto the tracked upstream branch.
gitPull :: Logger -> RepoConfig -> IO (ExitCode, Text, Text)
@@ -585,7 +587,7 @@ connectRelay :: Text -> Logger -> [RepoConfig] -> IO ()
connectRelay url logger repos = do
logMsg logger Info ("relay: connecting to " <> url)
req <- parseRequest (T.unpack url)
manager <- newManager tlsManagerSettings
manager <- newManager tlsManagerSettings{managerResponseTimeout = responseTimeoutNone}
let loop delay = do
let delaySec = min delay (60 :: Int)
result <- try $
+19
View File
@@ -0,0 +1,19 @@
[Unit]
Description=Converge - sync repos after waking from sleep
Documentation=https://github.com/jbrechtel/converge
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/bin/converge --sync
# Small delay as a safety net for environments (e.g. netctl) where
# network-online.target may not gate properly on resume, giving the
# network stack time to reconnect before converge attempts to pull.
ExecStartPre=/bin/sleep 5
# Allow notify-send to reach the desktop session
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/bus
[Install]
WantedBy=default.target
+1
View File
@@ -0,0 +1 @@
/work/personal/converge/converge.example.yaml
+1
View File
@@ -0,0 +1 @@
/work/personal/converge/converge.service