Skip to content

Commit

Permalink
Merge branch 'ci-make-check'
Browse files Browse the repository at this point in the history
* Branch commit log:
  .github/workflows/testing.yml: run make check without V=1
  .github/workflows/testing.yml: install jj-0.23.0, fzf-0.56.3
	* .github/workflows/testing.yml: comment-out # ./testing.sh -x
	* .github/workflows/testing.yml: work around missing ~/.local/bin/
		The Github action environment has ~/.local/bin/ in $PATH,
		without that directory actually existing.
	*.github/workflows/testing.yml: install fzf-0.56.3 directly
	*.github/workflows/testing.yml: install jj-0.23.0 directly
  testing.sh: use ERRORINERROR testing
	* Test that jj-fzf describe does not continue with $EDITOR
	  once an invalid change_id has been encountered.
	* Use the string 'ERRORINERROR' to catch $EDITOR invocations
  testing.sh: fix testing conditions and assertions
  .github/workflows/testing.yml: run verbose make and enable IRC bot on errors
  .github/workflows/testing.yml: add CI checks
  .github/workflows/testing.yml: add RFC-1459 based simple message IRC bot
	* GITHUB: workflows/ircbot.py: support -R for initiating repository name
	* GITHUB: workflows/ircbot.py: set "YYBOT" as bot name
	* GITHUB: workflows/ircbot.py: adjust mIRC RESET code to work without digits
	  The Thunderbird IRC client hides text if mIRC default-fg+default-bg is sent,
	  so instead, skip the numbers during reset, color codes are currently not
	  followed by any digits anyway.
	* GITHUB: workflows/ircbot.py: allow colorized NAME DEPARTMENT STATUS prefixing
	* GITHUB: workflows/ircbot.py: add RFC-1459 based simple message IRC bot
	Imported from:
	https://github.com/tim-janik/anklang/blob/f0e7a8bd91588d3e968e4747a44f06024b0820df/.github/workflows/ircbot.py

Signed-off-by: Tim Janik <[email protected]>
  • Loading branch information
tim-janik committed Dec 1, 2024
2 parents d6cd311 + 7697397 commit 8ae827d
Show file tree
Hide file tree
Showing 3 changed files with 290 additions and 22 deletions.
211 changes: 211 additions & 0 deletions .github/workflows/ircbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#!/usr/bin/env python3
# This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
import sys, os, re, socket, select, time

# https://datatracker.ietf.org/doc/html/rfc1459

server = "irc.libera.chat"
port = 6667
channel = "#anklang2"
nickname = "YYBOT"
ircsock = None
timeout = 150
wait_timeout = 15000

def colors (how):
E = '\u001b['
C = '\u0003'
if how == 0: # NONE
d = { 'YELLOW': '', 'ORANGE': '', 'RED': '', 'GREEN': '', 'CYAN': '', 'BLUE': '', 'MAGENTA': '', 'RESET': '' }
elif how == 1: # ANSI
d = { 'YELLOW': E+'93m', 'ORANGE': E+'33m', 'RED': E+'31m', 'GREEN': E+'32m', 'CYAN': E+'36m', 'BLUE': E+'34m', 'MAGENTA': E+'35m', 'RESET': E+'m' }
elif how == 2: # mIRC
d = { 'YELLOW': C+'08,99', 'ORANGE': C+'07,99', 'RED': C+'04,99', 'GREEN': C+'03,99', 'CYAN': C+'10,99', 'BLUE': C+'12,99', 'MAGENTA': C+'06,99', 'RESET': C+'' }
from collections import namedtuple
colors = namedtuple ("Colors", d.keys()) (*d.values())
return colors

def status_color (txt, c):
ER = r'false|\bno\b|\bnot|\bfail|fatal|error|\bwarn|\bbug|\bbad|\bred|broken'
OK = r'true|\byes|\bok\b|success|\bpass|good|\bgreen'
if re.search (ER, txt, flags = re.IGNORECASE):
return c.RED
if re.search (OK, txt, flags = re.IGNORECASE):
return c.GREEN
return c.YELLOW

