Skip to content

Commit 5de954a

Browse files
committed
Run tests as non-root user in Alpine Linux
- Add a note to the fixture in test_util.py that its ability to create files where rmtree will fail is contingent on not running as root (since root doesn't need to own a dir to delete from it). - Create a non-root user in the container. Give it the same UID as owns the repository files that are shared with the container. Also create a group with the GID of the repository files that are shared with the container and add the user to the group, though that is less important. Actually creating the user ensures it has a home directory and may help some commands work. Passing `options: --user 1001` under `container:` will not work because, even if we didn't make the user, the `apk add` commands still need to run as root. - Run all commands as the new non-root user, except for the system administration commands that install needed apk packages and set up the new non-root user account. To continue clearly expressing each step separately and have them automatically run in the container, this uses the hacky approach of having the default shell be a "sudo" command that runs the script step with "sh" (and passes the desired shell arguments). - Preserve environment variables that may have been set by or for the GHA runner, in commands that run as the non-root user. That is, pass those through, while still removing/resetting others. If this is not done, then the variables such as `CI`, which the init-tests-after-clone.sh uses to proceed without interactive confirmation, will not be set, and that step will fail. However, I think it is also a good idea to do this, which is why I've included all the relevant variables and not just `CI`. - Now that a non-root user is using "pip", stop using a venv, at least for now. The other test jobs don't use one, since the runners are isolated, and a container on a runner is even more isolated. But it may be best to bring the venv back, maybe even on the other test jobs, or alternatively to use "python -m pip" instead of "pip", to ensure expected version of pip is used. - Don't add safe.directory inside the container, in the hope that this may not be necessary because the cloned repository files should have the same UID (and even GID) as the user using them. But I expect this may need to be put back; it seems to be needed separately from that, as actions/checkout automatically attempts it for the git command it finds and attempts to use. This is not the only approach that could work. Another approach is to make use of the container explicit in each step, rather than using the `container` key. I think that would make the relationship between the commands here and in other test workflows less apparent and make the workflow a bit less clear, but it could also simplify things. A third approach is to create an image with the needed apk packages and user account, which switches to that user, by writing a Dockerfile and building in image, producing it in a previous job and sharing the image with the job that runs the tests so that `container` can still be used. That might be ideal if it could be done with upload-artifact and download-artifact, but I think `container` only supports getting images from registries.
1 parent a45d0b0 commit 5de954a

File tree

2 files changed

+11
-16
lines changed

2 files changed

+11
-16
lines changed

‎.github/workflows/alpine-test.yml

+8-15
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,22 @@ jobs:
1111

1212
defaults:
1313
run:
14-
shell: sh -exo pipefail {0}
14+
shell: sudo -u runner sh -exo pipefail {0}
1515

1616
steps:
17-
- name: Install Alpine Linux packages
17+
- name: Prepare Alpine Linux
1818
run: |
19-
apk add git git-daemon python3 py3-pip
19+
apk add sudo git git-daemon python3 py3-pip
20+
echo 'Defaults env_keep += "CI GITHUB_* RUNNER_*"' >/etc/sudoers.d/ci_env
21+
addgroup -g 127 docker
22+
adduser -D -u 1001 runner
23+
adduser runner docker
24+
shell: sh -exo pipefail {0} # Run this as root, not the "runner" user.
2025

2126
- uses: actions/checkout@v4
2227
with:
2328
fetch-depth: 0
2429

25-
- name: Special configuration for Alpine Linux git
26-
run: |
27-
git config --global --add safe.directory "$(pwd)"
28-
2930
- name: Prepare this repo for tests
3031
run: |
3132
./init-tests-after-clone.sh
@@ -38,24 +39,17 @@ jobs:
3839
# and cause subsequent tests to fail
3940
cat test/fixtures/.gitconfig >> ~/.gitconfig
4041
41-
- name: Create Python virtual environment
42-
run: |
43-
python -m venv .venv
44-
4542
- name: Update PyPA packages
4643
run: |
4744
# Get the latest pip, wheel, and prior to Python 3.12, setuptools.
48-
. .venv/bin/activate
4945
python -m pip install -U pip $(pip freeze --all | grep -ow ^setuptools) wheel
5046
5147
- name: Install project and test dependencies
5248
run: |
53-
. .venv/bin/activate
5449
pip install ".[test]"
5550
5651
- name: Show version and platform information
5752
run: |
58-
. .venv/bin/activate
5953
uname -a
6054
command -v git python
6155
git version
@@ -64,5 +58,4 @@ jobs:
6458
6559
- name: Test with pytest
6660
run: |
67-
. .venv/bin/activate
6861
pytest --color=yes -p no:sugar --instafail -vv

‎test/test_util.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ def permission_error_tmpdir(tmp_path):
5151
# Set up PermissionError on Windows, where we can't delete read-only files.
5252
(td / "x").chmod(stat.S_IRUSR)
5353

54-
# Set up PermissionError on Unix, where we can't delete files in read-only directories.
54+
# Set up PermissionError on Unix, where non-root users can't delete files in
55+
# read-only directories. (Tests that rely on this and assert that rmtree raises
56+
# PermissionError will fail if they are run as root.)
5557
td.chmod(stat.S_IRUSR | stat.S_IXUSR)
5658

5759
yield td

0 commit comments

Comments
 (0)