Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
1ce809d
update py webhook action to use a channel-specific url; add debug
qrkourier Jun 29, 2025
9023ee8
stop pinning py sdk version so we can always test latest
qrkourier Jul 23, 2025
562796e
deprecate channel input because incoming webhooks are now locked to a…
qrkourier Jul 24, 2025
a6b6c32
print env and event
qrkourier Jul 24, 2025
f31c36b
aggressively debug inconsistent segfault
qrkourier Jul 24, 2025
21f81b2
add py requirements file
qrkourier Jul 24, 2025
1bad8a9
don't raise bash exceptions while printing debug info
qrkourier Jul 24, 2025
831b9f8
stop setting pipefail
qrkourier Jul 24, 2025
e7e9693
also debug docker action
qrkourier Jul 24, 2025
7258c2f
fiddle escapes
qrkourier Jul 24, 2025
7a7b087
use env file
qrkourier Jul 24, 2025
91faae0
debug in bash container
qrkourier Jul 24, 2025
b3311ca
beef up backtrace script
qrkourier Jul 24, 2025
ee9e398
add interpreter to zhook.py
qrkourier Jul 24, 2025
bc7503f
interpolate vars in heredoc
qrkourier Jul 24, 2025
5449a48
test the script with valgrind in docker too
qrkourier Jul 25, 2025
d481575
pass json as an encoding to avoid word-splitting errors
qrkourier Jul 25, 2025
d726aa1
add required vars to the docker env
qrkourier Jul 25, 2025
791a3dd
tidy whitespace
qrkourier Jul 29, 2025
1c77879
validate json more robustly and hint at contents if invalid
qrkourier Aug 21, 2025
5ea63f2
trims redundant quotes
qrkourier Aug 21, 2025
f97e3a2
load identity in context manager to shut down cleanly
qrkourier Aug 21, 2025
dc0e801
move lint exceptions to project file so contributors can follow same …
qrkourier Aug 21, 2025
bad94e7
run zhook self-checks when owned by netfoundry, too
qrkourier Aug 21, 2025
ffc9fca
upload debug artifacts
qrkourier Aug 22, 2025
b5a1a2e
fix request body format
qrkourier Aug 22, 2025
58c7d9b
checkpoint
qrkourier Aug 22, 2025
80db146
test a version from test pypi
qrkourier Sep 29, 2025
1890cf6
encode event json to avoid whitespace errors
qrkourier Sep 29, 2025
0c15cb9
use ziti py v1.4.0
qrkourier Sep 29, 2025
a8ac1ea
Revert "use ziti py v1.4.0"
qrkourier Sep 29, 2025
b2b0c49
use ziti py 1.4.1
qrkourier Sep 29, 2025
713f67b
refine zhook.py
qrkourier Sep 30, 2025
b3d7fff
update py webhook action to use a channel-specific url; add debug
qrkourier Jun 29, 2025
04d43f0
add input validation and further debugging
qrkourier Jul 24, 2025
1e39dc4
update py webhook action to use a channel-specific url; add debug
qrkourier Jun 29, 2025
433a185
also debug docker action
qrkourier Jul 24, 2025
f529ce7
fiddle escapes
qrkourier Jul 24, 2025
eac677f
use env file
qrkourier Jul 24, 2025
376fb77
debug in bash container
qrkourier Jul 24, 2025
f93288a
beef up backtrace script
qrkourier Jul 24, 2025
6b763ca
add interpreter to zhook.py
qrkourier Jul 24, 2025
39f5070
interpolate vars in heredoc
qrkourier Jul 24, 2025
30f15f3
test the script with valgrind in docker too
qrkourier Jul 25, 2025
baedce7
pass json as an encoding to avoid word-splitting errors
qrkourier Jul 25, 2025
be695b6
add required vars to the docker env
qrkourier Jul 25, 2025
6c6c433
validate json more robustly and hint at contents if invalid
qrkourier Aug 21, 2025
2b96139
move lint exceptions to project file so contributors can follow same …
qrkourier Aug 21, 2025
3826bbe
checkpoint
qrkourier Aug 22, 2025
30a284e
encode event json to avoid whitespace errors
qrkourier Sep 29, 2025
d47edac
Merge branch 'debug-py-action' of github.com:openziti/ziti-mattermost…
qrkourier Oct 20, 2025
eaddcf0
add test mode
qrkourier Oct 20, 2025
b5cb599
finish Dockerfile
qrkourier Nov 10, 2025
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
10 changes: 10 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[flake8]
ignore =
E111,
E114,
E121,
E501

