at 24.11-pre 11 kB view raw
1{ system ? builtins.currentSystem 2, config ? { } 3, pkgs ? import ../.. { inherit system config; } 4}: 5 6with import ../lib/testing-python.nix { inherit system pkgs; }; 7with pkgs.lib; 8 9let 10 ## gpg --faked-system-time='20230301T010000!' --quick-generate-key snakeoil ed25519 sign 11 signingPrivateKey = '' 12 -----BEGIN PGP PRIVATE KEY BLOCK----- 13 14 lFgEY/6jkBYJKwYBBAHaRw8BAQdADXiZRV8RJUyC9g0LH04wLMaJL9WTc+szbMi7 15 5fw4yP8AAQCl8EwGfzSLm/P6fCBfA3I9znFb3MEHGCCJhJ6VtKYyRw7ktAhzbmFr 16 ZW9pbIiUBBMWCgA8FiEE+wUM6VW/NLtAdSixTWQt6LZ4x50FAmP+o5ACGwMFCQPC 17 ZwAECwkIBwQVCgkIBRYCAwEAAh4FAheAAAoJEE1kLei2eMedFTgBAKQs1oGFZrCI 18 TZP42hmBTKxGAI1wg7VSdDEWTZxut/2JAQDGgo2sa4VHMfj0aqYGxrIwfP2B7JHO 19 GCqGCRf9O/hzBA== 20 =9Uy3 21 -----END PGP PRIVATE KEY BLOCK----- 22 ''; 23 signingPrivateKeyId = "4D642DE8B678C79D"; 24 25 actionsWorkflowYaml = '' 26 run-name: dummy workflow 27 on: 28 push: 29 jobs: 30 cat: 31 runs-on: native 32 steps: 33 - uses: http://localhost:3000/test/checkout@main 34 - run: cat testfile 35 ''; 36 # https://github.com/actions/checkout/releases 37 checkoutActionSource = pkgs.fetchFromGitHub { 38 owner = "actions"; 39 repo = "checkout"; 40 rev = "v4.1.1"; 41 hash = "sha256-h2/UIp8IjPo3eE4Gzx52Fb7pcgG/Ww7u31w5fdKVMos="; 42 }; 43 44 supportedDbTypes = [ "mysql" "postgres" "sqlite3" ]; 45 makeForgejoTest = type: nameValuePair type (makeTest { 46 name = "forgejo-${type}"; 47 meta.maintainers = with maintainers; [ bendlas emilylange ]; 48 49 nodes = { 50 server = { config, pkgs, ... }: { 51 virtualisation.memorySize = 2047; 52 services.forgejo = { 53 enable = true; 54 database = { inherit type; }; 55 settings.service.DISABLE_REGISTRATION = true; 56 settings."repository.signing".SIGNING_KEY = signingPrivateKeyId; 57 settings.actions.ENABLED = true; 58 settings.repository = { 59 ENABLE_PUSH_CREATE_USER = true; 60 DEFAULT_PUSH_CREATE_PRIVATE = false; 61 }; 62 }; 63 environment.systemPackages = [ config.services.forgejo.package pkgs.gnupg pkgs.jq pkgs.file pkgs.htmlq ]; 64 services.openssh.enable = true; 65 66 specialisation.runner = { 67 inheritParentConfig = true; 68 configuration.services.gitea-actions-runner = { 69 package = pkgs.forgejo-runner; 70 instances."test" = { 71 enable = true; 72 name = "ci"; 73 url = "http://localhost:3000"; 74 labels = [ 75 # type ":host" does not depend on docker/podman/lxc 76 "native:host" 77 ]; 78 tokenFile = "/var/lib/forgejo/runner_token"; 79 }; 80 }; 81 }; 82 specialisation.dump = { 83 inheritParentConfig = true; 84 configuration.services.forgejo.dump = { 85 enable = true; 86 type = "tar.zst"; 87 file = "dump.tar.zst"; 88 }; 89 }; 90 }; 91 client = { ... }: { 92 programs.git = { 93 enable = true; 94 config = { 95 user.email = "test@localhost"; 96 user.name = "test"; 97 init.defaultBranch = "main"; 98 }; 99 }; 100 programs.ssh.extraConfig = '' 101 Host * 102 StrictHostKeyChecking no 103 IdentityFile ~/.ssh/privk 104 ''; 105 }; 106 }; 107 108 testScript = { nodes, ... }: 109 let 110 inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; 111 serverSystem = nodes.server.system.build.toplevel; 112 dumpFile = with nodes.server.specialisation.dump.configuration.services.forgejo.dump; "${backupDir}/${file}"; 113 remoteUri = "forgejo@server:test/repo"; 114 remoteUriCheckoutAction = "forgejo@server:test/checkout"; 115 in 116 '' 117 import json 118 119 start_all() 120 121 client.succeed("mkdir -p ~/.ssh") 122 client.succeed("(umask 0077; cat ${snakeOilPrivateKey} > ~/.ssh/privk)") 123 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}") 130 131 server.wait_for_unit("forgejo.service") 132 server.wait_for_open_port(3000) 133 server.wait_for_open_port(22) 134 server.succeed("curl --fail http://localhost:3000/") 135 136 server.succeed( 137 "su -l forgejo -c 'gpg --homedir /var/lib/forgejo/data/home/.gnupg " 138 + "--import ${toString (pkgs.writeText "forgejo.key" signingPrivateKey)}'" 139 ) 140 141 assert "BEGIN PGP PUBLIC KEY BLOCK" in server.succeed("curl http://localhost:3000/api/v1/signing-key.gpg") 142 143 api_version = json.loads(server.succeed("curl http://localhost:3000/api/forgejo/v1/version")).get("version") 144 assert "development" != api_version and "${pkgs.forgejo.version}+gitea-" in api_version, ( 145 "/api/forgejo/v1/version should not return 'development' " 146 + f"but should contain a forgejo+gitea compatibility version string. Got '{api_version}' instead." 147 ) 148 149 server.succeed( 150 "curl --fail http://localhost:3000/user/sign_up | grep 'Registration is disabled. " 151 + "Please contact your site administrator.'" 152 ) 153 server.succeed( 154 "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo gitea admin user create " 155 + "--username test --password totallysafe --email test@localhost --must-change-password=false'" 156 ) 157 158 api_token = server.succeed( 159 "curl --fail -X POST http://test:totallysafe@localhost:3000/api/v1/users/test/tokens " 160 + "-H 'Accept: application/json' -H 'Content-Type: application/json' -d " 161 + "'{\"name\":\"token\",\"scopes\":[\"all\"]}' | jq '.sha1' | xargs echo -n" 162 ) 163 164 server.succeed( 165 "curl --fail -X POST http://localhost:3000/api/v1/user/repos " 166 + "-H 'Accept: application/json' -H 'Content-Type: application/json' " 167 + f"-H 'Authorization: token {api_token}'" 168 + ' -d \'{"auto_init":false, "description":"string", "license":"mit", "name":"repo", "private":false}\''' 169 ) 170 171 server.succeed( 172 "curl --fail -X POST http://localhost:3000/api/v1/user/keys " 173 + "-H 'Accept: application/json' -H 'Content-Type: application/json' " 174 + f"-H 'Authorization: token {api_token}'" 175 + ' -d \'{"key":"${snakeOilPublicKey}","read_only":true,"title":"SSH"}\''' 176 ) 177 178 client.succeed("git -C /tmp/repo push origin main") 179 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() 183 184 with subtest("Testing git protocol version=2 over ssh"): 185 git_protocol = client.succeed("GIT_TRACE2_EVENT=true git -C /tmp/repo-clone fetch |& grep negotiated-version") 186 version = json.loads(git_protocol).get("value") 187 assert version == "2", f"git did not negotiate protocol version 2, but version {version} instead." 188 189 server.wait_until_succeeds( 190 'test "$(curl http://localhost:3000/api/v1/repos/test/repo/commits ' 191 + '-H "Accept: application/json" | jq length)" = "1"', 192 timeout=10 193 ) 194 195 with subtest("Testing runner registration and action workflow"): 196 server.succeed( 197 "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo gitea actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/forgejo/runner_token" 198 ) 199 server.succeed("${serverSystem}/specialisation/runner/bin/switch-to-configuration test") 200 server.wait_for_unit("gitea-runner-test.service") 201 server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'") 202 203 # enable actions feature for this repository, defaults to disabled 204 server.succeed( 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}\''' 209 ) 210 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") 218 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") 225 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"' 230 ).strip() 231 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.") 236 237 elif output in [ "Unknown", "Waiting", "Running", "" ]: 238 server.log(f"Workflow status is '{output}'. Waiting some more...") 239 return False 240 241 elif output in [ "Success" ]: 242 return True 243 244 raise Exception(f"Workflow status is '{output}', which we don't know. Value mappings likely need updating.") 245 246 with server.nested("Waiting for the workflow run to be successful"): 247 retry(poll_workflow_action_status) 248 249 with subtest("Testing backup service"): 250 server.succeed("${serverSystem}/specialisation/dump/bin/switch-to-configuration test") 251 server.systemctl("start forgejo-dump") 252 assert "Zstandard compressed data" in server.succeed("file ${dumpFile}") 253 server.copy_from_vm("${dumpFile}") 254 ''; 255 }); 256in 257 258listToAttrs (map makeForgejoTest supportedDbTypes)