Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,24 @@ jobs:
with:
dockerfile: evo-ai-core-service-community/Dockerfile
failure-threshold: error

event-names-sync:
name: EvoFlow event-names sync
runs-on: ubuntu-latest
# Always-on (~5s of pure bash). The previous "Detect relevant changes"
# gate filtered by file paths, which was silently skipped on the workflow
# that needs the gate the most: submodule-pointer bumps. `git diff
# --name-only` for a bump emits the submodule directory as a bare line
# (e.g. `evo-ai-crm-community`), which the `^evo-ai-crm-community/lib/…`
# regex did not match — so the check was bypassed precisely when one
# submodule moved to a new event_names list and the other did not.
# Cheaper to run unconditionally than to chase regex coverage for every
# diff shape git can emit.
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

- name: Check event_names sync
run: bash scripts/check-event-names-sync.sh
84 changes: 84 additions & 0 deletions scripts/check-event-names-sync.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Verifies that the CRM canonical event-names list and the evo-flow TS mirror
# stay in lockstep. Pure Bash + grep — no Ruby/Node/jq runtime required.
#
# Ruby source: evo-ai-crm-community/lib/events/evo_flow_event_names.rb
# (EvoFlow::EVENT_NAMES, %w[...] block)
# TS source: evo-flow/src/modules/events/event-names.enum.ts
# (export const EVENT_NAMES = [ 'foo', 'bar', ... ] as const;)
#
# Exit 0 on match. Exit 1 on any of:
# - the two lists disagree (DIVERGENT)
# - the lists agree but the combined size != EXPECTED_COUNT (DIVERGENT count)
# Exit 2 on missing source files.
#
# When the canonical list legitimately grows, bump EXPECTED_COUNT here in the
# same PR that adds the entries — this is the gate that catches a 17-entry
# Ruby list + 17-entry TS list that nobody told the integration story about.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
EXPECTED_COUNT=16

RUBY_FILE="$REPO_ROOT/evo-ai-crm-community/lib/events/evo_flow_event_names.rb"
TS_FILE="$REPO_ROOT/evo-flow/src/modules/events/event-names.enum.ts"

for f in "$RUBY_FILE" "$TS_FILE"; do
if [[ ! -f "$f" ]]; then
echo "event_names_sync: ERROR — file not found: $f" >&2
exit 2
fi
done

# Ruby side: scope to the %w[ ... ] block, then tokenise on whitespace.
ruby_list=$(
awk '
/%w\[/ { capture = 1; sub(/.*%w\[/, "") }
capture {
line = $0
if (sub(/\].*/, "", line)) { print line; capture = 0; exit }
print line
}
' "$RUBY_FILE" | tr -s '[:space:]' '\n' | sed '/^$/d' | sort -u
)

Comment on lines +37 to +46
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: TS block detection for EVENT_NAMES does not handle type annotations or alternative spacing around the =.

The current awk guard /EVENT_NAMES[[:space:]]*=[[:space:]]*\/ only matches when EVENT_NAMES is immediately followed by = and [. With a type annotation or inline comment (e.g. export const EVENT_NAMES: readonly string[] = [), the match fails and the script sees an empty list, causing confusing sync failures. Consider allowing arbitrary chars between the name and = (e.g. /EVENT_NAMES[^=]*=[[:space:]]*\[/) so annotations/comments don’t break extraction.

# TS side: scope to the EVENT_NAMES = [ ... ] block, THEN extract single-quoted
# literals. Anchoring to the block prevents a stray single-quoted string in a
# comment, JSDoc, or future export from leaking into the canonical set.
ts_list=$(
awk '
/EVENT_NAMES[[:space:]]*=[[:space:]]*\[/ { capture = 1 }
capture {
line = $0
if (match(line, /\]/)) { print substr(line, 1, RSTART - 1); capture = 0; exit }
print line
}
' "$TS_FILE" | grep -oE "'[a-z][a-z._]*'" | tr -d "'" | sort -u
)
Comment on lines +58 to +59
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): The TS event-name regex is very restrictive and will silently ignore many valid identifier shapes.

grep -oE "'[a-z][a-z._]*'" only matches lowercase letters plus . and _, so any event name with digits, hyphens, uppercase letters, etc. is excluded from ts_list. That can mask divergence between the TS file and the other list. Please broaden the regex (e.g. "'[-A-Za-z0-9._]+'") or otherwise match the full set of valid event-name characters so the check fails whenever either file adds an out-of-pattern name.

Suggested change
' "$TS_FILE" | grep -oE "'[a-z][a-z._]*'" | tr -d "'" | sort -u
)
' "$TS_FILE" | grep -oE "'[-A-Za-z0-9._]+'" | tr -d "'" | sort -u
)


ruby_tmp=$(mktemp)
ts_tmp=$(mktemp)
diff_tmp=$(mktemp)
trap 'rm -f "$ruby_tmp" "$ts_tmp" "$diff_tmp"' EXIT
printf '%s\n' "$ruby_list" > "$ruby_tmp"
printf '%s\n' "$ts_list" > "$ts_tmp"

if ! diff -u "$ruby_tmp" "$ts_tmp" > "$diff_tmp"; then
echo "event_names_sync: DIVERGENT — see diff below"
echo " (--- Ruby: $RUBY_FILE)"
echo " (+++ TS: $TS_FILE)"
cat "$diff_tmp"
exit 1
fi

count=$(wc -l < "$ruby_tmp" | tr -d '[:space:]')
if [[ "$count" != "$EXPECTED_COUNT" ]]; then
echo "event_names_sync: DIVERGENT — lists match each other but count is $count (expected $EXPECTED_COUNT)."
echo "If this growth is intentional, bump EXPECTED_COUNT in $SCRIPT_DIR/check-event-names-sync.sh in the same PR."
exit 1
fi

echo "event_names_sync: OK ($EXPECTED_COUNT entries)"
exit 0
Loading