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: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
CONVERGE_HASKELL_TOOLS_IMAGE: ghcr.io/flipstone/haskell-tools:debian-ghc-9.10.3-5d6640d STACK_ROOT: ${{ github.workspace }}/.stack-root
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
path: . - name: Install Stack
run: |
curl -sSL https://get.haskellstack.org/ | sh
- name: Cache Stack - name: Cache Stack
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: .stack-root path: .stack-root
key: stack-${{ runner.os }}-${{ hashFiles('stack.yaml', 'package.yaml') }} key: stack-v2-${{ runner.os }}-${{ hashFiles('stack.yaml', 'package.yaml') }}
restore-keys: stack-${{ runner.os }}- restore-keys: stack-v2-${{ runner.os }}-
- name: Build - name: Build
run: | run: |
mkdir -p .stack-root build mkdir -p build
./hs stack build --copy-bins --local-bin-path /work/build stack build --copy-bins --local-bin-path ./build
- name: Compress with UPX - name: Compress with UPX
run: | run: |
@@ -39,9 +41,55 @@ jobs:
fi fi
${UPX_DIR}/upx build/converge ${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 - name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: converge name: converge
path: build/converge path: build/converge
compression-level: 0
+2
View File
@@ -12,3 +12,5 @@ dist-newstyle/
.pi/ .pi/
converge-relay/.go-cache/ converge-relay/.go-cache/
converge-relay/build/ converge-relay/build/
*.pkg.tar
pkg/
+1 -1
View File
@@ -20,7 +20,7 @@ test/GitPullSpec.hs # Pull/rebase tests
test/GitSafetySpec.hs # Operation-in-progress detection tests test/GitSafetySpec.hs # Operation-in-progress detection tests
SPEC.md # Human-readable specifications SPEC.md # Human-readable specifications
converge.example.yaml # Example config file converge.example.yaml # Example config file
converge.service # systemd unit file converge.service # systemd user service (daemon mode)
``` ```
## Architecture ## 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 ## 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 ## 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 ## 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 ### 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] [Service]
Type=simple 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 Restart=on-failure
RestartSec=10 RestartSec=10
+4 -4
View File
@@ -1,13 +1,13 @@
name: converge 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 synopsis: Auto-sync a git repository by watching for changes, committing, and pulling
description: | description: |
Converge watches a git repository for file changes, automatically Converge watches a git repository for file changes, automatically
commits them, and pulls from the remote. It notifies the user via commits them, and pulls from the remote. It notifies the user via
libnotify/notify-send when merge conflicts arise. libnotify/notify-send when merge conflicts arise.
author: James Brechtel author: James Leonard-Brechtel
maintainer: james@flipstone.com maintainer: james@leonard-brechtel.com
copyright: 2026 James Brechtel copyright: 2026 James Leonard-Brechtel
license: BSD-3-Clause license: BSD-3-Clause
default-extensions: default-extensions:
+12
View File
@@ -20,3 +20,15 @@ echo "Compressing with UPX..."
' '
echo "Done: $(ls -lh build/converge | awk '{print $5}')" 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 then pure addResult
else do else do
hostname <- init <$> readProcess "hostname" [] "" 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. -- | Pull from the configured remote, rebasing onto the tracked upstream branch.
gitPull :: Logger -> RepoConfig -> IO (ExitCode, Text, Text) gitPull :: Logger -> RepoConfig -> IO (ExitCode, Text, Text)
@@ -585,7 +587,7 @@ connectRelay :: Text -> Logger -> [RepoConfig] -> IO ()
connectRelay url logger repos = do connectRelay url logger repos = do
logMsg logger Info ("relay: connecting to " <> url) logMsg logger Info ("relay: connecting to " <> url)
req <- parseRequest (T.unpack url) req <- parseRequest (T.unpack url)
manager <- newManager tlsManagerSettings manager <- newManager tlsManagerSettings{managerResponseTimeout = responseTimeoutNone}
let loop delay = do let loop delay = do
let delaySec = min delay (60 :: Int) let delaySec = min delay (60 :: Int)
result <- try $ 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