From 6dae27b200b859d094f94267d840355f91463aee Mon Sep 17 00:00:00 2001 From: Paul Querna Date: Thu, 23 Jul 2026 06:58:28 +0000 Subject: [PATCH] Repair main golangci-lint SA1019 baseline (trait-profile deprecations) The 2026-07-21 baton-sdk dependency bump deprecated the trait-level profile/status/icon/created_at options, turning `verify / lint` red on main across untouched trait-profile call sites (15 SA1019 findings, golangci-lint v2.11.4). Migrate producers to the resource-level replacements (WithResourceProfile / WithResourceStatus / WithResourceIcon / WithResourceCreatedAt) and switch reads to the fallback-aware resourceSdk.GetProfile. Connector output is preserved: profile/icon/ created_at relocate from the deprecated, duplicated trait fields to their canonical Resource-level attributes with identical values, and status is preserved on both levels. Invitation status intentionally keeps WithStatus(UNSPECIFIED) under a targeted //nolint:staticcheck: WithResourceStatus would let NewUserTrait force the (unset) trait status to ENABLED, misrepresenting a pending or expired invite as an enabled user. Verified with CI-exact golangci-lint v2.11.4 (0 issues), the full test suite + race on pkg/connector, go build, and go generate. Co-authored-by: c1-squire-dev[bot] --- pkg/connector/api_token.go | 7 ++++++- pkg/connector/app.go | 4 +++- pkg/connector/app_test.go | 8 +++++--- pkg/connector/invitation.go | 11 ++++++++++- pkg/connector/invitation_test.go | 6 ++++-- pkg/connector/org_role.go | 6 +++--- pkg/connector/team.go | 23 ++++++++++------------- pkg/connector/user.go | 22 +++++++++++++++------- 8 files changed, 56 insertions(+), 31 deletions(-) diff --git a/pkg/connector/api_token.go b/pkg/connector/api_token.go index 5f0852e8..7a6a7fea 100644 --- a/pkg/connector/api_token.go +++ b/pkg/connector/api_token.go @@ -26,8 +26,12 @@ func apiTokenResource(ctx context.Context, token *github.PersonalAccessToken) (* options = append(options, resourceSdk.WithSecretLastUsedAt(token.TokenLastUsedAt.Time)) } + // created_at has moved from SecretTrait to a Resource-level attribute, so + // set it via the resource-level option instead of the deprecated + // WithSecretCreatedAt trait option. + var resourceOpts []resourceSdk.ResourceOption if token.AccessGrantedAt != nil { - options = append(options, resourceSdk.WithSecretCreatedAt(token.AccessGrantedAt.Time)) + resourceOpts = append(resourceOpts, resourceSdk.WithResourceCreatedAt(token.AccessGrantedAt.Time)) } if token.TokenExpiresAt != nil { @@ -38,6 +42,7 @@ func apiTokenResource(ctx context.Context, token *github.PersonalAccessToken) (* resourceTypeApiToken, token.GetID(), options, + resourceOpts..., ) if err != nil { return nil, err diff --git a/pkg/connector/app.go b/pkg/connector/app.go index cafe9ed9..8f5cb7c1 100644 --- a/pkg/connector/app.go +++ b/pkg/connector/app.go @@ -39,7 +39,9 @@ func appResource(ctx context.Context, installation *github.Installation, parentR opts := []resourceSdk.ResourceOption{ resourceSdk.WithParentResourceID(parentResourceID), - resourceSdk.WithAppTrait(resourceSdk.WithAppProfile(profile)), + resourceSdk.WithAppTrait(), + // profile has moved from AppTrait to a Resource-level attribute. + resourceSdk.WithResourceProfile(profile), resourceSdk.WithNHIType(v2.NonHumanIdentityTrait_NHI_TYPE_APP_REGISTRATION, "github.app"), } if installation.HTMLURL != nil { diff --git a/pkg/connector/app_test.go b/pkg/connector/app_test.go index eb5e4e4c..c222477a 100644 --- a/pkg/connector/app_test.go +++ b/pkg/connector/app_test.go @@ -36,12 +36,14 @@ func TestAppResource(t *testing.T) { require.Equal(t, v2.NonHumanIdentityTrait_NHI_TYPE_APP_REGISTRATION, nhi.GetNhiType()) require.Equal(t, "github.app", nhi.GetNhiDetail()) - app, err := resourceSdk.GetAppTrait(resource) + _, err = resourceSdk.GetAppTrait(resource) require.NoError(t, err) - appID, ok := resourceSdk.GetProfileInt64Value(app.GetProfile(), "app_id") + // profile is now a Resource-level attribute; read it via GetProfile. + profile := resourceSdk.GetProfile(resource) + appID, ok := resourceSdk.GetProfileInt64Value(profile, "app_id") require.True(t, ok) require.Equal(t, int64(7), appID) - login, ok := resourceSdk.GetProfileStringValue(app.GetProfile(), "account_login") + login, ok := resourceSdk.GetProfileStringValue(profile, "account_login") require.True(t, ok) require.Equal(t, "acme", login) } diff --git a/pkg/connector/invitation.go b/pkg/connector/invitation.go index 52df44a2..fe49b5c6 100644 --- a/pkg/connector/invitation.go +++ b/pkg/connector/invitation.go @@ -64,10 +64,19 @@ func invitationToUserResource(invitation *github.Invitation, status string) (*v2 invitation.GetID(), []resourceSdk.UserTraitOption{ resourceSdk.WithEmail(invitation.GetEmail(), true), - resourceSdk.WithUserProfile(profile), + // An invitation is a pending/expired user that must not be + // reported as enabled. WithResourceStatus cannot express this: + // NewUserTrait force-defaults an unset trait status to ENABLED, so + // migrating this line would flip the emitted status from + // UNSPECIFIED to ENABLED. Keep the deprecated trait option (which + // also mirrors UNSPECIFIED to the resource level) to preserve the + // exact status semantics. + //nolint:staticcheck // deliberate: WithResourceStatus would force the trait status to ENABLED; UNSPECIFIED must be preserved for invitations. resourceSdk.WithStatus(v2.UserTrait_Status_STATUS_UNSPECIFIED), resourceSdk.WithUserLogin(login), }, + // profile has moved from UserTrait to a Resource-level attribute. + resourceSdk.WithResourceProfile(profile), ) if err != nil { return nil, err diff --git a/pkg/connector/invitation_test.go b/pkg/connector/invitation_test.go index e466366e..9f79e299 100644 --- a/pkg/connector/invitation_test.go +++ b/pkg/connector/invitation_test.go @@ -257,6 +257,8 @@ func invitationProfile(t *testing.T, r *v2.Resource) map[string]any { ut, err := resourceSdk.GetUserTrait(r) require.NoError(t, err) require.NotNil(t, ut) - require.NotNil(t, ut.Profile) - return ut.Profile.AsMap() + // profile is now a Resource-level attribute; read it via GetProfile. + profile := resourceSdk.GetProfile(r) + require.NotNil(t, profile) + return profile.AsMap() } diff --git a/pkg/connector/org_role.go b/pkg/connector/org_role.go index e78c30a0..32942686 100644 --- a/pkg/connector/org_role.go +++ b/pkg/connector/org_role.go @@ -53,9 +53,9 @@ func orgRoleResource( role.Name, resourceTypeOrgRole, role.ID, - []resourceSdk.RoleTraitOption{ - resourceSdk.WithRoleProfile(profile), - }, + []resourceSdk.RoleTraitOption{}, + // profile has moved from RoleTrait to a Resource-level attribute. + resourceSdk.WithResourceProfile(profile), resourceSdk.WithParentResourceID(org.Id), resourceSdk.WithAnnotation( &v2.V1Identifier{Id: fmt.Sprintf("org_role:%d", role.ID)}, diff --git a/pkg/connector/team.go b/pkg/connector/team.go index 8e1579ba..0dc2eb24 100644 --- a/pkg/connector/team.go +++ b/pkg/connector/team.go @@ -45,7 +45,9 @@ func teamResource(team *github.Team, orgID int64, parentResourceID *v2.ResourceI team.GetName(), resourceTypeTeam, team.GetID(), - []rType.GroupTraitOption{rType.WithGroupProfile(profile)}, + []rType.GroupTraitOption{}, + // profile has moved from GroupTrait to a Resource-level attribute. + rType.WithResourceProfile(profile), rType.WithAnnotation( &v2.ExternalLink{Url: team.GetURL()}, &v2.V1Identifier{Id: fmt.Sprintf("team:%d", team.GetID())}, @@ -159,12 +161,10 @@ func (o *teamResourceType) Grants(ctx context.Context, resource *v2.Resource, op return nil, nil, err } - teamTrait, err := rType.GetGroupTrait(resource) - if err != nil { - return nil, nil, err - } - - orgID, ok := rType.GetProfileInt64Value(teamTrait.Profile, "orgID") + // profile has moved from GroupTrait to a Resource-level attribute; read it + // via GetProfile, which resolves the resource-level value (with a + // trait-level fallback for older data). + orgID, ok := rType.GetProfileInt64Value(rType.GetProfile(resource), "orgID") if !ok { return nil, nil, fmt.Errorf("error fetching orgID from team profile") } @@ -280,12 +280,9 @@ func (o *teamResourceType) Grant(ctx context.Context, principal *v2.Resource, en return nil, err } case resourceTypeTeam.Id: - groupTrait, err := rType.GetGroupTrait(entitlement.Resource) - if err != nil { - return nil, err - } - - orgID, ok := rType.GetProfileInt64Value(groupTrait.Profile, "orgID") + // profile has moved from GroupTrait to a Resource-level attribute; read + // it via GetProfile (resource-level value, with a trait-level fallback). + orgID, ok := rType.GetProfileInt64Value(rType.GetProfile(entitlement.Resource), "orgID") if !ok { return nil, fmt.Errorf("error fetching orgID from team profile") } diff --git a/pkg/connector/user.go b/pkg/connector/user.go index d5c6568f..705507a5 100644 --- a/pkg/connector/user.go +++ b/pkg/connector/user.go @@ -48,8 +48,14 @@ func userResource(ctx context.Context, user *github.User, userEmail string, extr userTrait := []resource.UserTraitOption{ resource.WithEmail(userEmail, true), - resource.WithUserProfile(profile), - resource.WithStatus(v2.UserTrait_Status_STATUS_ENABLED), + } + + // profile, status, and icon have moved from UserTrait to Resource-level + // attributes. NewUserTrait still defaults the (unset) trait status to + // ENABLED, so the enabled semantics are preserved on both levels. + resourceOpts := []resource.ResourceOption{ + resource.WithResourceProfile(profile), + resource.WithResourceStatus(v2.Status_RESOURCE_STATUS_ENABLED, ""), } for _, email := range extraEmails { @@ -57,7 +63,7 @@ func userResource(ctx context.Context, user *github.User, userEmail string, extr } if user.GetAvatarURL() != "" { - userTrait = append(userTrait, resource.WithUserIcon(&v2.AssetRef{ + resourceOpts = append(resourceOpts, resource.WithResourceIcon(&v2.AssetRef{ Id: user.GetAvatarURL(), })) } @@ -70,15 +76,17 @@ func userResource(ctx context.Context, user *github.User, userEmail string, extr })) } + resourceOpts = append(resourceOpts, resource.WithAnnotation( + &v2.ExternalLink{Url: user.GetHTMLURL()}, + &v2.V1Identifier{Id: strconv.FormatInt(user.GetID(), 10)}, + )) + ret, err := resource.NewUserResource( displayName, resourceTypeUser, user.GetID(), userTrait, - resource.WithAnnotation( - &v2.ExternalLink{Url: user.GetHTMLURL()}, - &v2.V1Identifier{Id: strconv.FormatInt(user.GetID(), 10)}, - ), + resourceOpts..., ) if err != nil { return nil, err