at master 12 kB view raw
1{ 2 runTest, 3 forgejoPackage, 4}: 5 6let 7 ## gpg --faked-system-time='20230301T010000!' --quick-generate-key snakeoil ed25519 sign 8 signingPrivateKey = '' 9 -----BEGIN PGP PRIVATE KEY BLOCK----- 10 11 lFgEY/6jkBYJKwYBBAHaRw8BAQdADXiZRV8RJUyC9g0LH04wLMaJL9WTc+szbMi7 12 5fw4yP8AAQCl8EwGfzSLm/P6fCBfA3I9znFb3MEHGCCJhJ6VtKYyRw7ktAhzbmFr 13 ZW9pbIiUBBMWCgA8FiEE+wUM6VW/NLtAdSixTWQt6LZ4x50FAmP+o5ACGwMFCQPC 14 ZwAECwkIBwQVCgkIBRYCAwEAAh4FAheAAAoJEE1kLei2eMedFTgBAKQs1oGFZrCI 15 TZP42hmBTKxGAI1wg7VSdDEWTZxut/2JAQDGgo2sa4VHMfj0aqYGxrIwfP2B7JHO 16 GCqGCRf9O/hzBA== 17 =9Uy3 18 -----END PGP PRIVATE KEY BLOCK----- 19 ''; 20 signingPrivateKeyId = "4D642DE8B678C79D"; 21 22 metricSecret = "fakesecret"; 23 24 base = 25 { 26 lib, 27 pkgs, 28 type, 29 ... 30 }: 31 32 { 33 name = "forgejo-${type}"; 34 meta.maintainers = with lib.maintainers; [ 35 bendlas 36 emilylange 37 tebriel 38 ]; 39 40 nodes = { 41 server = 42 { config, pkgs, ... }: 43 { 44 virtualisation.memorySize = 2047; 45 services.forgejo = { 46 enable = true; 47 package = forgejoPackage; 48 database = { inherit type; }; 49 settings.service.DISABLE_REGISTRATION = true; 50 settings."repository.signing".SIGNING_KEY = signingPrivateKeyId; 51 settings.actions.ENABLED = true; 52 settings.repository = { 53 ENABLE_PUSH_CREATE_USER = true; 54 DEFAULT_PUSH_CREATE_PRIVATE = false; 55 }; 56 settings.metrics.ENABLED = true; 57 secrets.metrics.TOKEN = pkgs.writeText "metrics_secret" metricSecret; 58 }; 59 environment.systemPackages = [ 60 config.services.forgejo.package 61 pkgs.gnupg 62 pkgs.jq 63 pkgs.file 64 ]; 65 services.openssh.enable = true; 66 67 specialisation.runner = { 68 inheritParentConfig = true; 69 configuration.services.gitea-actions-runner = { 70 package = pkgs.forgejo-runner; 71 instances."test" = { 72 enable = true; 73 name = "ci"; 74 url = "http://localhost:3000"; 75 labels = [ 76 # type ":host" does not depend on docker/podman/lxc 77 "native:host" 78 ]; 79 tokenFile = "/var/lib/forgejo/runner_token"; 80 }; 81 }; 82 }; 83 specialisation.dump = { 84 inheritParentConfig = true; 85 configuration.services.forgejo.dump = { 86 enable = true; 87 type = "tar.zst"; 88 file = "dump.tar.zst"; 89 }; 90 }; 91 }; 92 client = 93 { ... }: 94 { 95 programs.git = { 96 enable = true; 97 config = { 98 user.email = "test@localhost"; 99 user.name = "test"; 100 init.defaultBranch = "main"; 101 }; 102 }; 103 programs.ssh.extraConfig = '' 104 Host * 105 StrictHostKeyChecking no 106 IdentityFile ~/.ssh/privk 107 ''; 108 }; 109 }; 110 111 testScript = 112 { nodes, ... }: 113 let 114 inherit (import ./ssh-keys.nix pkgs) snakeOilPrivateKey snakeOilPublicKey; 115 serverSystem = nodes.server.system.build.toplevel; 116 dumpFile = 117 with nodes.server.specialisation.dump.configuration.services.forgejo.dump; 118 "${backupDir}/${file}"; 119 remoteUri = "forgejo@server:test/repo"; 120 remoteUriCheckoutAction = "forgejo@server:test/checkout"; 121 122 actionsWorkflowYaml = '' 123 run-name: dummy workflow 124 on: 125 push: 126 jobs: 127 cat: 128 runs-on: native 129 steps: 130 - uses: http://localhost:3000/test/checkout@main 131 - run: cat testfile 132 ''; 133 # https://github.com/actions/checkout/releases 134 checkoutActionSource = pkgs.fetchFromGitHub { 135 owner = "actions"; 136 repo = "checkout"; 137 rev = "v4.1.1"; 138 hash = "sha256-h2/UIp8IjPo3eE4Gzx52Fb7pcgG/Ww7u31w5fdKVMos="; 139 }; 140 in 141 '' 142 import json 143 144 start_all() 145 146 client.succeed("mkdir -p ~/.ssh") 147 client.succeed("(umask 0077; cat ${snakeOilPrivateKey} > ~/.ssh/privk)") 148 149 client.succeed("mkdir /tmp/repo") 150 client.succeed("git -C /tmp/repo init") 151 client.succeed("echo 'hello world' > /tmp/repo/testfile") 152 client.succeed("git -C /tmp/repo add .") 153 client.succeed("git -C /tmp/repo commit -m 'Initial import'") 154 client.succeed("git -C /tmp/repo remote add origin ${remoteUri}") 155 156 server.wait_for_unit("forgejo.service") 157 server.wait_for_open_port(3000) 158 server.wait_for_open_port(22) 159 server.succeed("curl --fail http://localhost:3000/") 160 161 server.succeed( 162 "su -l forgejo -c 'gpg --homedir /var/lib/forgejo/data/home/.gnupg " 163 + "--import ${toString (pkgs.writeText "forgejo.key" signingPrivateKey)}'" 164 ) 165 166 assert "BEGIN PGP PUBLIC KEY BLOCK" in server.succeed("curl http://localhost:3000/api/v1/signing-key.gpg") 167 168 api_version = json.loads(server.succeed("curl http://localhost:3000/api/forgejo/v1/version")).get("version") 169 assert "development" != api_version and "${forgejoPackage.version}+gitea-" in api_version, ( 170 "/api/forgejo/v1/version should not return 'development' " 171 + f"but should contain a forgejo+gitea compatibility version string. Got '{api_version}' instead." 172 ) 173 174 server.succeed( 175 "curl --fail http://localhost:3000/user/sign_up | grep 'Registration is disabled. " 176 + "Please contact your site administrator.'" 177 ) 178 server.succeed( 179 "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo forgejo admin user create " 180 + "--username test --password totallysafe --email test@localhost --must-change-password=false'" 181 ) 182 183 api_token = server.succeed( 184 "curl --fail -X POST http://test:totallysafe@localhost:3000/api/v1/users/test/tokens " 185 + "-H 'Accept: application/json' -H 'Content-Type: application/json' -d " 186 + "'{\"name\":\"token\",\"scopes\":[\"all\"]}' | jq '.sha1' | xargs echo -n" 187 ) 188 189 server.succeed( 190 "curl --fail -X POST http://localhost:3000/api/v1/user/repos " 191 + "-H 'Accept: application/json' -H 'Content-Type: application/json' " 192 + f"-H 'Authorization: token {api_token}'" 193 + ' -d \'{"auto_init":false, "description":"string", "license":"mit", "name":"repo", "private":false}\''' 194 ) 195 196 server.succeed( 197 "curl --fail -X POST http://localhost:3000/api/v1/user/keys " 198 + "-H 'Accept: application/json' -H 'Content-Type: application/json' " 199 + f"-H 'Authorization: token {api_token}'" 200 + ' -d \'{"key":"${snakeOilPublicKey}","read_only":true,"title":"SSH"}\''' 201 ) 202 203 client.succeed("git -C /tmp/repo push origin main") 204 205 client.succeed("git clone ${remoteUri} /tmp/repo-clone") 206 print(client.succeed("ls -lash /tmp/repo-clone")) 207 assert "hello world" == client.succeed("cat /tmp/repo-clone/testfile").strip() 208 209 with subtest("Testing git protocol version=2 over ssh"): 210 git_protocol = client.succeed("GIT_TRACE2_EVENT=true GIT_TRACE2_EVENT_NESTING=3 git -C /tmp/repo-clone fetch |& grep negotiated-version") 211 version = json.loads(git_protocol).get("value") 212 assert version == "2", f"git did not negotiate protocol version 2, but version {version} instead." 213 214 server.wait_until_succeeds( 215 'test "$(curl http://localhost:3000/api/v1/repos/test/repo/commits ' 216 + '-H "Accept: application/json" | jq length)" = "1"', 217 timeout=10 218 ) 219 220 with subtest("Testing /metrics endpoint with token from cfg.secrets"): 221 server.fail("curl --fail http://localhost:3000/metrics") 222 server.succeed('curl --fail http://localhost:3000/metrics -H "Authorization: Bearer ${metricSecret}"') 223 224 with subtest("Testing runner registration and action workflow"): 225 server.succeed( 226 "su -l forgejo -c 'GITEA_WORK_DIR=/var/lib/forgejo forgejo actions generate-runner-token' | sed 's/^/TOKEN=/' | tee /var/lib/forgejo/runner_token" 227 ) 228 server.succeed("${serverSystem}/specialisation/runner/bin/switch-to-configuration test") 229 server.wait_for_unit("gitea-runner-test.service") 230 server.succeed("journalctl -o cat -u gitea-runner-test.service | grep -q 'Runner registered successfully'") 231 232 # enable actions feature for this repository, defaults to disabled 233 server.succeed( 234 "curl --fail -X PATCH http://localhost:3000/api/v1/repos/test/repo " 235 + "-H 'Accept: application/json' -H 'Content-Type: application/json' " 236 + f"-H 'Authorization: token {api_token}'" 237 + ' -d \'{"has_actions":true}\''' 238 ) 239 240 # mirror "actions/checkout" action 241 client.succeed("cp -R ${checkoutActionSource}/ /tmp/checkout") 242 client.succeed("git -C /tmp/checkout init") 243 client.succeed("git -C /tmp/checkout add .") 244 client.succeed("git -C /tmp/checkout commit -m 'Initial import'") 245 client.succeed("git -C /tmp/checkout remote add origin ${remoteUriCheckoutAction}") 246 client.succeed("git -C /tmp/checkout push origin main") 247 248 # push workflow to initial repo 249 client.succeed("mkdir -p /tmp/repo/.forgejo/workflows") 250 client.succeed("cp ${pkgs.writeText "dummy-workflow.yml" actionsWorkflowYaml} /tmp/repo/.forgejo/workflows/") 251 client.succeed("git -C /tmp/repo add .") 252 client.succeed("git -C /tmp/repo commit -m 'Add dummy workflow'") 253 client.succeed("git -C /tmp/repo push origin main") 254 255 def poll_workflow_action_status(_) -> bool: 256 try: 257 response = server.succeed("curl --fail http://localhost:3000/api/v1/repos/test/repo/actions/tasks") 258 status = json.loads(response).get("workflow_runs")[0].get("status") 259 260 except IndexError: 261 status = "???" 262 263 server.log(f"Workflow status: {status}") 264 265 if status == "failure": 266 raise Exception("Workflow failed") 267 268 return status == "success" 269 270 with server.nested("Waiting for the workflow run to be successful"): 271 retry(poll_workflow_action_status, 60) 272 273 with subtest("Testing backup service"): 274 server.succeed("${serverSystem}/specialisation/dump/bin/switch-to-configuration test") 275 server.systemctl("start forgejo-dump") 276 assert "Zstandard compressed data" in server.succeed("file ${dumpFile}") 277 server.copy_from_vm("${dumpFile}") 278 ''; 279 }; 280in 281{ 282 mysql = runTest { 283 imports = [ base ]; 284 _module.args.type = "mysql"; 285 }; 286 sqlite3 = runTest { 287 imports = [ base ]; 288 _module.args.type = "sqlite3"; 289 }; 290 postgres = runTest { 291 imports = [ base ]; 292 _module.args.type = "postgres"; 293 }; 294}