OS world vs Container world
rafsaf opened this issue · 3 comments
Hi, this is a nice image, i would love to use it instead of hard coding webhook directly on server's OS, BUT there is a problem you didn't mentioned in README and I suppose it makes the image at least not useful in many cases and at worst not useful at all for some situations.
This was discussed in more detail here for example:
https://stackoverflow.com/questions/32163955/how-to-run-shell-script-on-host-from-docker-container
Short: If you want to execute shell scripts from the inside of a container, you need to use hacks or even worse hacks (this is not designed this way, but the opposite), and after doing so, encounter problems like container restart, server restart which may (or may not) mess up things, in sense of messing those hacks. I didn't find yet any clean and STABLE solution to this problem (disclaimer: didn't spend much time trying), you probably don't want to run your scripts inside of the container with Webhook (which actually happens by reasonable default after using this image), but directly in OS, and this is different matter.
To sum up (it's late nigh here in PL :D, hope above text is understandable), if you are aware of any clean and stable solution, please share, cheers ;)
That's out of the scope of this repo, which exists solely to publish a Docker image for webhook.
Just to share what I've done;
- I expect the processes inside the container to run as
root
so things like permissions need to be accounted for. - I expect the image to include
sh
(shell) so I can execute commands/scripts (it does not includebash
)
With those expectations, my mentality is for my execute-command
to be a shell script that checks if any of the commands I plan to use exist, and if not it installs them. Check and then install is useful because it will check and install when the first webhook request is received after creating the container, but not reinstall every time a webhook is received.
My docker-compose.yml
services:
webhook:
image: ghcr.io/thecatlady/webhook:latest
container_name: webhook
command: -verbose -hooks=hooks.json -hotreload
volumes:
- /etc/localtime:/etc/localtime:ro
- /path/to/config:/config:ro
- /path/to/parent/git_folder:/opt/git
- /path/to/.ssh:/root/.ssh:ro
ports:
- 9000:9000
restart: unless-stopped
In the above:
/path/to/config
is hopefully self explanatory/path/to/parent/git_folder
is one level above where I put all my git repos (ex:/home/myuser/git/
which contains multiple repos), you can mount your git repos however works for you (it's not really relevant to answering this issue)/path/to/.ssh
for me is/home/myuser/.ssh
which contains myid_ed25519
(deploy key for github) (again, not really relevant to answering this issue)
My hooks.json
(placed at /path/to/config/hooks.json
)
[
{
"id": "my-hook-name",
"execute-command": "/config/run/git-checkout-force.sh",
"command-working-directory": "/opt/git/myrepo",
"include-command-output-in-response": true,
"include-command-output-in-response-on-error": true,
"pass-arguments-to-command": [
{ "source": "payload", "name": "head_commit.id", "comment": "GIT_REF" },
{
"source": "string",
"name": "/opt/git/myrepo",
"comment": "GIT_DIR"
},
{ "source": "string", "name": "1000", "comment": "PUID" },
{ "source": "string", "name": "1000", "comment": "PGID" }
],
"trigger-rule": {
"and": [
{
"match": {
"type": "payload-hmac-sha1",
"secret": "<YOUR_GITHUB_WEBHOOK_SECRET>",
"parameter": { "source": "header", "name": "X-Hub-Signature" }
}
},
{
"match": {
"type": "value",
"value": "refs/heads/main",
"parameter": { "source": "payload", "name": "ref" }
}
}
]
}
}
]
I'm not going to go too much into the details of this, it's all covered in https://github.com/adnanh/webhook/tree/master/docs
I will note that I'm expecting the branch to be main
, you might need it to be master
or something else.
Also, command-working-directory
is pointed at where the container will see my repo folder (mounted inside the container).
My git-checkout-force.sh
(placed at /path/to/config/run/git-checkout-force.sh
)
#!/usr/bin/env sh
# variables
GIT_REF=${1}
GIT_DIR=${2}
PUID=${3}
PGID=${4}
# log date
date
# install git
if ! command -v git > /dev/null 2>&1; then
apk add --no-cache \
git
fi
# install openssh
if ! command -v ssh > /dev/null 2>&1; then
apk add --no-cache \
openssh
fi
# allow git with different ownership
git config --global --add safe.directory ${GIT_DIR}
# fetch from git
git fetch --all
# checkout git reference
git checkout --force ${GIT_REF}
# set ownership
chown -R ${PUID}:${PGID} ${GIT_DIR}
In the above:
GIT_REF
is the first argument, passed in whenwebhook
runs the script. It should be the commit id (seehooks.json above
)PUID
andPGID
are the second/third arguments, passed in, later used tochown
. Why not hard code? So I can setup multiple hooks with the end result allowing different file ownership in each working directory.- Log the date, just for the sake of it
- Check if the
git
command exists. If not, install it usingapk
(the alpine package manager included in the base OS of the image) - Check if the
ssh
command exists. If not, install it git config --global --add safe.directory ${GIT_DIR}
newer versions ofgit
started caring about who owns the files in the repository, so tellgit
this directory is safe to run the rest of the commands (sinceroot
is the user runningwebhook
in the container). This can result inroot
being the owner of newly added or changed files, which we will handle belowgit fetch --all
self explanatorygit checkout --force ${GIT_REF}
force checkout the referenced commit (passed in argument)chown -R ${PUID}:${PGID} ${GIT_DIR}
set the ownership of all the files in the repo using thePUID
andPGID
that are passed to the script fromhooks.json
The important takeaways:
webhook
runs in the container as root- all commands
webhook
executes via yourhooks.json
execute as root - you (may) need to
chown
at the end of your script so thatroot
is not the owner of your files - the image does not include much tooling (ex:
git
orssh
) but you can install the tools you need in your script - when installing tooling, check if it exists before installing, so that you're not reinstalling every time you receive a webhook request
The image itself does not need to change. It just requires some clever configuration. It may be reasonable to document some of this information in the README.