tenseleyflow/shithub / 4fd1a72

Browse files

Credit affiliated imported commit authors

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
4fd1a7257580deb4cf6f63a1dce18de0daff9203
Parents
0801501
Tree
7029654

2 changed files

StatusFile+-
M internal/web/handlers/profile/overview.go 33 14
M internal/web/handlers/profile/profile_test.go 8 5
internal/web/handlers/profile/overview.gomodified
@@ -82,8 +82,9 @@ type contributionDay struct {
8282
 }
8383
 
8484
 type profileContributionRepo struct {
85
-	Repo      reposdb.Repo
86
-	OwnerSlug string
85
+	Repo                  reposdb.Repo
86
+	OwnerSlug             string
87
+	AllowIdentityFallback bool
8788
 }
8889
 
8990
 func (h *Handlers) visibleUserRepos(ctx context.Context, userID int64, viewer middleware.CurrentUser) []reposdb.Repo {
@@ -236,7 +237,7 @@ func (h *Handlers) contributionCalendar(ctx context.Context, user usersdb.User,
236237
 				continue
237238
 			}
238239
 			for _, commit := range commits {
239
-				if !commitMatchesProfileUser(commit, emails, user) {
240
+				if !commitMatchesProfileUser(commit, emails, user, source.AllowIdentityFallback) {
240241
 					continue
241242
 				}
242243
 				day := time.Date(commit.AuthorWhen.UTC().Year(), commit.AuthorWhen.UTC().Month(), commit.AuthorWhen.UTC().Day(), 0, 0, 0, 0, time.UTC)
@@ -305,7 +306,7 @@ func (h *Handlers) profileContributionRepos(ctx context.Context, user usersdb.Us
305306
 	queries := reposdb.New()
306307
 	seen := map[int64]struct{}{}
307308
 	out := make([]profileContributionRepo, 0, 64)
308
-	add := func(ownerSlug string, repo reposdb.Repo) {
309
+	add := func(ownerSlug string, repo reposdb.Repo, allowIdentityFallback bool) {
309310
 		if ownerSlug == "" {
310311
 			return
311312
 		}
@@ -316,7 +317,11 @@ func (h *Handlers) profileContributionRepos(ctx context.Context, user usersdb.Us
316317
 			return
317318
 		}
318319
 		seen[repo.ID] = struct{}{}
319
-		out = append(out, profileContributionRepo{Repo: repo, OwnerSlug: ownerSlug})
320
+		out = append(out, profileContributionRepo{
321
+			Repo:                  repo,
322
+			OwnerSlug:             ownerSlug,
323
+			AllowIdentityFallback: allowIdentityFallback,
324
+		})
320325
 	}
321326
 
322327
 	userRepos, err := queries.ListReposForOwnerUser(ctx, h.d.Pool, pgtype.Int8{Int64: user.ID, Valid: true})
@@ -324,7 +329,7 @@ func (h *Handlers) profileContributionRepos(ctx context.Context, user usersdb.Us
324329
 		h.d.Logger.WarnContext(ctx, "profile overview: contribution user repos", "user_id", user.ID, "error", err)
325330
 	} else {
326331
 		for _, repo := range userRepos {
327
-			add(user.Username, repo)
332
+			add(user.Username, repo, true)
328333
 		}
329334
 	}
330335
 
@@ -339,7 +344,7 @@ func (h *Handlers) profileContributionRepos(ctx context.Context, user usersdb.Us
339344
 				continue
340345
 			}
341346
 			for _, repo := range orgRepos {
342
-				add(org.Slug, repo)
347
+				add(org.Slug, repo, true)
343348
 			}
344349
 		}
345350
 	}
@@ -350,7 +355,7 @@ func (h *Handlers) profileContributionRepos(ctx context.Context, user usersdb.Us
350355
 		return out
351356
 	}
352357
 	for _, row := range publicRepos {
353
-		add(row.OwnerSlug, row.Repo)
358
+		add(row.OwnerSlug, row.Repo, false)
354359
 	}
355360
 	return out
356361
 }
@@ -387,19 +392,33 @@ func contributionYears(username string, currentYear, selectedYear int) []contrib
387392
 	return years
388393
 }
389394
 
390
-func commitMatchesProfileUser(commit repogit.Commit, verifiedEmails map[string]struct{}, user usersdb.User) bool {
395
+func commitMatchesProfileUser(commit repogit.Commit, verifiedEmails map[string]struct{}, user usersdb.User, allowIdentityFallback bool) bool {
391396
 	if len(verifiedEmails) > 0 {
392
-		_, ok := verifiedEmails[strings.ToLower(strings.TrimSpace(commit.AuthorEmail))]
393
-		return ok
397
+		if _, ok := verifiedEmails[strings.ToLower(strings.TrimSpace(commit.AuthorEmail))]; ok {
398
+			return true
399
+		}
394400
 	}
395
-	name := strings.ToLower(strings.TrimSpace(commit.AuthorName))
396
-	if name == "" {
401
+	if !allowIdentityFallback {
397402
 		return false
398403
 	}
404
+	name := strings.ToLower(strings.TrimSpace(commit.AuthorName))
399405
 	if name == strings.ToLower(user.Username) {
400406
 		return true
401407
 	}
402
-	return user.DisplayName != "" && name == strings.ToLower(strings.TrimSpace(user.DisplayName))
408
+	if user.DisplayName != "" && name == strings.ToLower(strings.TrimSpace(user.DisplayName)) {
409
+		return true
410
+	}
411
+	return commitEmailNamesProfileUser(commit.AuthorEmail, user.Username)
412
+}
413
+
414
+func commitEmailNamesProfileUser(email, username string) bool {
415
+	email = strings.ToLower(strings.TrimSpace(email))
416
+	username = strings.ToLower(strings.TrimSpace(username))
417
+	if email == "" || username == "" {
418
+		return false
419
+	}
420
+	return strings.HasSuffix(email, "+"+username+"@users.noreply.github.com") ||
421
+		email == username+"@users.noreply.github.com"
403422
 }
404423
 
405424
 func (h *Handlers) verifiedEmails(ctx context.Context, userID int64) map[string]struct{} {
internal/web/handlers/profile/profile_test.gomodified
@@ -364,20 +364,23 @@ func TestProfile_OverviewDataUsesVisibleReposAndOrganizations(t *testing.T) {
364364
 	}
365365
 }
366366
 
367
-func TestProfile_ContributionsCountVerifiedEmailAcrossUserAndOrgRepos(t *testing.T) {
367
+func TestProfile_ContributionsCountVerifiedAndAffiliatedImportedIdentities(t *testing.T) {
368368
 	t.Parallel()
369369
 	env := setupProfileEnvWithRepoFS(t)
370370
 	alice := env.insertUser(t, "alice", "Alice Anderson", "Hi.")
371
-	env.insertVerifiedEmail(t, alice.ID, "alice@example.com")
371
+	env.insertVerifiedEmail(t, alice.ID, "alice@outlook.com")
372372
 	env.insertUserRepo(t, alice.ID, "owned", "user repo", "public", "Go", 0, 0)
373373
 	orgID := env.insertOrg(t, "acme", "Acme", "", alice)
374
-	env.insertOrgRepo(t, orgID, "team", "org repo", "public", "Rust", 0, 0)
374
+	env.insertOrgRepo(t, orgID, "team", "org repo with imported author email", "public", "Rust", 0, 0)
375375
 	env.insertOrgRepo(t, orgID, "other", "different author", "public", "Rust", 0, 0)
376
+	bob := env.insertUser(t, "bob", "Bob", "")
377
+	env.insertUserRepo(t, bob.ID, "spoof", "public repo with same author name", "public", "Go", 0, 0)
376378
 
377379
 	now := time.Now().UTC()
378
-	env.writeInitialCommit(t, "alice", "owned", "Alice Anderson", "alice@example.com", now.AddDate(0, 0, -7))
379
-	env.writeInitialCommit(t, "acme", "team", "A. Alice", "alice@example.com", now.AddDate(0, 0, -14))
380
+	env.writeInitialCommit(t, "alice", "owned", "Alice Anderson", "alice@outlook.com", now.AddDate(0, 0, -7))
381
+	env.writeInitialCommit(t, "acme", "team", "alice", "alice@unverified.example", now.AddDate(0, 0, -14))
380382
 	env.writeInitialCommit(t, "acme", "other", "Bob", "bob@example.com", now.AddDate(0, 0, -5))
383
+	env.writeInitialCommit(t, "bob", "spoof", "alice", "alice@spoof.example", now.AddDate(0, 0, -3))
381384
 
382385
 	body := env.getAs(t, "/alice", usersdb.User{})
383386
 	for _, want := range []string{