def format_msg (args, how = 2):
msg = ' '.join (args.message)
c = colors (how)
if args.S:
msg = '[' + status_color (args.S, c) + args.S.upper() + c.RESET + '] ' + msg
if args.D:
msg = c.CYAN + args.D + c.RESET + ' ' + msg
if args.U:
msg = c.ORANGE + args.U + c.RESET + ' ' + msg
if args.R:
msg = '[' + c.BLUE + args.R + c.RESET + '] ' + msg
return msg

def sendline (text):
global args
if not args.quiet:
print (text, flush = True)
msg = text + "\r\n"
ircsock.send (msg.encode ('utf8'))

def connect (server, port):
global ircsock
ircsock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
ircsock.connect ((server, port))
ircsock.setblocking (True) # False

def canread (milliseconds):
rs, ws, es = select.select ([ ircsock ], [], [], milliseconds * 0.001)
return ircsock in rs

readall_buffer = b'' # unterminated start of next line
def readall (milliseconds = timeout):
global readall_buffer
gotlines = False
while canread (milliseconds):
milliseconds = 0
buf = ircsock.recv (128 * 1024)
if len (buf) == 0:
# raise (Exception ('SOCKET BROKEN:', 'readable but has 0 data'))
break # socket closed
gotlines = True
readall_buffer += buf
if readall_buffer.find (b'\n') >= 0:
lines, readall_buffer = readall_buffer.rsplit (b'\n', 1)
lines = lines.decode ('utf8', 'replace')
for l in lines.split ('\n'):
if l:
gotline (l.rstrip())
return gotlines

def gotline (msg):
global args
if not args.quiet:
print (msg, flush = True)
cmdargs = re.split (' +', msg)
if cmdargs:
prefix = ''
if cmdargs[0] and cmdargs[0][0] == ':':
prefix = cmdargs[0]
cmdargs = cmdargs[1:]
if not cmdargs:
return
gotcmd (prefix, cmdargs[0], cmdargs[1:])

expecting_commands = []
check_cmds = []
def gotcmd (prefix, cmd, args):
global expecting_commands, check_cmds
if check_cmds:
try: check_cmds.remove (cmd)
except: pass
if cmd in expecting_commands:
expecting_commands = []
if cmd == 'PING':
return sendline ('PONG ' + ' '.join (args))

def expect (what = []):
global expecting_commands
expecting_commands = what if isinstance (what, (list, tuple)) else [ what ]
while readall (wait_timeout) and expecting_commands: pass
if expecting_commands:
raise (Exception ('MISSING REPLY: ' + ' | '.join (expecting_commands)))

usage_help = '''
Simple IRC bot for short messages.
A password for authentication can be set via $IRCBOT_PASS.
'''

def parse_args (sysargs):
import argparse
global server, port, nickname
parser = argparse.ArgumentParser (description = usage_help)
parser.add_argument ('message', metavar = 'messages', type = str, nargs = '*',
help = 'Message to post on IRC')
parser.add_argument ('-j', metavar = 'CHANNEL', default = '',
help = 'Channel to join on IRC')
parser.add_argument ('-J', metavar = 'CHANNEL', default = '',
help = 'Message channel without joining')
parser.add_argument ('-n', metavar = 'NICK', default = nickname,
help = 'Nickname to use on IRC [' + nickname + ']')
parser.add_argument ('-s', metavar = 'SERVER', default = server,
help = 'Server for IRC connection [' + server + ']')
parser.add_argument ('-p', metavar = 'PORT', default = port, type = int,
help = 'Port to connect to [' + str (port) + ']')
parser.add_argument ('-l', action = "store_true",
help = 'List channels')
parser.add_argument ('-R', metavar = 'REPOSITORY', default = '',
help = 'Initiating repository name')
parser.add_argument ('-U', metavar = 'NAME', default = '',
help = 'Initiating user name')
parser.add_argument ('-D', metavar = 'DEPARTMENT', default = '',
help = 'Initiating department')
parser.add_argument ('-S', metavar = 'STATUS', default = '',
help = 'Initiating status code')
parser.add_argument ('--ping', action = "store_true",
help = 'Require PING/PONG after connecting')
parser.add_argument ('--quiet', '-q', action = "store_true",
help = 'Avoid unnecessary output')
args = parser.parse_args (sysargs)
#print ('ARGS:', repr (args), flush = True)
return args

