loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request '[gitea] week 2024-48 cherry pick (gitea/main -> forgejo)' (#6062) from earl-warren/wcp/2024-48 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6062
Reviewed-by: Gusted <gusted@noreply.codeberg.org>

+287 -146
-1
flake.nix
··· 29 29 poetry 30 30 31 31 # backend 32 - go_1_22 33 32 gofumpt 34 33 sqlite 35 34 ];
+19
models/fixtures/action_task.yml
··· 1 + - 2 + id: 46 3 + attempt: 3 4 + runner_id: 1 5 + status: 3 # 3 is the status code for "cancelled" 6 + started: 1683636528 7 + stopped: 1683636626 8 + repo_id: 4 9 + owner_id: 1 10 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 11 + is_fork_pull_request: 0 12 + token_hash: 6d8ef48297195edcc8e22c70b3020eaa06c52976db67d39b4260c64a69a2cc1508825121b7b8394e48e00b1bf8718b2aaaaa 13 + token_salt: eeeeeeee 14 + token_last_eight: eeeeeeee 15 + log_filename: artifact-test2/2f/47.log 16 + log_in_storage: 1 17 + log_length: 707 18 + log_size: 90179 19 + log_expired: 0 1 20 - 2 21 id: 47 3 22 job_id: 192
+1 -1
models/fixtures/system_setting.yml
··· 1 1 - 2 2 id: 1 3 3 setting_key: 'picture.disable_gravatar' 4 - setting_value: 'false' 4 + setting_value: 'true' 5 5 version: 1 6 6 created: 1653533198 7 7 updated: 1653533198
+82 -80
models/fixtures/user.yml
··· 23 23 allow_import_local: false 24 24 allow_create_organization: true 25 25 prohibit_login: false 26 - avatar: avatar1 26 + avatar: "" 27 27 avatar_email: user1@example.com 28 - use_custom_avatar: false 28 + use_custom_avatar: true 29 29 num_followers: 0 30 30 num_following: 0 31 31 num_stars: 0 ··· 60 60 allow_import_local: false 61 61 allow_create_organization: true 62 62 prohibit_login: false 63 - avatar: avatar2 63 + avatar: "" 64 64 avatar_email: user2@example.com 65 + # cause a random avatar to be generated when referenced for test purposes 65 66 use_custom_avatar: false 66 67 num_followers: 2 67 68 num_following: 1 ··· 97 98 allow_import_local: false 98 99 allow_create_organization: true 99 100 prohibit_login: false 100 - avatar: avatar3 101 + avatar: "" 101 102 avatar_email: org3@example.com 102 - use_custom_avatar: false 103 + use_custom_avatar: true 103 104 num_followers: 0 104 105 num_following: 0 105 106 num_stars: 0 ··· 134 135 allow_import_local: false 135 136 allow_create_organization: true 136 137 prohibit_login: false 137 - avatar: avatar4 138 + avatar: "" 138 139 avatar_email: user4@example.com 139 - use_custom_avatar: false 140 + use_custom_avatar: true 140 141 num_followers: 0 141 142 num_following: 1 142 143 num_stars: 0 ··· 171 172 allow_import_local: false 172 173 allow_create_organization: false 173 174 prohibit_login: false 174 - avatar: avatar5 175 + avatar: "" 175 176 avatar_email: user5@example.com 176 - use_custom_avatar: false 177 + use_custom_avatar: true 177 178 num_followers: 0 178 179 num_following: 0 179 180 num_stars: 0 ··· 208 209 allow_import_local: false 209 210 allow_create_organization: true 210 211 prohibit_login: false 211 - avatar: avatar6 212 + avatar: "" 212 213 avatar_email: org6@example.com 213 - use_custom_avatar: false 214 + use_custom_avatar: true 214 215 num_followers: 0 215 216 num_following: 0 216 217 num_stars: 0 ··· 245 246 allow_import_local: false 246 247 allow_create_organization: true 247 248 prohibit_login: false 248 - avatar: avatar7 249 + avatar: "" 249 250 avatar_email: org7@example.com 250 - use_custom_avatar: false 251 + use_custom_avatar: true 251 252 num_followers: 0 252 253 num_following: 0 253 254 num_stars: 0 ··· 282 283 allow_import_local: false 283 284 allow_create_organization: true 284 285 prohibit_login: false 285 - avatar: avatar8 286 + avatar: "" 286 287 avatar_email: user8@example.com 287 - use_custom_avatar: false 288 + use_custom_avatar: true 288 289 num_followers: 1 289 290 num_following: 1 290 291 num_stars: 0 ··· 319 320 allow_import_local: false 320 321 allow_create_organization: true 321 322 prohibit_login: false 322 - avatar: avatar9 323 + avatar: "" 323 324 avatar_email: user9@example.com 324 - use_custom_avatar: false 325 + use_custom_avatar: true 325 326 num_followers: 0 326 327 num_following: 0 327 328 num_stars: 0 ··· 332 333 repo_admin_change_team_access: false 333 334 theme: "" 334 335 keep_activity_private: false 336 + created_unix: 1730468968 335 337 336 338 - 337 339 id: 10 ··· 356 358 allow_import_local: false 357 359 allow_create_organization: true 358 360 prohibit_login: false 359 - avatar: avatar10 361 + avatar: "" 360 362 avatar_email: user10@example.com 361 - use_custom_avatar: false 363 + use_custom_avatar: true 362 364 num_followers: 0 363 365 num_following: 0 364 366 num_stars: 0 ··· 393 395 allow_import_local: false 394 396 allow_create_organization: true 395 397 prohibit_login: false 396 - avatar: avatar11 398 + avatar: "" 397 399 avatar_email: user11@example.com 398 - use_custom_avatar: false 400 + use_custom_avatar: true 399 401 num_followers: 0 400 402 num_following: 0 401 403 num_stars: 0 ··· 430 432 allow_import_local: false 431 433 allow_create_organization: true 432 434 prohibit_login: false 433 - avatar: avatar12 435 + avatar: "" 434 436 avatar_email: user12@example.com 435 - use_custom_avatar: false 437 + use_custom_avatar: true 436 438 num_followers: 0 437 439 num_following: 0 438 440 num_stars: 0 ··· 467 469 allow_import_local: false 468 470 allow_create_organization: true 469 471 prohibit_login: false 470 - avatar: avatar13 472 + avatar: "" 471 473 avatar_email: user13@example.com 472 - use_custom_avatar: false 474 + use_custom_avatar: true 473 475 num_followers: 0 474 476 num_following: 0 475 477 num_stars: 0 ··· 504 506 allow_import_local: false 505 507 allow_create_organization: true 506 508 prohibit_login: false 507 - avatar: avatar14 509 + avatar: "" 508 510 avatar_email: user13@example.com 509 - use_custom_avatar: false 511 + use_custom_avatar: true 510 512 num_followers: 0 511 513 num_following: 0 512 514 num_stars: 0 ··· 541 543 allow_import_local: false 542 544 allow_create_organization: true 543 545 prohibit_login: false 544 - avatar: avatar15 546 + avatar: "" 545 547 avatar_email: user15@example.com 546 - use_custom_avatar: false 548 + use_custom_avatar: true 547 549 num_followers: 0 548 550 num_following: 0 549 551 num_stars: 0 ··· 578 580 allow_import_local: false 579 581 allow_create_organization: true 580 582 prohibit_login: false 581 - avatar: avatar16 583 + avatar: "" 582 584 avatar_email: user16@example.com 583 - use_custom_avatar: false 585 + use_custom_avatar: true 584 586 num_followers: 0 585 587 num_following: 0 586 588 num_stars: 0 ··· 615 617 allow_import_local: false 616 618 allow_create_organization: true 617 619 prohibit_login: false 618 - avatar: avatar17 620 + avatar: "" 619 621 avatar_email: org17@example.com 620 - use_custom_avatar: false 622 + use_custom_avatar: true 621 623 num_followers: 0 622 624 num_following: 0 623 625 num_stars: 0 ··· 652 654 allow_import_local: false 653 655 allow_create_organization: true 654 656 prohibit_login: false 655 - avatar: avatar18 657 + avatar: "" 656 658 avatar_email: user18@example.com 657 - use_custom_avatar: false 659 + use_custom_avatar: true 658 660 num_followers: 0 659 661 num_following: 0 660 662 num_stars: 0 ··· 689 691 allow_import_local: false 690 692 allow_create_organization: true 691 693 prohibit_login: false 692 - avatar: avatar19 694 + avatar: "" 693 695 avatar_email: org19@example.com 694 - use_custom_avatar: false 696 + use_custom_avatar: true 695 697 num_followers: 0 696 698 num_following: 0 697 699 num_stars: 0 ··· 726 728 allow_import_local: false 727 729 allow_create_organization: true 728 730 prohibit_login: false 729 - avatar: avatar20 731 + avatar: "" 730 732 avatar_email: user20@example.com 731 - use_custom_avatar: false 733 + use_custom_avatar: true 732 734 num_followers: 0 733 735 num_following: 0 734 736 num_stars: 0 ··· 763 765 allow_import_local: false 764 766 allow_create_organization: true 765 767 prohibit_login: false 766 - avatar: avatar21 768 + avatar: "" 767 769 avatar_email: user21@example.com 768 - use_custom_avatar: false 770 + use_custom_avatar: true 769 771 num_followers: 0 770 772 num_following: 0 771 773 num_stars: 0 ··· 800 802 allow_import_local: false 801 803 allow_create_organization: true 802 804 prohibit_login: false 803 - avatar: avatar22 805 + avatar: "" 804 806 avatar_email: limited_org@example.com 805 - use_custom_avatar: false 807 + use_custom_avatar: true 806 808 num_followers: 0 807 809 num_following: 0 808 810 num_stars: 0 ··· 837 839 allow_import_local: false 838 840 allow_create_organization: true 839 841 prohibit_login: false 840 - avatar: avatar23 842 + avatar: "" 841 843 avatar_email: privated_org@example.com 842 - use_custom_avatar: false 844 + use_custom_avatar: true 843 845 num_followers: 0 844 846 num_following: 0 845 847 num_stars: 0 ··· 874 876 allow_import_local: false 875 877 allow_create_organization: true 876 878 prohibit_login: false 877 - avatar: avatar24 879 + avatar: "" 878 880 avatar_email: user24@example.com 879 - use_custom_avatar: false 881 + use_custom_avatar: true 880 882 num_followers: 0 881 883 num_following: 0 882 884 num_stars: 0 ··· 911 913 allow_import_local: false 912 914 allow_create_organization: true 913 915 prohibit_login: false 914 - avatar: avatar25 916 + avatar: "" 915 917 avatar_email: org25@example.com 916 - use_custom_avatar: false 918 + use_custom_avatar: true 917 919 num_followers: 0 918 920 num_following: 0 919 921 num_stars: 0 ··· 948 950 allow_import_local: false 949 951 allow_create_organization: true 950 952 prohibit_login: false 951 - avatar: avatar26 953 + avatar: "" 952 954 avatar_email: org26@example.com 953 - use_custom_avatar: false 955 + use_custom_avatar: true 954 956 num_followers: 0 955 957 num_following: 0 956 958 num_stars: 0 ··· 985 987 allow_import_local: false 986 988 allow_create_organization: true 987 989 prohibit_login: false 988 - avatar: avatar27 990 + avatar: "" 989 991 avatar_email: user27@example.com 990 - use_custom_avatar: false 992 + use_custom_avatar: true 991 993 num_followers: 0 992 994 num_following: 0 993 995 num_stars: 0 ··· 1022 1024 allow_import_local: false 1023 1025 allow_create_organization: true 1024 1026 prohibit_login: false 1025 - avatar: avatar28 1027 + avatar: "" 1026 1028 avatar_email: user28@example.com 1027 - use_custom_avatar: false 1029 + use_custom_avatar: true 1028 1030 num_followers: 0 1029 1031 num_following: 0 1030 1032 num_stars: 0 ··· 1059 1061 allow_import_local: false 1060 1062 allow_create_organization: true 1061 1063 prohibit_login: false 1062 - avatar: avatar29 1064 + avatar: "" 1063 1065 avatar_email: user29@example.com 1064 - use_custom_avatar: false 1066 + use_custom_avatar: true 1065 1067 num_followers: 0 1066 1068 num_following: 0 1067 1069 num_stars: 0 ··· 1096 1098 allow_import_local: false 1097 1099 allow_create_organization: true 1098 1100 prohibit_login: false 1099 - avatar: avatar29 1101 + avatar: "" 1100 1102 avatar_email: user30@example.com 1101 - use_custom_avatar: false 1103 + use_custom_avatar: true 1102 1104 num_followers: 0 1103 1105 num_following: 0 1104 1106 num_stars: 0 ··· 1133 1135 allow_import_local: false 1134 1136 allow_create_organization: true 1135 1137 prohibit_login: false 1136 - avatar: avatar31 1138 + avatar: "" 1137 1139 avatar_email: user31@example.com 1138 - use_custom_avatar: false 1140 + use_custom_avatar: true 1139 1141 num_followers: 0 1140 1142 num_following: 1 1141 1143 num_stars: 0 ··· 1170 1172 allow_import_local: false 1171 1173 allow_create_organization: true 1172 1174 prohibit_login: false 1173 - avatar: avatar32 1175 + avatar: "" 1174 1176 avatar_email: user30@example.com 1175 - use_custom_avatar: false 1177 + use_custom_avatar: true 1176 1178 num_followers: 0 1177 1179 num_following: 0 1178 1180 num_stars: 0 ··· 1207 1209 allow_import_local: false 1208 1210 allow_create_organization: true 1209 1211 prohibit_login: false 1210 - avatar: avatar33 1212 + avatar: "" 1211 1213 avatar_email: user33@example.com 1212 - use_custom_avatar: false 1214 + use_custom_avatar: true 1213 1215 num_followers: 1 1214 1216 num_following: 0 1215 1217 num_stars: 0 ··· 1245 1247 allow_import_local: false 1246 1248 allow_create_organization: false 1247 1249 prohibit_login: false 1248 - avatar: avatar34 1250 + avatar: "" 1249 1251 avatar_email: user34@example.com 1250 1252 use_custom_avatar: true 1251 1253 num_followers: 0 ··· 1282 1284 allow_import_local: false 1283 1285 allow_create_organization: true 1284 1286 prohibit_login: false 1285 - avatar: avatar35 1287 + avatar: "" 1286 1288 avatar_email: private_org35@example.com 1287 - use_custom_avatar: false 1289 + use_custom_avatar: true 1288 1290 num_followers: 0 1289 1291 num_following: 0 1290 1292 num_stars: 0 ··· 1319 1321 allow_import_local: false 1320 1322 allow_create_organization: true 1321 1323 prohibit_login: false 1322 - avatar: avatar22 1324 + avatar: "" 1323 1325 avatar_email: abcde@gitea.com 1324 - use_custom_avatar: false 1326 + use_custom_avatar: true 1325 1327 num_followers: 0 1326 1328 num_following: 0 1327 1329 num_stars: 0 ··· 1356 1358 allow_import_local: false 1357 1359 allow_create_organization: true 1358 1360 prohibit_login: true 1359 - avatar: avatar29 1361 + avatar: "" 1360 1362 avatar_email: user37@example.com 1361 - use_custom_avatar: false 1363 + use_custom_avatar: true 1362 1364 num_followers: 0 1363 1365 num_following: 0 1364 1366 num_stars: 0 ··· 1393 1395 allow_import_local: false 1394 1396 allow_create_organization: true 1395 1397 prohibit_login: false 1396 - avatar: avatar38 1398 + avatar: "" 1397 1399 avatar_email: user38@example.com 1398 - use_custom_avatar: false 1400 + use_custom_avatar: true 1399 1401 num_followers: 0 1400 1402 num_following: 0 1401 1403 num_stars: 0 ··· 1430 1432 allow_import_local: false 1431 1433 allow_create_organization: true 1432 1434 prohibit_login: false 1433 - avatar: avatar39 1435 + avatar: "" 1434 1436 avatar_email: user39@example.com 1435 - use_custom_avatar: false 1437 + use_custom_avatar: true 1436 1438 num_followers: 0 1437 1439 num_following: 0 1438 1440 num_stars: 0 ··· 1467 1469 allow_import_local: false 1468 1470 allow_create_organization: true 1469 1471 prohibit_login: false 1470 - avatar: avatar40 1472 + avatar: "" 1471 1473 avatar_email: user40@example.com 1472 - use_custom_avatar: false 1474 + use_custom_avatar: true 1473 1475 num_followers: 0 1474 1476 num_following: 0 1475 1477 num_stars: 0 ··· 1504 1506 allow_import_local: false 1505 1507 allow_create_organization: true 1506 1508 prohibit_login: false 1507 - avatar: avatar41 1509 + avatar: "" 1508 1510 avatar_email: org41@example.com 1509 - use_custom_avatar: false 1511 + use_custom_avatar: true 1510 1512 num_followers: 0 1511 1513 num_following: 0 1512 1514 num_stars: 0
+12 -6
models/user/user.go
··· 50 50 UserTypeIndividual UserType = iota // Historic reason to make it starts at 0. 51 51 52 52 // UserTypeOrganization defines an organization 53 - UserTypeOrganization 53 + UserTypeOrganization // 1 54 54 55 55 // UserTypeUserReserved reserves a (non-existing) user, i.e. to prevent a spam user from re-registering after being deleted, or to reserve the name until the user is actually created later on 56 - UserTypeUserReserved 56 + UserTypeUserReserved // 2 57 57 58 58 // UserTypeOrganizationReserved reserves a (non-existing) organization, to be used in combination with UserTypeUserReserved 59 - UserTypeOrganizationReserved 59 + UserTypeOrganizationReserved // 3 60 60 61 61 // UserTypeBot defines a bot user 62 - UserTypeBot 62 + UserTypeBot // 4 63 63 64 64 // UserTypeRemoteUser defines a remote user for federated users 65 - UserTypeRemoteUser 65 + UserTypeRemoteUser // 5 66 66 ) 67 67 68 68 const ( ··· 919 919 920 920 // GetInactiveUsers gets all inactive users 921 921 func GetInactiveUsers(ctx context.Context, olderThan time.Duration) ([]*User, error) { 922 - var cond builder.Cond = builder.Eq{"is_active": false} 922 + cond := builder.And( 923 + builder.Eq{"is_active": false}, 924 + builder.Or( // only plain user 925 + builder.Eq{"`type`": UserTypeIndividual}, 926 + builder.Eq{"`type`": UserTypeUserReserved}, 927 + ), 928 + ) 923 929 924 930 if olderThan > 0 { 925 931 cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-olderThan).Unix()})
+14
models/user/user_test.go
··· 766 766 assert.Nil(t, authToken) 767 767 }) 768 768 } 769 + 770 + func TestGetInactiveUsers(t *testing.T) { 771 + require.NoError(t, unittest.PrepareTestDatabase()) 772 + 773 + // all inactive users 774 + // user1's createdunix is 1730468968 775 + users, err := user_model.GetInactiveUsers(db.DefaultContext, 0) 776 + require.NoError(t, err) 777 + assert.Len(t, users, 1) 778 + interval := time.Now().Unix() - 1730468968 + 3600*24 779 + users, err = user_model.GetInactiveUsers(db.DefaultContext, time.Duration(interval*int64(time.Second))) 780 + require.NoError(t, err) 781 + require.Empty(t, users) 782 + }
+21 -24
modules/git/commit.go
··· 17 17 18 18 "code.gitea.io/gitea/modules/log" 19 19 "code.gitea.io/gitea/modules/util" 20 + 21 + "github.com/go-git/go-git/v5/config" 20 22 ) 21 23 22 24 // Commit represents a git commit. ··· 365 367 return nil, err 366 368 } 367 369 368 - rd, err := entry.Blob().DataAsync() 370 + content, err := entry.Blob().GetBlobContent(10 * 1024) 369 371 if err != nil { 370 372 return nil, err 371 373 } 372 374 373 - defer rd.Close() 374 - scanner := bufio.NewScanner(rd) 375 - c.submoduleCache = newObjectCache() 376 - var ismodule bool 377 - var path string 378 - for scanner.Scan() { 379 - if strings.HasPrefix(scanner.Text(), "[submodule") { 380 - ismodule = true 381 - continue 382 - } 383 - if ismodule { 384 - fields := strings.Split(scanner.Text(), "=") 385 - k := strings.TrimSpace(fields[0]) 386 - if k == "path" { 387 - path = strings.TrimSpace(fields[1]) 388 - } else if k == "url" { 389 - c.submoduleCache.Set(path, &SubModule{path, strings.TrimSpace(fields[1])}) 390 - ismodule = false 391 - } 392 - } 375 + c.submoduleCache, err = parseSubmoduleContent([]byte(content)) 376 + if err != nil { 377 + return nil, err 378 + } 379 + return c.submoduleCache, nil 380 + } 381 + 382 + func parseSubmoduleContent(bs []byte) (*ObjectCache, error) { 383 + cfg := config.NewModules() 384 + if err := cfg.Unmarshal(bs); err != nil { 385 + return nil, err 386 + } 387 + submoduleCache := newObjectCache() 388 + if len(cfg.Submodules) == 0 { 389 + return nil, fmt.Errorf("no submodules found") 393 390 } 394 - if err = scanner.Err(); err != nil { 395 - return nil, fmt.Errorf("GetSubModules scan: %w", err) 391 + for _, subModule := range cfg.Submodules { 392 + submoduleCache.Set(subModule.Path, subModule.URL) 396 393 } 397 394 398 - return c.submoduleCache, nil 395 + return submoduleCache, nil 399 396 } 400 397 401 398 // GetSubModule get the sub module according entryname
+30
modules/git/commit_test.go
··· 369 369 assert.Equal(t, testcase.renames, renames) 370 370 } 371 371 } 372 + 373 + func Test_parseSubmoduleContent(t *testing.T) { 374 + submoduleFiles := []struct { 375 + fileContent string 376 + expectedPath string 377 + expectedURL string 378 + }{ 379 + { 380 + fileContent: `[submodule "jakarta-servlet"] 381 + url = ../../ALP-pool/jakarta-servlet 382 + path = jakarta-servlet`, 383 + expectedPath: "jakarta-servlet", 384 + expectedURL: "../../ALP-pool/jakarta-servlet", 385 + }, 386 + { 387 + fileContent: `[submodule "jakarta-servlet"] 388 + path = jakarta-servlet 389 + url = ../../ALP-pool/jakarta-servlet`, 390 + expectedPath: "jakarta-servlet", 391 + expectedURL: "../../ALP-pool/jakarta-servlet", 392 + }, 393 + } 394 + for _, kase := range submoduleFiles { 395 + submodule, err := parseSubmoduleContent([]byte(kase.fileContent)) 396 + require.NoError(t, err) 397 + v, ok := submodule.Get(kase.expectedPath) 398 + assert.True(t, ok) 399 + assert.Equal(t, kase.expectedURL, v) 400 + } 401 + }
+2 -7
modules/repository/commits_test.go
··· 4 4 package repository 5 5 6 6 import ( 7 - "crypto/md5" 8 - "fmt" 9 7 "strconv" 10 8 "testing" 11 9 "time" ··· 126 124 }, 127 125 } 128 126 129 - setting.GravatarSource = "https://secure.gravatar.com/avatar" 130 - setting.OfflineMode = true 131 - 132 127 assert.Equal(t, 133 - "/avatars/avatar2?size="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor), 128 + "/avatars/ab53a2911ddf9b4817ac01ddcd3d975f?size="+strconv.Itoa(28*setting.Avatar.RenderedSizeFactor), 134 129 pushCommits.AvatarLink(db.DefaultContext, "user2@example.com")) 135 130 136 131 assert.Equal(t, 137 - fmt.Sprintf("https://secure.gravatar.com/avatar/%x?d=identicon&s=%d", md5.Sum([]byte("nonexistent@example.com")), 28*setting.Avatar.RenderedSizeFactor), 132 + "/assets/img/avatar_default.png", 138 133 pushCommits.AvatarLink(db.DefaultContext, "nonexistent@example.com")) 139 134 } 140 135
+4
release-notes/6062.md
··· 1 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/32a91add34519ef7768ec907888ed837ad0dde2f) Fix GetInactiveUsers 2 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/64824290912b6300ede2b2f95ff77d55dde9859b) Fix submodule parsing 3 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/ddabba5f89c4b196daeeb2af17de9ec2cec14b63) allow the actions user to login via the jwt token 4 + feat: [commit](https://codeberg.org/forgejo/forgejo/commit/262c48409b1224e3f6dc63c8d1e04fef0e0cf2c0) Support HTTP POST requests to `/userinfo`, aligning to OpenID Core specification
-5
routers/api/v1/repo/branch.go
··· 133 133 134 134 branchName := ctx.Params("*") 135 135 136 - if ctx.Repo.Repository.IsEmpty { 137 - ctx.Error(http.StatusForbidden, "", "Git Repository is empty.") 138 - return 139 - } 140 - 141 136 // check whether branches of this repository has been synced 142 137 totalNumOfBranches, err := db.Count[git_model.Branch](ctx, git_model.FindBranchOptions{ 143 138 RepoID: ctx.Repo.Repository.ID,
+1 -1
routers/web/web.go
··· 530 530 m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) 531 531 }, ignSignInAndCsrf, reqSignIn) 532 532 533 - m.Methods("GET, OPTIONS", "/userinfo", optionsCorsHandler(), ignSignInAndCsrf, auth.InfoOAuth) 533 + m.Methods("GET, POST, OPTIONS", "/userinfo", optionsCorsHandler(), ignSignInAndCsrf, auth.InfoOAuth) 534 534 m.Methods("POST, OPTIONS", "/access_token", optionsCorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) 535 535 m.Methods("GET, OPTIONS", "/keys", optionsCorsHandler(), ignSignInAndCsrf, auth.OIDCKeys) 536 536 m.Methods("POST, OPTIONS", "/introspect", optionsCorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth)
+8 -3
services/actions/auth.go
··· 83 83 return 0, fmt.Errorf("split token failed") 84 84 } 85 85 86 - token, err := jwt.ParseWithClaims(parts[1], &actionsClaims{}, func(t *jwt.Token) (any, error) { 86 + return TokenToTaskID(parts[1]) 87 + } 88 + 89 + // TokenToTaskID returns the TaskID associated with the provided JWT token 90 + func TokenToTaskID(token string) (int64, error) { 91 + parsedToken, err := jwt.ParseWithClaims(token, &actionsClaims{}, func(t *jwt.Token) (any, error) { 87 92 if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { 88 93 return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) 89 94 } ··· 93 98 return 0, err 94 99 } 95 100 96 - c, ok := token.Claims.(*actionsClaims) 97 - if !token.Valid || !ok { 101 + c, ok := parsedToken.Claims.(*actionsClaims) 102 + if !parsedToken.Valid || !ok { 98 103 return 0, fmt.Errorf("invalid token claim") 99 104 } 100 105
+23 -1
services/auth/oauth2.go
··· 18 18 "code.gitea.io/gitea/modules/setting" 19 19 "code.gitea.io/gitea/modules/timeutil" 20 20 "code.gitea.io/gitea/modules/web/middleware" 21 + "code.gitea.io/gitea/services/actions" 21 22 "code.gitea.io/gitea/services/auth/source/oauth2" 22 23 ) 23 24 ··· 94 95 return grant.UserID, grantScopes 95 96 } 96 97 98 + // CheckTaskIsRunning verifies that the TaskID corresponds to a running task 99 + func CheckTaskIsRunning(ctx context.Context, taskID int64) bool { 100 + // Verify the task exists 101 + task, err := actions_model.GetTaskByID(ctx, taskID) 102 + if err != nil { 103 + return false 104 + } 105 + 106 + // Verify that it's running 107 + return task.Status == actions_model.StatusRunning 108 + } 109 + 97 110 // OAuth2 implements the Auth interface and authenticates requests 98 111 // (API requests only) by looking for an OAuth token in query parameters or the 99 112 // "Authorization" header. ··· 137 150 func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store DataStore) int64 { 138 151 // Let's see if token is valid. 139 152 if strings.Contains(tokenSHA, ".") { 153 + // First attempt to decode an actions JWT, returning the actions user 154 + if taskID, err := actions.TokenToTaskID(tokenSHA); err == nil { 155 + if CheckTaskIsRunning(ctx, taskID) { 156 + store.GetData()["IsActionsToken"] = true 157 + store.GetData()["ActionsTaskID"] = taskID 158 + return user_model.ActionsUserID 159 + } 160 + } 161 + 162 + // Otherwise, check if this is an OAuth access token 140 163 uid, grantScopes := CheckOAuthAccessToken(ctx, tokenSHA) 141 - 142 164 if uid != 0 { 143 165 store.GetData()["IsApiToken"] = true 144 166 if grantScopes != "" {
+55
services/auth/oauth2_test.go
··· 1 + // Copyright 2024 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package auth 5 + 6 + import ( 7 + "context" 8 + "testing" 9 + 10 + "code.gitea.io/gitea/models/unittest" 11 + user_model "code.gitea.io/gitea/models/user" 12 + "code.gitea.io/gitea/modules/web/middleware" 13 + "code.gitea.io/gitea/services/actions" 14 + 15 + "github.com/stretchr/testify/assert" 16 + "github.com/stretchr/testify/require" 17 + ) 18 + 19 + func TestUserIDFromToken(t *testing.T) { 20 + require.NoError(t, unittest.PrepareTestDatabase()) 21 + 22 + t.Run("Actions JWT", func(t *testing.T) { 23 + const RunningTaskID = 47 24 + token, err := actions.CreateAuthorizationToken(RunningTaskID, 1, 2) 25 + require.NoError(t, err) 26 + 27 + ds := make(middleware.ContextData) 28 + 29 + o := OAuth2{} 30 + uid := o.userIDFromToken(context.Background(), token, ds) 31 + assert.Equal(t, int64(user_model.ActionsUserID), uid) 32 + assert.Equal(t, true, ds["IsActionsToken"]) 33 + assert.Equal(t, ds["ActionsTaskID"], int64(RunningTaskID)) 34 + }) 35 + } 36 + 37 + func TestCheckTaskIsRunning(t *testing.T) { 38 + require.NoError(t, unittest.PrepareTestDatabase()) 39 + cases := map[string]struct { 40 + TaskID int64 41 + Expected bool 42 + }{ 43 + "Running": {TaskID: 47, Expected: true}, 44 + "Missing": {TaskID: 1, Expected: false}, 45 + "Cancelled": {TaskID: 46, Expected: false}, 46 + } 47 + 48 + for name := range cases { 49 + c := cases[name] 50 + t.Run(name, func(t *testing.T) { 51 + actual := CheckTaskIsRunning(context.Background(), c.TaskID) 52 + assert.Equal(t, c.Expected, actual) 53 + }) 54 + } 55 + }
-7
services/context/repo.go
··· 413 413 } 414 414 } 415 415 416 - pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{}) 417 - if err != nil { 418 - ctx.ServerError("GetPushMirrorsByRepoID", err) 419 - return 420 - } 421 - 422 416 ctx.Repo.Repository = repo 423 - ctx.Data["PushMirrors"] = pushMirrors 424 417 ctx.Data["RepoName"] = ctx.Repo.Repository.Name 425 418 ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty 426 419 ctx.Data["DefaultWikiBranchName"] = setting.Repository.DefaultBranch
+2 -2
services/repository/contributors_graph_test.go
··· 43 43 dataString, isData := mockCache.Get("key2").(string) 44 44 assert.True(t, isData) 45 45 // Verify that JSON is actually stored in the cache. 46 - assert.JSONEq(t, `{"ethantkoenig@gmail.com":{"name":"Ethan Koenig","login":"","avatar_link":"https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon","home_link":"","total_commits":1,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1}}},"jimmy.praet@telenet.be":{"name":"Jimmy Praet","login":"","avatar_link":"https://secure.gravatar.com/avatar/93c49b7c89eb156971d11161c9b52795?d=identicon","home_link":"","total_commits":1,"weeks":{"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}},"jon@allspice.io":{"name":"Jon","login":"","avatar_link":"https://secure.gravatar.com/avatar/00388ce725e6886f3e07c3733007289b?d=identicon","home_link":"","total_commits":1,"weeks":{"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1}}},"total":{"name":"Total","login":"","avatar_link":"","home_link":"","total_commits":3,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1},"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1},"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}}}`, dataString) 46 + assert.JSONEq(t, `{"ethantkoenig@gmail.com":{"name":"Ethan Koenig","login":"","avatar_link":"/assets/img/avatar_default.png","home_link":"","total_commits":1,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1}}},"jimmy.praet@telenet.be":{"name":"Jimmy Praet","login":"","avatar_link":"/assets/img/avatar_default.png","home_link":"","total_commits":1,"weeks":{"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}},"jon@allspice.io":{"name":"Jon","login":"","avatar_link":"/assets/img/avatar_default.png","home_link":"","total_commits":1,"weeks":{"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1}}},"total":{"name":"Total","login":"","avatar_link":"","home_link":"","total_commits":3,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1},"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1},"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}}}`, dataString) 47 47 48 48 var data map[string]*ContributorData 49 49 require.NoError(t, json.Unmarshal([]byte(dataString), &data)) ··· 62 62 63 63 assert.EqualValues(t, &ContributorData{ 64 64 Name: "Ethan Koenig", 65 - AvatarLink: "https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon", 65 + AvatarLink: "/assets/img/avatar_default.png", 66 66 TotalCommits: 1, 67 67 Weeks: map[int64]*WeekData{ 68 68 1511654400000: {
+13 -8
tests/integration/opengraph_test.go
··· 5 5 6 6 import ( 7 7 "net/http" 8 + "strings" 8 9 "testing" 9 10 10 11 "code.gitea.io/gitea/modules/setting" ··· 42 43 "og:title": "User Thirty", 43 44 "og:url": setting.AppURL + "user30", 44 45 "og:type": "profile", 45 - "og:image": "https://secure.gravatar.com/avatar/eae1f44b34ff27284cb0792c7601c89c?d=identicon", 46 + "og:image": "http://localhost:3003/assets/img/avatar_default.png", 46 47 "og:site_name": siteName, 47 48 }, 48 49 }, ··· 54 55 "og:url": setting.AppURL + "the_34-user.with.all.allowedChars", 55 56 "og:description": "some [commonmark](https://commonmark.org/)!", 56 57 "og:type": "profile", 57 - "og:image": setting.AppURL + "avatars/avatar34", 58 + "og:image": "http://localhost:3003/assets/img/avatar_default.png", 58 59 "og:site_name": siteName, 59 60 }, 60 61 }, ··· 66 67 "og:url": setting.AppURL + "user2/repo1/issues/1", 67 68 "og:description": "content for the first issue", 68 69 "og:type": "object", 69 - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", 70 + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", 70 71 "og:site_name": siteName, 71 72 }, 72 73 }, ··· 78 79 "og:url": setting.AppURL + "user2/repo1/pulls/2", 79 80 "og:description": "content for the second issue", 80 81 "og:type": "object", 81 - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", 82 + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", 82 83 "og:site_name": siteName, 83 84 }, 84 85 }, ··· 89 90 "og:title": "repo49/test/test.txt at master", 90 91 "og:url": setting.AppURL + "/user27/repo49/src/branch/master/test/test.txt", 91 92 "og:type": "object", 92 - "og:image": "https://secure.gravatar.com/avatar/7095710e927665f1bdd1ced94152f232?d=identicon", 93 + "og:image": "http://localhost:3003/assets/img/avatar_default.png", 93 94 "og:site_name": siteName, 94 95 }, 95 96 }, ··· 100 101 "og:title": "Page With Spaced Name", 101 102 "og:url": setting.AppURL + "/user2/repo1/wiki/Page-With-Spaced-Name", 102 103 "og:type": "object", 103 - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", 104 + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", 104 105 "og:site_name": siteName, 105 106 }, 106 107 }, ··· 111 112 "og:title": "repo1", 112 113 "og:url": setting.AppURL + "user2/repo1", 113 114 "og:type": "object", 114 - "og:image": "https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon", 115 + "og:image": "http://localhost:3003/avatars/ab53a2911ddf9b4817ac01ddcd3d975f", 115 116 "og:site_name": siteName, 116 117 }, 117 118 }, ··· 123 124 "og:url": setting.AppURL + "user27/repo49", 124 125 "og:description": "A wonderful repository with more than just a README.md", 125 126 "og:type": "object", 126 - "og:image": "https://secure.gravatar.com/avatar/7095710e927665f1bdd1ced94152f232?d=identicon", 127 + "og:image": "http://localhost:3003/assets/img/avatar_default.png", 127 128 "og:site_name": siteName, 128 129 }, 129 130 }, ··· 141 142 assert.True(t, foundProp) 142 143 content, foundContent := selection.Attr("content") 143 144 assert.True(t, foundContent, "opengraph meta tag without a content property") 145 + if prop == "og:image" { 146 + content = strings.ReplaceAll(content, "http://localhost:3001", "http://localhost:3003") 147 + content = strings.ReplaceAll(content, "http://localhost:3002", "http://localhost:3003") 148 + } 144 149 foundProps[prop] = content 145 150 }) 146 151