# You can add project-wide settings here later, e.g.:
# max-line-length = 120
# exclude = .git,__pycache__,.venv
2 changes: 1 addition & 1 deletion .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ jobs:
- name: Lint
run: |
pip install flake8
flake8 --ignore E111,E114,E121,E501 zhook.py
flake8 --config ./.flake8 ./zhook.py
246 changes: 220 additions & 26 deletions .github/workflows/zhook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,49 +18,243 @@ on:

jobs:
mattermost-ziti-webhook:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
name: POST Webhook
steps:
- uses: actions/checkout@v4
- name: run hook directly
- name: Debug Environment
uses: hmarr/debug-action@v3

- name: Install Debug Tools
shell: bash
run: sudo apt-get install --yes valgrind gdb

- name: Checkout
uses: actions/checkout@v4

- name: Run Python Script Directly
if: |
github.repository_owner == 'openziti'
(github.repository_owner == 'openziti' || github.repository_owner == 'netfoundry')
&& ((github.event_name != 'pull_request_review')
|| (github.event_name == 'pull_request_review' && github.event.review.state == 'approved'))
env:
INPUT_ZITIID: ${{ secrets.ZITI_MATTERMOST_IDENTITY }}
INPUT_WEBHOOKURL: ${{ secrets.ZHOOK_URL }}
INPUT_WEBHOOKURL: ${{ secrets.ZHOOK_URL_DEV_NOTIFICATIONS }}
INPUT_EVENTJSON: ${{ toJson(github.event) }}
INPUT_SENDERUSERNAME: GitHubZ
INPUT_DESTCHANNEL: dev-notifications
INPUT_SENDERICONURL: https://github.com/fluidicon.png
INPUT_ZITILOGLEVEL: 6
shell: bash
run: |
pip install --upgrade requests openziti
set +e
if [ "${ZHOOK_VALGRIND}" = "true" ]; then
echo "⚠️ running with valgrind..."
sudo apt-get update
sudo apt-get install -y valgrind
echo "⚙️ Running under valgrind..."
valgrind --tool=memcheck --leak-check=full \
--show-leak-kinds=all --track-origins=yes \
python ./zhook.py
else
set -o pipefail
set -o xtrace
pip install --user --upgrade --requirement ./requirements.txt
# in case valgrind catches a segfault, it will write a core file in ./vgcore.%p
valgrind \
--verbose \
--log-file=${GITHUB_WORKSPACE}/direct-valgrind-%p-%n.log \
--leak-check=yes \
python ./zhook.py
fi
echo "⚠️ zhook.py exited with code $?, continuing..."
exit 0

- uses: ./ # use self to bring the pain forward
name: run action
- name: Run in Docker with Core Dumps
if: |
github.repository_owner == 'openziti'
always()
&& (github.repository_owner == 'openziti' || github.repository_owner == 'netfoundry')
&& ((github.event_name != 'pull_request_review')
|| (github.event_name == 'pull_request_review' && github.event.review.state == 'approved'))
shell: bash
env:
INPUT_ZITIID: ${{ secrets.ZITI_MATTERMOST_IDENTITY }}
INPUT_WEBHOOKURL: ${{ secrets.ZHOOK_URL_DEV_NOTIFICATIONS }}
INPUT_SENDERUSERNAME: GitHubZ
INPUT_SENDERICONURL: https://github.com/fluidicon.png
INPUT_ZITILOGLEVEL: 6
run: |
set -o pipefail
set -o xtrace