args = parse_args (sys.argv[1:])
if args.message and not args.quiet:
print (format_msg (args, 1))
connect (args.s, args.p)
readall (500)

ircbot_pass = os.getenv ("IRCBOT_PASS")
if ircbot_pass:
sendline ("PASS " + ircbot_pass)
sendline ("USER " + args.n + " localhost " + server + " :" + args.n)
readall()
sendline ("NICK " + args.n)
expect ('251') # LUSER reply

if args.ping:
sendline ("PING :pleasegetbacktome")
expect ('PONG')

if args.j:
#sendline ("PING :ircbotpyping")
#expect ('PONG')
sendline ("JOIN " + args.j)
expect ('JOIN')

msg = format_msg (args)
for line in re.split ('\n ?', msg):
channel = args.j or args.J or args.n
if line:
sendline ("PRIVMSG " + channel + " :" + line)
readall()

if args.l:
sendline ("LIST")
check_cmds = [ '322' ]
expect ('323')
if check_cmds:
# empty list, retry after 60seconds
time.sleep (30)
check_cmds = [ 'PING' ]
readall()
if check_cmds:
sendline ("PING :pleasegetbacktome")
expect ('PONG')
time.sleep (30)
readall()
sendline ("LIST")
expect ('323')

readall (500)

sendline ("QUIT :Bye Bye")
expect (['QUIT', 'ERROR'])
ircsock.close()
50 changes: 50 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0

# Linting: xclip -sel c <.github/workflows/testing.yml # https://rhysd.github.io/actionlint/

on:
push:
branches: [ 'trunk', 'next', 'wip/**' ]
# tags: [ 'v[0-9]+.[0-9]+.[0-9]+*' ]
pull_request:
branches: [ 'trunk', 'next' ]

jobs:

