Compare commits
23 Commits
bdd9e663b9
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 26df70f9f5 | |||
| 96e66be50e | |||
| c0b566fc70 | |||
| bdcb3d1d20 | |||
| cc2df21796 | |||
| f546ee450e | |||
| 6d165a3912 | |||
| 3edb5c33ee | |||
| ab87e943f3 | |||
| 39f85a3900 | |||
| af0f03758a | |||
| dee7c95542 | |||
| c7a3690129 | |||
| 0d8cddb9be | |||
| 9bca886629 | |||
| edc6d5acf3 | |||
| 4473dc9164 | |||
| 29a984934d | |||
| ef49f30c88 | |||
| 89e7729f2c | |||
| 1ad993b9d1 | |||
| 27f4e1fd2c | |||
| 657855bd41 |
@@ -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
|
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../.pi/skills/gitea-actions/scripts/gitea-actions.sh
|
||||||
Executable
+13
@@ -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
@@ -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 $
|
||||||
|
|||||||
@@ -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
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
/work/personal/converge/converge.example.yaml
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
/work/personal/converge/converge.service
|
||||||
Reference in New Issue
Block a user