cat > /tmp/docker.env << EOF
INPUT_ZITIID=${INPUT_ZITIID}
INPUT_WEBHOOKURL=${INPUT_WEBHOOKURL}
INPUT_EVENTJSON=$(base64 -w 0 <<< '${{ toJson(github.event) }}')
INPUT_SENDERUSERNAME=${INPUT_SENDERUSERNAME}
INPUT_SENDERICONURL=${INPUT_SENDERICONURL}
INPUT_ZITILOGLEVEL=${INPUT_ZITILOGLEVEL}
GITHUB_WORKSPACE=${GITHUB_WORKSPACE}
GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME}
GITHUB_ACTION_REPOSITORY=${GITHUB_ACTION_REPOSITORY}
EOF

# configure the kernel to write core dumps to the workspace directory that is writable by the container in case there is a segfault valgrind cannot catch in a vgcore.%p
sudo sysctl -w kernel.core_pattern="${GITHUB_WORKSPACE}/core.%e.%p.%t"

# build the action's container image so we can source it for the debug image
docker build -t zhook-action .
docker build -t zhook-action-dbg -f debug.Dockerfile .
docker run --rm \
--volume "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \
--workdir "${GITHUB_WORKSPACE}" \
--env-file /tmp/docker.env \
--entrypoint=/bin/bash \
zhook-action-dbg -euxo pipefail -c '
ulimit -c unlimited;
exec valgrind \
--verbose \
--log-file=${GITHUB_WORKSPACE}/docker-valgrind-%p-%n.log \
--leak-check=yes \
python /app/zhook.py;
'

- name: Run in Docker with Core Dumps
if: |
always()
&& (github.repository_owner == 'openziti' || github.repository_owner == 'netfoundry')
&& ((github.event_name != 'pull_request_review')
|| (github.event_name == 'pull_request_review' && github.event.review.state == 'approved'))
shell: bash
env:
INPUT_ZITIID: ${{ secrets.ZITI_MATTERMOST_IDENTITY }}
INPUT_WEBHOOKURL: ${{ secrets.ZHOOK_URL_DEV_NOTIFICATIONS }}
INPUT_SENDERUSERNAME: GitHubZ
INPUT_SENDERICONURL: https://github.com/fluidicon.png
INPUT_ZITILOGLEVEL: 6
run: |
set -o pipefail
set -o xtrace

cat > /tmp/docker.env << EOF
INPUT_ZITIID=${INPUT_ZITIID}
INPUT_WEBHOOKURL=${INPUT_WEBHOOKURL}
INPUT_EVENTJSON=$(base64 -w 0 <<< '${{ toJson(github.event) }}')
INPUT_SENDERUSERNAME=${INPUT_SENDERUSERNAME}
INPUT_SENDERICONURL=${INPUT_SENDERICONURL}
INPUT_ZITILOGLEVEL=${INPUT_ZITILOGLEVEL}
GITHUB_WORKSPACE=${GITHUB_WORKSPACE}
GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME}
GITHUB_ACTION_REPOSITORY=${GITHUB_ACTION_REPOSITORY}
EOF

# configure the kernel to write core dumps to the workspace directory that is writable by the container in case there is a segfault valgrind cannot catch in a vgcore.%p
sudo sysctl -w kernel.core_pattern="${GITHUB_WORKSPACE}/core.%e.%p.%t"

# build the action's container image so we can source it for the debug image
docker build -t zhook-action .
docker build -t zhook-action-dbg -f debug.Dockerfile .
docker run --rm \
--volume "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \
--workdir "${GITHUB_WORKSPACE}" \
--env-file /tmp/docker.env \
--entrypoint=/bin/bash \
zhook-action-dbg -euxo pipefail -c '
ulimit -c unlimited;
exec valgrind \
--verbose \
--log-file=${GITHUB_WORKSPACE}/docker-valgrind-%p-%n.log \
--leak-check=yes \
python /app/zhook.py;
'