MakeCheck:
runs-on: ubuntu-latest
steps:
- { uses: actions/[email protected], with: { fetch-depth: 0, submodules: recursive, github-server-url: 'https://github.com' } }
- run: git fetch -f --tags && git describe --long # Fix actions/checkout#290
- run: |
curl -s -L https://github.com/junegunn/fzf/releases/download/v0.56.3/fzf-0.56.3-linux_amd64.tar.gz |
tar zxvf - -C ~/.cargo/bin/ fzf
fzf --version
- run: |
curl -s -L https://github.com/martinvonz/jj/releases/download/v0.23.0/jj-v0.23.0-x86_64-unknown-linux-musl.tar.gz |
tar zxvf - -C ~/.cargo/bin/ ./jj
jj --version
#- run: ./testing.sh -x
- run: |
make check
Ping-IRC:
if: always()
needs: [MakeCheck]
runs-on: ubuntu-latest
steps:
- { uses: actions/[email protected], with: { fetch-depth: 0, github-server-url: 'https://github.com' } }
- run: git fetch -f --tags && git describe --long # Fix actions/checkout#290
- name: Check Jobs
run: |
echo '${{ needs.MakeCheck.result }}'
[[ ${{ needs.MakeCheck.result }} =~ success|skipped ]]
- name: Ping IRC
if: ${{ always() && !env.ACT }}
run: |
R='${{ github.repository }}' && R=${R#*/}
B='${{ github.ref }}' && B=${B#refs/heads/}
S='${{ job.status }}' && URL='${{ github.event.head_commit.url }}'
A='${{ github.actor }}' && B="$(git branch --show-current)"
MSG=$(git log -1 --format='%s')
.github/workflows/ircbot.py -q -j "#Anklang" -R "$R" -U "$A" -D "$B" -S "$S" "$MSG" "$URL"
51 changes: 29 additions & 22 deletions testing.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ SCRIPTDIR="$(readlink -f "$0")" && SCRIPTDIR="${SCRIPTDIR%/*}"
TEST=
die()
{
set +x
R='\033[31m'
Z='\033[0m'
[ -n "$*" ] &&
echo -e "$SCRIPTNAME: $R**ERROR**:${TEST:+ $TEST:}$Z ${*:-aborting}" >&2
exit 127
( set +x
R='\033[31m'
Z='\033[0m'
[ -n "$*" ] &&
echo -e "$SCRIPTNAME: $R**ERROR**:${TEST:+ $TEST:}$Z ${*:-aborting}"
) >&2
exit 77
}
DEVERR=/dev/null
[[ " $* " =~ -x ]] && {
DEVERR=/dev/stderr
PS4="$SCRIPTNAME:\${LINENO}: "
PS4="+ $SCRIPTNAME:\${LINENO}: "
set -x
}

Expand All @@ -29,17 +30,19 @@ jj-fzf()
}
assert1error()
{
local L="$(wc -l <<<"$*")"
test "$L" == 1 ||
die $'output exceeds one line: \\\n' "$*"
grep -Eq '\bERROR:' <<<"$*" ||
die "output contains no ERROR message: $*"
}
assert0error()
{
grep -Eq '\bERROR:' <<<"$*" &&
! grep -Eq '\bERROR:' <<<"$*" ||
die "output contains an ERROR message: $*"
}
assert0errorinerror()
{
! grep -Eq 'ERRORINERROR' <<<"$*" ||
die "output contains an ERRORINERROR message: $*"
}
assert_zero()
{
test "$1" == 0 ||
Expand All @@ -50,10 +53,9 @@ assert_nonzero()
test "$1" != 0 ||
die "command exit status failed to be non-zero: $1"
}
OK()
TEST_OK()
{
printf " %-7s" OK
echo "${TEST:-}$*"
printf ' %-7s %s\n' OK "$*"
}
mkcommits() # mkcommits A B 'A|B ->C'
( # Create empty test commits with bookamrks
Expand Down Expand Up @@ -83,20 +85,25 @@ clear_repo()
# == TESTS ==
TEST='jj-fzf-functions-fail-early'
clear_repo
( set +e
OUT="$(export EDITOR=false JJ_CONFIG='' && jj-fzf describe 'zzzzaaaa' 2>&1)"
assert_nonzero $?
( set -e
# Test that jj-fzf describe does not continue with $EDITOR
# once an invalid change_id has been encountered.
export JJ_CONFIG='' EDITOR='echo ERRORINERROR'
OUT="$(set +x; jj-fzf describe 'zzzzaaaa' 2>&1)" && E=$? || E=$?
assert_nonzero $E
assert1error "$OUT"
) && OK
assert0errorinerror "$OUT"
); TEST_OK "$TEST"

TEST='jj-fzf-new'
clear_repo && mkcommits A B
( set +e
( set -e
export JJ_CONFIG=''
WC="$(jj log --no-pager --ignore-working-copy --no-graph -T commit_id -r @)"
OUT="$(export JJ_CONFIG='' && jj-fzf new A B 2>&1)"
assert_zero $?
OUT="$(set +x; jj-fzf new A B 2>&1)" && E=$? || E=$?
assert_zero $E
assert0error "$OUT"
NEW="$(jj log --no-pager --ignore-working-copy --no-graph -T commit_id -r @)"
test "$WC" != "$NEW" ||
die "failed to create new revision"
) && OK
); TEST_OK "$TEST"

0 comments on commit 8ae827d

Please sign in to comment.