Files
goclaw/scripts/deploy-forkdev.sh
T
61993b4387 feat(i18n): add Russian (ru) language — full parity across backend, web & desktop (#1391)
* docs(i18n): design spec for Russian (ru) language support

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(i18n): register ru locale constant and IsSupported

* feat(i18n): scaffold ru catalog (en placeholders)

* feat(i18n): translate ru backend catalog to Russian

* fix(i18n): polish ru catalog grammar per review

* feat(i18n): wire ru into system messages, config enum, parity test

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(i18n): add ru to web language constants and system-message locales

* feat(i18n): scaffold ru web locale dir (en placeholders)

* feat(i18n): wire ru resources into web i18next config

* feat(i18n): translate ru web locale to Russian (41 namespaces)

* fix(i18n): normalize ru web terminology (арендатор, привязка)

* fix(i18n): restore dropped 'named' in ru setup OAuth hint

* feat(i18n): wire ru into desktop i18next and language pickers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(i18n): add ru desktop locale (Russian, 16 namespaces)

* test(i18n): extend traces/hooks locale guards to cover ru

* chore(deploy): add repeatable fork-dev local build+deploy tooling

Adds a one-command, idempotent way to run goclaw from a locally built
image that merges fresh upstream dev with un-merged fork features
(Telegram trigger-words PR #1383 + Russian locale), independent of the
published :latest image.

- scripts/deploy-forkdev.sh: fetch origin/dev -> merge feature branches
  into local/fork-dev worktree -> push fork/dev -> build -> up -d.
- docker-compose.forkdev.yml: overlay pinning image to goclaw:forkdev
  with pull_policy:never so :latest never silently replaces the build.
- .gitignore: track the deploy helper (past deploy-*.sh), ignore the
  ../.goclaw-forkdev integration worktree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(telegram): agent-callable message reaction (message action=react)

Adds a "react" action to the message tool so an agent can set an emoji
reaction on an existing message (e.g. mark its own status post 👍 once
fully paid). Mirrors the ChannelEditor wiring: ReactionSetter capability
-> Manager.ReactToMessage -> telegram Channel.ReactToMessage (validates
against Telegram's supported reaction set; ✅/❌ are rejected).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(config): remove ignored session scoping controls (#1405)

Co-authored-by: Collective Developer <man@collective.dev>

* feat(telegram): agent-callable message reaction (message action=react) (#1407)

Adds a "react" action to the message tool so an agent can set an emoji
reaction on an existing message (e.g. mark its own status post 👍 once
fully paid). Mirrors the ChannelEditor wiring: ReactionSetter capability
-> Manager.ReactToMessage -> telegram Channel.ReactToMessage (validates
against Telegram's supported reaction set; ✅/❌ are rejected).

Co-authored-by: skensel <skensel@MacBook-Pro-skensel.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: build & publish fork image to GHCR (temporary, pre-upstream)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(memory): use 'russian' FTS config for memory_chunks recall

Memory keyword search (memory_search.go FTS + the generated tsv column)
used the 'simple' text-search config, which does no stemming. Russian
grammatical cases therefore never matched: a genitive query token
("Дианы") could not hit the stored nominative token ("Диана"), so
memory_search returned nothing for natural queries even though the fact
was saved. This is the sole recall path on deployments without an
embedding provider (ChatGPT-OAuth/codex cannot embed), so recall was
effectively dead there.

Switch both sides to the Snowball 'russian' config so index and query
lexemes stem to the same root ("диан"), restoring case-insensitive
recall. Migration 000094 recreates the generated tsv column (a GENERATED
expression cannot be ALTERed in place; ADD COLUMN backfills all rows).

Surface parity: desktop/SQLite N/A — SQLite memory search uses LIKE, a
separate mechanism unaffected by the PG regconfig.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: skensel <skensel@MacBook-Pro-skensel.local>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: ナム <namnhfreelancer@gmail.com>
Co-authored-by: Collective Developer <man@collective.dev>
2026-07-11 12:25:38 +07:00

111 lines
4.5 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# deploy-forkdev.sh — build & run goclaw from a fresh "fork-dev" integration
# branch, independent of any published image or your current checkout.
#
# What it does, every run (idempotent):
# 1. Fetch the freshest upstream dev (origin/dev).
# 2. Rebuild integration branch `local/fork-dev` = origin/dev, in a dedicated
# worktree OUTSIDE this repo (so your main checkout / git status stay clean).
# 3. Merge the un-merged fork features into it:
# - feat/telegram-wake-words (Telegram trigger-words + edit/topic)
# - the Russian-locale delta (whatever local `dev` has over upstream)
# 4. (optional) Push the result to YOUR fork's dev branch so you can see both
# features in skensell201/goclaw:dev (PUSH_FORK=1, on by default).
# 5. Build a local image `goclaw:forkdev` from that merged tree.
# 6. Bring the stack up with that image.
#
# Re-run any time to pull fresh upstream + re-merge + redeploy. On a merge
# conflict it stops and tells you which worktree to resolve in.
#
# Env knobs:
# PUSH_FORK=0 skip pushing to your fork's dev (default: 1 = push)
# FEATURE_BRANCHES space-separated feature branches to merge
# (default: "feat/telegram-wake-words")
# RU_FROM ref carrying the ru-locale work (default: "dev" = local dev)
# WT_DIR integration worktree path (default: ../.goclaw-forkdev)
# NO_BUILD=1 skip docker build/up (just prepare + push the branch)
#
set -euo pipefail
cd "$(dirname "$0")/.."
ROOT="$(pwd)"
INT_BRANCH="local/fork-dev"
FORK_REMOTE="fork"
UPSTREAM_REMOTE="origin"
WT_DIR="${WT_DIR:-${ROOT}/../.goclaw-forkdev}"
# Normalize to an absolute, ..-free path so the worktree-exists check matches
# `git worktree list --porcelain` output (which is always absolute + resolved).
WT_DIR="$(cd "$(dirname "${WT_DIR}")" && pwd)/$(basename "${WT_DIR}")"
FEATURE_BRANCHES="${FEATURE_BRANCHES:-feat/telegram-wake-words}"
RU_FROM="${RU_FROM:-dev}"
PUSH_FORK="${PUSH_FORK:-1}"
COMPOSE=(-f docker-compose.yml -f docker-compose.postgres.yml -f docker-compose.forkdev.yml)
say() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; }
die() { printf '\n\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; }
say "Fetching upstream ${UPSTREAM_REMOTE}/dev"
git fetch "${UPSTREAM_REMOTE}" dev
# ---- (re)create integration worktree pinned to fresh upstream dev ----------
# The integration branch is checked out INSIDE the worktree, so it must be moved
# via `reset --hard` there — `git branch -f` refuses to move a checked-out branch.
say "Pinning integration branch ${INT_BRANCH} to ${UPSTREAM_REMOTE}/dev"
if git worktree list --porcelain | grep -qx "worktree ${WT_DIR}"; then
git -C "${WT_DIR}" checkout -q "${INT_BRANCH}" 2>/dev/null || true
git -C "${WT_DIR}" reset --hard "${UPSTREAM_REMOTE}/dev"
git -C "${WT_DIR}" clean -fdx >/dev/null 2>&1 || true
else
git branch -f "${INT_BRANCH}" "${UPSTREAM_REMOTE}/dev"
git worktree add -f "${WT_DIR}" "${INT_BRANCH}"
fi
merge_ref() {
local ref="$1" label="$2"
say "Merging ${label} (${ref})"
git -C "${WT_DIR}" log --oneline "HEAD..${ref}" | sed 's/^/ /' || true
if ! git -C "${WT_DIR}" merge --no-edit "${ref}"; then
git -C "${WT_DIR}" merge --abort 2>/dev/null || true
die "Conflict merging ${label} (${ref}). Resolve manually:
cd ${WT_DIR}
git merge ${ref} # fix conflicts, git add, git commit
then re-run this script (it will pick up the resolved branch)."
fi
}
# ---- merge the un-merged fork features -------------------------------------
for b in ${FEATURE_BRANCHES}; do
merge_ref "${b}" "feature branch ${b}"
done
# ru locale = whatever local dev carries over upstream dev (no-op if already upstream)
merge_ref "${RU_FROM}" "ru-locale delta"
say "Integration branch ready:"
git -C "${WT_DIR}" log --oneline -8 | sed 's/^/ /'
# ---- publish to your fork's dev so both features are visible there ---------
if [ "${PUSH_FORK}" = "1" ]; then
say "Pushing ${INT_BRANCH} -> ${FORK_REMOTE}/dev (your fork)"
git push --force-with-lease "${FORK_REMOTE}" "${INT_BRANCH}:dev"
else
say "PUSH_FORK=0 — skipping fork push"
fi
if [ "${NO_BUILD:-0}" = "1" ]; then
say "NO_BUILD=1 — branch prepared & pushed; skipping docker build/up"
exit 0
fi
# ---- build local image from the merged tree & deploy -----------------------
say "Building goclaw:forkdev from ${WT_DIR}"
GOCLAW_DIR="${WT_DIR}" docker compose "${COMPOSE[@]}" build goclaw
say "Deploying"
GOCLAW_DIR="${WT_DIR}" docker compose "${COMPOSE[@]}" up -d
say "Done. Follow logs with:"
printf ' docker compose %s logs -f goclaw\n' "${COMPOSE[*]}"