Merge pull request #16129 from jgrigg/resolve-github-locations-via-login-annotation

Resolve locations via github annotations rather than entity name
This commit is contained in:
Patrik Oldsberg
2023-02-12 22:37:49 +01:00
committed by GitHub
6 changed files with 89 additions and 8 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-github': patch
---
Use github team slug and login annotations rather than entity name to infer location annotations
@@ -0,0 +1,37 @@
/*
* Copyright 2020 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* The value of this annotation is the so-called login that identifies a user on
[GitHub](https://github.com) (either the public one, or a private GitHub
Enterprise installation) that is related to this entity. It is on the format
`<username>`, and is the same as can be seen in the URL location bar of the
browser when viewing that user.
*
* @public
*/
export const ANNOTATION_GITHUB_USER_LOGIN = 'github.com/user-login';
/**
* The value of this annotation is the so-called slug that identifies a team on
[GitHub](https://github.com) (either the public one, or a private GitHub
Enterprise installation) that is related to this entity. It is on the format
`<organization>/<team>`, and is the same as can be seen in the URL location bar
of the browser when viewing that team.
*
* @public
*/
export const ANNOTATION_GITHUB_TEAM_SLUG = 'github.com/team-slug';
@@ -16,6 +16,10 @@
import { GroupEntity, UserEntity } from '@backstage/catalog-model';
import { graphql } from '@octokit/graphql';
import {
ANNOTATION_GITHUB_TEAM_SLUG,
ANNOTATION_GITHUB_USER_LOGIN,
} from './annotation';
import { GithubTeam, GithubUser } from './github';
/**
@@ -63,7 +67,7 @@ export const defaultUserTransformer: UserTransformer = async (
metadata: {
name: item.login,
annotations: {
'github.com/user-login': item.login,
[ANNOTATION_GITHUB_USER_LOGIN]: item.login,
},
},
spec: {
@@ -87,7 +91,7 @@ export const defaultUserTransformer: UserTransformer = async (
export const defaultOrganizationTeamTransformer: TeamTransformer =
async team => {
const annotations: { [annotationName: string]: string } = {
'github.com/team-slug': team.combinedSlug,
[ANNOTATION_GITHUB_TEAM_SLUG]: team.combinedSlug,
};
if (team.editTeamUrl) {
@@ -88,3 +88,14 @@ export function satisfiesForkFilter(
// if forks are allowed, allow all repos through
return true;
}
// Given the github organisation team slug, returns a tuple containing [organisation, team]
export function splitTeamSlug(slug: string): [string, string] {
const parts = slug.split('/');
if (parts.length !== 2) {
throw new Error(
`Github team slug '${slug}' was not in the expected format <organisation>/<team>`,
);
}
return [parts[0], parts[1]];
}
@@ -181,7 +181,10 @@ describe('GithubOrgEntityProvider', () => {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name: 'githubuser',
name: 'user-name',
annotations: {
'github.com/user-login': 'githubuser',
},
},
spec: {
memberOf: [],
@@ -192,12 +195,13 @@ describe('GithubOrgEntityProvider', () => {
apiVersion: 'backstage.io/v1alpha1',
kind: 'User',
metadata: {
name: 'githubuser',
name: 'user-name',
annotations: {
'backstage.io/managed-by-location':
'url:https://github.com/githubuser',
'backstage.io/managed-by-origin-location':
'url:https://github.com/githubuser',
'github.com/user-login': 'githubuser',
},
},
spec: {
@@ -211,7 +215,10 @@ describe('GithubOrgEntityProvider', () => {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'mygroup',
name: 'group-name',
annotations: {
'github.com/team-slug': 'backstage/mygroup',
},
},
spec: {
type: 'team',
@@ -223,12 +230,13 @@ describe('GithubOrgEntityProvider', () => {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Group',
metadata: {
name: 'mygroup',
name: 'group-name',
annotations: {
'backstage.io/managed-by-location':
'url:https://github.com/orgs/backstage/teams/mygroup',
'backstage.io/managed-by-origin-location':
'url:https://github.com/orgs/backstage/teams/mygroup',
'github.com/team-slug': 'backstage/mygroup',
},
},
spec: {
@@ -65,6 +65,11 @@ import {
getOrganizationTeam,
getOrganizationTeamsFromUsers,
} from '../lib/github';
import {
ANNOTATION_GITHUB_TEAM_SLUG,
ANNOTATION_GITHUB_USER_LOGIN,
} from '../lib/annotation';
import { splitTeamSlug } from '../lib/util';
/**
* Options for {@link GithubOrgEntityProvider}.
@@ -602,10 +607,21 @@ export function withLocations(
org: string,
entity: Entity,
): Entity {
const login =
entity.metadata.annotations?.[ANNOTATION_GITHUB_USER_LOGIN] ||
entity.metadata.name;
let team = entity.metadata.name;
const slug = entity.metadata.annotations?.[ANNOTATION_GITHUB_TEAM_SLUG];
if (slug) {
const [_, slugTeam] = splitTeamSlug(slug);
team = slugTeam;
}
const location =
entity.kind === 'Group'
? `url:${baseUrl}/orgs/${org}/teams/${entity.metadata.name}`
: `url:${baseUrl}/${entity.metadata.name}`;
? `url:${baseUrl}/orgs/${org}/teams/${team}`
: `url:${baseUrl}/${login}`;
return merge(
{
metadata: {