- uses: ./
name: Run as a GH Action from the Local Checkout
if: |
always()
&& (github.repository_owner == 'openziti' || github.repository_owner == 'netfoundry')
&& ((github.event_name != 'pull_request_review')
|| (github.event_name == 'pull_request_review' && github.event.review.state == 'approved'))
shell: bash
env:
INPUT_ZITIID: ${{ secrets.ZITI_MATTERMOST_IDENTITY }}
INPUT_WEBHOOKURL: ${{ secrets.ZHOOK_URL_DEV_NOTIFICATIONS }}
INPUT_EVENTJSON: ${{ toJson(github.event) }}
INPUT_SENDERUSERNAME: GitHubZ
INPUT_SENDERICONURL: https://github.com/fluidicon.png
INPUT_ZITILOGLEVEL: 6
run: |
set -o pipefail
set -o xtrace

cat > /tmp/docker.env << EOF
INPUT_ZITIID=${INPUT_ZITIID}
INPUT_WEBHOOKURL=${INPUT_WEBHOOKURL}
INPUT_EVENTJSON=$(base64 -w 0 <<< '${{ toJson(github.event) }}')
INPUT_SENDERUSERNAME=${INPUT_SENDERUSERNAME}
INPUT_SENDERICONURL=${INPUT_SENDERICONURL}
INPUT_ZITILOGLEVEL=${INPUT_ZITILOGLEVEL}
GITHUB_WORKSPACE=${GITHUB_WORKSPACE}
GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME}
GITHUB_ACTION_REPOSITORY=${GITHUB_ACTION_REPOSITORY}
EOF

# configure the kernel to write core dumps to the workspace directory that is writable by the container in case there is a segfault valgrind cannot catch in a vgcore.%p
sudo sysctl -w kernel.core_pattern="${GITHUB_WORKSPACE}/core.%e.%p.%t"

# build the action's container image so we can source it for the debug image
docker build -t zhook-action .
docker build -t zhook-action-dbg -f debug.Dockerfile .
docker run --rm \
--volume "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}" \
--workdir "${GITHUB_WORKSPACE}" \
--env-file /tmp/docker.env \
--entrypoint=/bin/bash \
zhook-action-dbg -euxo pipefail -c '
ulimit -c unlimited;
exec valgrind \
--verbose \
--log-file=${GITHUB_WORKSPACE}/docker-valgrind-%p-%n.log \
--leak-check=yes \
python /app/zhook.py;
'

- uses: ./
name: Run as a GH Action from the Local Checkout
if: |
always()
&& (github.repository_owner == 'openziti' || github.repository_owner == 'netfoundry')
&& ((github.event_name != 'pull_request_review')
|| (github.event_name == 'pull_request_review' && github.event.review.state == 'approved'))
with:
zitiId: ${{ secrets.ZITI_MATTERMOST_IDENTITY }}
webhookUrl: ${{ secrets.ZHOOK_URL }}
webhookUrl: ${{ secrets.ZHOOK_URL_DEV_NOTIFICATIONS }}
eventJson: ${{ toJson(github.event) }}
senderUsername: "GitHubZ"
destChannel: "dev-notifications"
senderUsername: GitHubZ
senderIconUrl: https://github.com/fluidicon.png
zitiLogLevel: 6

- name: Print Debug Info
if: always()
shell: bash
run: |
set -o xtrace
set +o errexit
echo "DEBUG: PYTHONPATH=${PYTHONPATH:-}"
echo "DEBUG: PATH=${PATH:-}"
echo "DEBUG: LD_LIBRARY_PATH=${LD_LIBRARY_PATH:-}"
# list non-git files in the two uppermost levels of the workspace directory hierarchy
find . -maxdepth 2 -path './.git' -prune -o -print
find $(python -c "import site; print(site.USER_SITE)") -path "*/openziti*" -name "*.so*" -type f -print0 | xargs -0r ldd

