···
signingPrivateKeyId = "4D642DE8B678C79D";
25
+
actionsWorkflowYaml = ''
26
+
run-name: dummy workflow
33
+
- uses: http://localhost:3000/test/checkout@main
36
+
# https://github.com/actions/checkout/releases
37
+
checkoutActionSource = pkgs.fetchFromGitHub {
41
+
hash = "sha256-h2/UIp8IjPo3eE4Gzx52Fb7pcgG/Ww7u31w5fdKVMos=";
supportedDbTypes = [ "mysql" "postgres" "sqlite3" ];
26
-
makeGForgejoTest = type: nameValuePair type (makeTest {
45
+
makeForgejoTest = type: nameValuePair type (makeTest {
name = "forgejo-${type}";
meta.maintainers = with maintainers; [ bendlas emilylange ];
···
settings.service.DISABLE_REGISTRATION = true;
settings."repository.signing".SIGNING_KEY = signingPrivateKeyId;
settings.actions.ENABLED = true;
58
+
settings.repository = {
59
+
ENABLE_PUSH_CREATE_USER = true;
60
+
DEFAULT_PUSH_CREATE_PRIVATE = false;
40
-
environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq pkgs.file ];
63
+
environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq pkgs.file pkgs.htmlq ];
services.openssh.enable = true;
specialisation.runner = {
inheritParentConfig = true;
45
-
configuration.services.gitea-actions-runner.instances."test" = {
48
-
url = "http://localhost:3000";
50
-
# don't require docker/podman
53
-
tokenFile = "/var/lib/forgejo/runner_token";
68
+
configuration.services.gitea-actions-runner = {
69
+
package = pkgs.forgejo-runner;
70
+
instances."test" = {
73
+
url = "http://localhost:3000";
75
+
# type ":host" does not depend on docker/podman/lxc
78
+
tokenFile = "/var/lib/forgejo/runner_token";
···
65
-
client1 = { config, pkgs, ... }: {
66
-
environment.systemPackages = [ pkgs.git ];
68
-
client2 = { config, pkgs, ... }: {
69
-
environment.systemPackages = [ pkgs.git ];
95
+
user.email = "test@localhost";
97
+
init.defaultBranch = "main";
100
+
programs.ssh.extraConfig = ''
102
+
StrictHostKeyChecking no
103
+
IdentityFile ~/.ssh/privk
···
inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey;
serverSystem = nodes.server.system.build.toplevel;
dumpFile = with nodes.server.specialisation.dump.configuration.services.forgejo.dump; "${backupDir}/${file}";
113
+
remoteUri = "forgejo@server:test/repo";
114
+
remoteUriCheckoutAction = "forgejo@server:test/checkout";
81
-
GIT_SSH_COMMAND = "ssh -i $HOME/.ssh/privk -o StrictHostKeyChecking=no"
82
-
REPO = "forgejo@server:test/repo"
83
-
PRIVK = "${snakeOilPrivateKey}"
87
-
client1.succeed("mkdir /tmp/repo")
88
-
client1.succeed("mkdir -p $HOME/.ssh")
89
-
client1.succeed(f"cat {PRIVK} > $HOME/.ssh/privk")
90
-
client1.succeed("chmod 0400 $HOME/.ssh/privk")
91
-
client1.succeed("git -C /tmp/repo init")
92
-
client1.succeed("echo hello world > /tmp/repo/testfile")
93
-
client1.succeed("git -C /tmp/repo add .")
94
-
client1.succeed("git config --global user.email test@localhost")
95
-
client1.succeed("git config --global user.name test")
96
-
client1.succeed("git -C /tmp/repo commit -m 'Initial import'")
97
-
client1.succeed(f"git -C /tmp/repo remote add origin {REPO}")
121
+
client.succeed("mkdir -p ~/.ssh")
122
+
client.succeed("(umask 0077; cat ${snakeOilPrivateKey} > ~/.ssh/privk)")
124
+
client.succeed("mkdir /tmp/repo")
125
+
client.succeed("git -C /tmp/repo init")
126
+
client.succeed("echo 'hello world' > /tmp/repo/testfile")
127
+
client.succeed("git -C /tmp/repo add .")
128
+
client.succeed("git -C /tmp/repo commit -m 'Initial import'")
129
+
client.succeed("git -C /tmp/repo remote add origin ${remoteUri}")
server.wait_for_unit("forgejo.service")
server.wait_for_open_port(3000)
···
+ ' -d \'{"key":"${snakeOilPublicKey}","read_only":true,"title":"SSH"}\'''
147
-
f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' git -C /tmp/repo push origin master"
178
+
client.succeed("git -C /tmp/repo push origin main")
150
-
client2.succeed("mkdir -p $HOME/.ssh")
151
-
client2.succeed(f"cat {PRIVK} > $HOME/.ssh/privk")
152
-
client2.succeed("chmod 0400 $HOME/.ssh/privk")
153
-
client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' git clone {REPO}")
154
-
client2.succeed('test "$(cat repo/testfile | xargs echo -n)" = "hello world"')
180
+
client.succeed("git clone ${remoteUri} /tmp/repo-clone")
181
+
print(client.succeed("ls -lash /tmp/repo-clone"))
182
+
assert "hello world" == client.succeed("cat /tmp/repo-clone/testfile").strip()
with subtest("Testing git protocol version=2 over ssh"):
157
-
git_protocol = client2.succeed(f"GIT_SSH_COMMAND='{GIT_SSH_COMMAND}' GIT_TRACE2_EVENT=true git -C repo fetch |& grep negotiated-version")
185
+
git_protocol = client.succeed("GIT_TRACE2_EVENT=true git -C /tmp/repo-clone fetch |& grep negotiated-version")
version = json.loads(git_protocol).get("value")
assert version == "2", f"git did not negotiate protocol version 2, but version {version} instead."
···
167
-
with subtest("Testing runner registration"):
195
+
with subtest("Testing runner registration and action workflow"):
"su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo gitea actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/forgejo/runner_token"
···
server.wait_for_unit("gitea-runner-test.service")
server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'")
203
+
# enable actions feature for this repository, defaults to disabled
205
+
"curl --fail -X PATCH http://localhost:3000/api/v1/repos/test/repo "
206
+
+ "-H 'Accept: application/json' -H 'Content-Type: application/json' "
207
+
+ f"-H 'Authorization: token {api_token}'"
208
+
+ ' -d \'{"has_actions":true}\'''
211
+
# mirror "actions/checkout" action
212
+
client.succeed("cp -R ${checkoutActionSource}/ /tmp/checkout")
213
+
client.succeed("git -C /tmp/checkout init")
214
+
client.succeed("git -C /tmp/checkout add .")
215
+
client.succeed("git -C /tmp/checkout commit -m 'Initial import'")
216
+
client.succeed("git -C /tmp/checkout remote add origin ${remoteUriCheckoutAction}")
217
+
client.succeed("git -C /tmp/checkout push origin main")
219
+
# push workflow to initial repo
220
+
client.succeed("mkdir -p /tmp/repo/.forgejo/workflows")
221
+
client.succeed("cp ${pkgs.writeText "dummy-workflow.yml" actionsWorkflowYaml} /tmp/repo/.forgejo/workflows/")
222
+
client.succeed("git -C /tmp/repo add .")
223
+
client.succeed("git -C /tmp/repo commit -m 'Add dummy workflow'")
224
+
client.succeed("git -C /tmp/repo push origin main")
226
+
def poll_workflow_action_status(_) -> bool:
227
+
output = server.succeed(
228
+
"curl --fail http://localhost:3000/test/repo/actions | "
229
+
+ 'htmlq ".flex-item-leading span" --attribute "data-tooltip-content"'
232
+
# values taken from https://codeberg.org/forgejo/forgejo/src/commit/af47c583b4fb3190fa4c4c414500f9941cc02389/options/locale/locale_en-US.ini#L3649-L3661
233
+
if output in [ "Failure", "Canceled", "Skipped", "Blocked" ]:
234
+
raise Exception(f"Workflow status is '{output}', which we consider failed.")
235
+
server.log(f"Command returned '{output}', which we consider failed.")
237
+
elif output in [ "Unknown", "Waiting", "Running", "" ]:
238
+
server.log(f"Workflow status is '{output}'. Waiting some more...")
241
+
elif output in [ "Success" ]:
244
+
raise Exception(f"Workflow status is '{output}', which we don't know. Value mappings likely need updating.")
246
+
with server.nested("Waiting for the workflow run to be successful"):
247
+
retry(poll_workflow_action_status)
with subtest("Testing backup service"):
server.succeed("${serverSystem}/specialisation/dump/bin/switch-to-configuration test")
server.systemctl("start forgejo-dump")
···
184
-
listToAttrs (map makeGForgejoTest supportedDbTypes)
258
+
listToAttrs (map makeForgejoTest supportedDbTypes)