# find core dumps produced by the kernel or valgrind
shopt -s nullglob
typeset -a CORES=(${GITHUB_WORKSPACE}/core.* ${GITHUB_WORKSPACE}/vgcore.*)
shopt -u nullglob
if (( ${#CORES[@]} )); then
for CORE in "${CORES[@]}"; do
if [ -s "$CORE" ]; then
echo "DEBUG: Core dump: $CORE"
EXECUTABLE=$(basename "$CORE" | cut -d. -f2)
gdb -q $(realpath $(which "$EXECUTABLE")) -c "$CORE" --ex bt --ex exit
fi
done
else
echo "DEBUG: No core dumps found"
fi

- name: Upload Valgrind Logs and Core Dumps
if: always()
uses: actions/upload-artifact@v4
with:
name: valgrind-logs-and-core-dumps-${{ github.run_id }}
path: |
${{ github.workspace }}/*-valgrind-*.log
${{ github.workspace }}/core.*
${{ github.workspace }}/vgcore.*
if-no-files-found: ignore
15 changes: 9 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
FROM python:3.11-bullseye
FROM python:3.11-bullseye AS builder

COPY requirements.txt /tmp/requirements.txt
RUN pip install --target=/app --requirement /tmp/requirements.txt

# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/python3-debian12
COPY --from=builder /app /app
COPY --chmod=0755 ./zhook.py /app/zhook.py
WORKDIR /app
COPY ./zhook.py /app/zhook.py

RUN pip install --no-cache-dir requests openziti

ENV PYTHONPATH=/app
#ENV ZITI_LOG=6
#ENV TLSUV_DEBUG=6

CMD ["python", "/app/zhook.py"]
CMD ["/app/zhook.py"]
57 changes: 54 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ jobs:

# URL to post the payload. Note that the `zitiId` must provide access to a service
# intercepting `my-mattermost-ziti-server`
webhookUrl: 'https://{my-mattermost-ziti-server}/hook/{my-mattermost-webhook-id}}'
webhookUrl: http://{my-mattermost-ziti-server}/hook/{my-mattermost-webhook-id}}

eventJson: ${{ toJson(github.event) }}
senderUsername: "GitHubZ"
destChannel: "github-notifications"
senderUsername: GitHubZ
```

### Inputs
Expand All @@ -53,6 +52,58 @@ The identity can be created by enrolling via the `ziti edge enroll path/to/jwt [

This input value is a Mattermost "Incoming Webhook" URL available over an OpenZiti Network to the identity specified by `zitiId`. This URL should be configured in Mattermost to allow posting to any valid channel with any sender username. The default username will be the `sender.login` from the GitHub Action event.

## Testing

Test `zhook.py` locally before deploying it as a GitHub Action using the built-in test mode:

### Basic Usage

```bash
# Quick test with default push event
INPUT_ZITIID="$(< /path/to/ziti-identity.json)" \
INPUT_WEBHOOKURL="http://webhook.mattermost.ziti/hooks/YOUR_ID" \
python3 zhook.py --test

# Test different event types
python3 zhook.py --test --event-type pull_request
python3 zhook.py --test --event-type issues
python3 zhook.py --test --event-type release

# Preview payload without sending (dry-run)
python3 zhook.py --test --event-type push --dry-run
```

### Available Options

**Event types:** `push`, `pull_request`, `issues`, `release`, `watch`, `fork`

**Flags:**

- `--test`: Enable test mode with generated event data
- `--event-type TYPE`: Specify which GitHub event to simulate (default: push)
- `--dry-run`: Preview the webhook payload without sending it

**Environment variables:**

- `INPUT_ZITIID`: Ziti identity JSON (required, or use `INPUT_ZITIJWT`)
- `INPUT_ZITIJWT`: Ziti enrollment JWT (alternative to `INPUT_ZITIID`)
- `INPUT_WEBHOOKURL`: Mattermost webhook URL (uses default if not set in test mode)
- `INPUT_SENDERUSERNAME`: Override sender username (optional)
- `INPUT_SENDERICONURL`: Override sender icon URL (optional)
- `GITHUB_ACTION_REPOSITORY`: Override repository name (optional)
- `ZITI_LOG`: Ziti log level 0-6 (default: 3)

### Advanced: Manual Event JSON

For testing with custom event data, provide your own `INPUT_EVENTJSON`:

```bash
INPUT_ZITIID="$(< /path/to/ziti-identity.json)" \
INPUT_WEBHOOKURL="http://webhook.mattermost.ziti/hooks/YOUR_ID" \
INPUT_EVENTJSON='{"repository": {...}, "sender": {...}}' \
GITHUB_EVENT_NAME="push" \
python3 zhook.py
```

## Updating the Container

Expand Down
Loading
Loading