@backstage-community/plugin-copilot-backend 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @backstage-community/plugin-copilot-backend
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6e44ae3: Added ability to use GitHub App authentication for organization level metrics
8
+
9
+ ## 0.3.2
10
+
11
+ ### Patch Changes
12
+
13
+ - e9b265d: Removed usages of `@backstage/backend-tasks`
14
+
3
15
  ## 0.3.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -36,11 +36,11 @@ To install the plugin using the old method:
36
36
  1. In your `packages/backend/src/plugins/copilot.ts` file, add the following code:
37
37
 
38
38
  ```typescript
39
- import { TaskScheduleDefinition } from '@backstage/backend-tasks';
39
+ import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api';
40
40
  import { createRouterFromConfig } from '@backstage-community/plugin-copilot-backend';
41
41
 
42
42
  export default async function createPlugin(): Promise<void> {
43
- const schedule: TaskScheduleDefinition = {
43
+ const schedule: SchedulerServiceTaskScheduleDefinition = {
44
44
  frequency: { cron: '0 2 * * *' },
45
45
  timeout: { minutes: 15 },
46
46
  initialDelay: { seconds: 15 },
@@ -63,9 +63,9 @@ To install the plugin using the old method:
63
63
 
64
64
  ## Configuration
65
65
 
66
- ### Environment Variables
66
+ ### App Config
67
67
 
68
- To configure the GitHub Copilot plugin, you need to set the following environment variables:
68
+ To configure the GitHub Copilot plugin, you need to set the following values in the app-config:
69
69
 
70
70
  - **`copilot.host`**: The host URL for your GitHub Copilot instance (e.g., `github.com` or `github.enterprise.com`).
71
71
  - **`copilot.enterprise`**: The name of your GitHub Enterprise instance (e.g., `my-enterprise`).
@@ -75,9 +75,14 @@ These variables are used to configure the plugin and ensure it communicates with
75
75
 
76
76
  ### GitHub Credentials
77
77
 
78
- **Important:** The GitHub token, necessary for authentication, should be managed within your Backstage integrations configuration. Ensure that your GitHub integration in the Backstage configuration includes the necessary token for the `GithubCredentialsProvider` to function correctly.
78
+ GitHub support different auth methods depending on which API you are using.
79
79
 
80
- ### GitHub Token Scopes
80
+ - Enterprise API - [only supports "classic" PAT tokens](https://docs.github.com/en/enterprise-cloud@latest/rest/copilot/copilot-usage?apiVersion=2022-11-28#get-a-summary-of-copilot-usage-for-enterprise-members)
81
+ - Org Api - [Supports app tokens, "classic", and fine grained PAT tokens](https://docs.github.com/en/enterprise-cloud@latest/rest/copilot/copilot-usage?apiVersion=2022-11-28#get-a-summary-of-copilot-usage-for-organization-members)
82
+
83
+ This plugin supports both schemes and detects the best scheme based on which API(s) you have configured for use.
84
+
85
+ ### GitHub Token/App Scopes
81
86
 
82
87
  To ensure the GitHub Copilot plugin operates correctly within your organization or enterprise, your GitHub access token must include specific scopes. These scopes grant the plugin the necessary permissions to interact with your GitHub organization and manage Copilot usage.
83
88
 
@@ -95,10 +100,18 @@ To ensure the GitHub Copilot plugin operates correctly within your organization
95
100
 
96
101
  #### How to Configure Token Scopes
97
102
 
98
- 1. **Generate a Personal Access Token (PAT):**
99
- - Navigate to [GitHub Personal Access Tokens](https://github.com/settings/tokens).
100
- - Click on **Generate new token**.
101
- - Select the scopes according to your needs
103
+ **Generate a Personal Access Token (PAT) (Entperise only supports "classic" PAT tokens)**
104
+
105
+ - Navigate to [GitHub Personal Access Tokens](https://github.com/settings/tokens).
106
+ - Click on **Generate new token**.
107
+ - Select the scopes according to your needs
108
+
109
+ **Using a GitHub App**
110
+
111
+ - Create or reuse an existing GitHub App that you own.
112
+ - Navigate to the app permissions
113
+ - Select the permissions to read the org and manage billing for copilot and save
114
+ - Install and update permissions in your oeg.
102
115
 
103
116
  ### YAML Configuration Example
104
117
 
@@ -115,12 +128,28 @@ copilot:
115
128
  enterprise: YOUR_ENTERPRISE_NAME_HERE
116
129
  organization: YOUR_ORGANIZATION_NAME_HERE
117
130
 
131
+ # Using a PAT
118
132
  integrations:
119
133
  github:
120
134
  - host: YOUR_GITHUB_HOST_HERE
121
135
  token: YOUR_GENERATED_TOKEN
136
+
137
+ # Using a GitHub App
138
+ integrations:
139
+ github:
140
+ - host: github.com
141
+ apps:
142
+ - appId: YOUR_APP_ID
143
+ allowedInstallationOwners:
144
+ - YOUR_ORG_NAME
145
+ clientId: CLIENT_ID
146
+ clientSecret: CLIENT_SECRET
147
+ webhookSecret: WEBHOOK_SECRET
148
+ privateKey: PRIVATE_KEY
122
149
  ```
123
150
 
151
+ [You can find more about the integrations config in the official docs](https://backstage.io/docs/integrations/github/locations/)
152
+
124
153
  ### API Documentation
125
154
 
126
155
  For more details on using the GitHub Copilot and Teams APIs, refer to the following documentation:
@@ -9,41 +9,47 @@ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'defau
9
9
  var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
10
10
 
11
11
  class GithubClient {
12
- constructor(props) {
13
- this.props = props;
12
+ constructor(copilotConfig, config) {
13
+ this.copilotConfig = copilotConfig;
14
+ this.config = config;
14
15
  }
15
16
  static async fromConfig(config) {
16
- const info = await GithubUtils.getGithubInfo(config);
17
- return new GithubClient(info);
17
+ const info = GithubUtils.getCopilotConfig(config);
18
+ return new GithubClient(info, config);
19
+ }
20
+ async getCredentials() {
21
+ return await GithubUtils.getGithubCredentials(this.config, this.copilotConfig);
18
22
  }
19
23
  async fetchEnterpriseCopilotUsage() {
20
- const path = `/enterprises/${this.props.enterprise}/copilot/usage`;
24
+ const path = `/enterprises/${this.copilotConfig.enterprise}/copilot/usage`;
21
25
  return this.get(path);
22
26
  }
23
27
  async fetchEnterpriseTeamCopilotUsage(teamId) {
24
- const path = `/enterprises/${this.props.enterprise}/team/${teamId}/copilot/usage`;
28
+ const path = `/enterprises/${this.copilotConfig.enterprise}/team/${teamId}/copilot/usage`;
25
29
  return this.get(path);
26
30
  }
27
31
  async fetchEnterpriseTeams() {
28
- const path = `/enterprises/${this.props.enterprise}/teams`;
32
+ const path = `/enterprises/${this.copilotConfig.enterprise}/teams`;
29
33
  return this.get(path);
30
34
  }
31
35
  async fetchOrganizationCopilotUsage() {
32
- const path = `/orgs/${this.props.organization}/copilot/usage`;
36
+ const path = `/orgs/${this.copilotConfig.organization}/copilot/usage`;
33
37
  return this.get(path);
34
38
  }
35
39
  async fetchOrganizationTeamCopilotUsage(teamId) {
36
- const path = `/orgs/${this.props.organization}/team/${teamId}/copilot/usage`;
40
+ const path = `/orgs/${this.copilotConfig.organization}/team/${teamId}/copilot/usage`;
37
41
  return this.get(path);
38
42
  }
39
43
  async fetchOrganizationTeams() {
40
- const path = `/orgs/${this.props.organization}/teams`;
44
+ const path = `/orgs/${this.copilotConfig.organization}/teams`;
41
45
  return this.get(path);
42
46
  }
43
47
  async get(path) {
44
- const response = await fetch__default.default(`${this.props.apiBaseUrl}${path}`, {
48
+ const credentials = await this.getCredentials();
49
+ const headers = path.startsWith("/enterprises") ? credentials.enterprise?.headers : credentials.organization?.headers;
50
+ const response = await fetch__default.default(`${this.copilotConfig.apiBaseUrl}${path}`, {
45
51
  headers: {
46
- ...this.props.credentials.headers,
52
+ ...headers,
47
53
  Accept: "application/vnd.github+json",
48
54
  "X-GitHub-Api-Version": "2022-11-28"
49
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GithubClient.cjs.js","sources":["../../src/client/GithubClient.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ResponseError } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport { Metric, TeamInfo } from '@backstage-community/plugin-copilot-common';\nimport fetch from 'node-fetch';\nimport { getGithubInfo, GithubInfo } from '../utils/GithubUtils';\n\ninterface GithubApi {\n fetchEnterpriseCopilotUsage: () => Promise<Metric[]>;\n fetchEnterpriseTeamCopilotUsage: (teamId: string) => Promise<Metric[]>;\n fetchEnterpriseTeams: () => Promise<TeamInfo[]>;\n fetchOrganizationCopilotUsage: () => Promise<Metric[]>;\n fetchOrganizationTeamCopilotUsage: (teamId: string) => Promise<Metric[]>;\n fetchOrganizationTeams: () => Promise<TeamInfo[]>;\n}\n\nexport class GithubClient implements GithubApi {\n constructor(private readonly props: GithubInfo) {}\n\n static async fromConfig(config: Config) {\n const info = await getGithubInfo(config);\n return new GithubClient(info);\n }\n\n async fetchEnterpriseCopilotUsage(): Promise<Metric[]> {\n const path = `/enterprises/${this.props.enterprise}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchEnterpriseTeamCopilotUsage(teamId: string): Promise<Metric[]> {\n const path = `/enterprises/${this.props.enterprise}/team/${teamId}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchEnterpriseTeams(): Promise<TeamInfo[]> {\n const path = `/enterprises/${this.props.enterprise}/teams`;\n return this.get(path);\n }\n\n async fetchOrganizationCopilotUsage(): Promise<Metric[]> {\n const path = `/orgs/${this.props.organization}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchOrganizationTeamCopilotUsage(teamId: string): Promise<Metric[]> {\n const path = `/orgs/${this.props.organization}/team/${teamId}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchOrganizationTeams(): Promise<TeamInfo[]> {\n const path = `/orgs/${this.props.organization}/teams`;\n return this.get(path);\n }\n\n private async get<T>(path: string): Promise<T> {\n const response = await fetch(`${this.props.apiBaseUrl}${path}`, {\n headers: {\n ...this.props.credentials.headers,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json() as Promise<T>;\n }\n}\n"],"names":["getGithubInfo","fetch","ResponseError"],"mappings":";;;;;;;;;;AA+BO,MAAM,YAAkC,CAAA;AAAA,EAC7C,YAA6B,KAAmB,EAAA;AAAnB,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA;AAAoB,EAEjD,aAAa,WAAW,MAAgB,EAAA;AACtC,IAAM,MAAA,IAAA,GAAO,MAAMA,yBAAA,CAAc,MAAM,CAAA;AACvC,IAAO,OAAA,IAAI,aAAa,IAAI,CAAA;AAAA;AAC9B,EAEA,MAAM,2BAAiD,GAAA;AACrD,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,KAAA,CAAM,UAAU,CAAA,cAAA,CAAA;AAClD,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,gCAAgC,MAAmC,EAAA;AACvE,IAAA,MAAM,OAAO,CAAgB,aAAA,EAAA,IAAA,CAAK,KAAM,CAAA,UAAU,SAAS,MAAM,CAAA,cAAA,CAAA;AACjE,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,oBAA4C,GAAA;AAChD,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,KAAA,CAAM,UAAU,CAAA,MAAA,CAAA;AAClD,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,6BAAmD,GAAA;AACvD,IAAA,MAAM,IAAO,GAAA,CAAA,MAAA,EAAS,IAAK,CAAA,KAAA,CAAM,YAAY,CAAA,cAAA,CAAA;AAC7C,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,kCAAkC,MAAmC,EAAA;AACzE,IAAA,MAAM,OAAO,CAAS,MAAA,EAAA,IAAA,CAAK,KAAM,CAAA,YAAY,SAAS,MAAM,CAAA,cAAA,CAAA;AAC5D,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,sBAA8C,GAAA;AAClD,IAAA,MAAM,IAAO,GAAA,CAAA,MAAA,EAAS,IAAK,CAAA,KAAA,CAAM,YAAY,CAAA,MAAA,CAAA;AAC7C,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAc,IAAO,IAA0B,EAAA;AAC7C,IAAM,MAAA,QAAA,GAAW,MAAMC,sBAAM,CAAA,CAAA,EAAG,KAAK,KAAM,CAAA,UAAU,CAAG,EAAA,IAAI,CAAI,CAAA,EAAA;AAAA,MAC9D,OAAS,EAAA;AAAA,QACP,GAAG,IAAK,CAAA,KAAA,CAAM,WAAY,CAAA,OAAA;AAAA,QAC1B,MAAQ,EAAA,6BAAA;AAAA,QACR,sBAAwB,EAAA;AAAA;AAC1B,KACD,CAAA;AAED,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,MAAMC,oBAAc,CAAA,YAAA,CAAa,QAAQ,CAAA;AAAA;AAGjD,IAAA,OAAO,SAAS,IAAK,EAAA;AAAA;AAEzB;;;;"}
1
+ {"version":3,"file":"GithubClient.cjs.js","sources":["../../src/client/GithubClient.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ResponseError } from '@backstage/errors';\nimport { Config } from '@backstage/config';\nimport { Metric, TeamInfo } from '@backstage-community/plugin-copilot-common';\nimport fetch from 'node-fetch';\nimport {\n CopilotConfig,\n CopilotCredentials,\n getCopilotConfig,\n getGithubCredentials,\n} from '../utils/GithubUtils';\n\ninterface GithubApi {\n fetchEnterpriseCopilotUsage: () => Promise<Metric[]>;\n fetchEnterpriseTeamCopilotUsage: (teamId: string) => Promise<Metric[]>;\n fetchEnterpriseTeams: () => Promise<TeamInfo[]>;\n fetchOrganizationCopilotUsage: () => Promise<Metric[]>;\n fetchOrganizationTeamCopilotUsage: (teamId: string) => Promise<Metric[]>;\n fetchOrganizationTeams: () => Promise<TeamInfo[]>;\n}\n\nexport class GithubClient implements GithubApi {\n constructor(\n private readonly copilotConfig: CopilotConfig,\n private readonly config: Config,\n ) {}\n\n static async fromConfig(config: Config) {\n const info = getCopilotConfig(config);\n return new GithubClient(info, config);\n }\n\n private async getCredentials(): Promise<CopilotCredentials> {\n return await getGithubCredentials(this.config, this.copilotConfig);\n }\n\n async fetchEnterpriseCopilotUsage(): Promise<Metric[]> {\n const path = `/enterprises/${this.copilotConfig.enterprise}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchEnterpriseTeamCopilotUsage(teamId: string): Promise<Metric[]> {\n const path = `/enterprises/${this.copilotConfig.enterprise}/team/${teamId}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchEnterpriseTeams(): Promise<TeamInfo[]> {\n const path = `/enterprises/${this.copilotConfig.enterprise}/teams`;\n return this.get(path);\n }\n\n async fetchOrganizationCopilotUsage(): Promise<Metric[]> {\n const path = `/orgs/${this.copilotConfig.organization}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchOrganizationTeamCopilotUsage(teamId: string): Promise<Metric[]> {\n const path = `/orgs/${this.copilotConfig.organization}/team/${teamId}/copilot/usage`;\n return this.get(path);\n }\n\n async fetchOrganizationTeams(): Promise<TeamInfo[]> {\n const path = `/orgs/${this.copilotConfig.organization}/teams`;\n return this.get(path);\n }\n\n private async get<T>(path: string): Promise<T> {\n const credentials = await this.getCredentials();\n const headers = path.startsWith('/enterprises')\n ? credentials.enterprise?.headers\n : credentials.organization?.headers;\n\n const response = await fetch(`${this.copilotConfig.apiBaseUrl}${path}`, {\n headers: {\n ...headers,\n Accept: 'application/vnd.github+json',\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json() as Promise<T>;\n }\n}\n"],"names":["getCopilotConfig","getGithubCredentials","fetch","ResponseError"],"mappings":";;;;;;;;;;AAoCO,MAAM,YAAkC,CAAA;AAAA,EAC7C,WAAA,CACmB,eACA,MACjB,EAAA;AAFiB,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA;AAChB,EAEH,aAAa,WAAW,MAAgB,EAAA;AACtC,IAAM,MAAA,IAAA,GAAOA,6BAAiB,MAAM,CAAA;AACpC,IAAO,OAAA,IAAI,YAAa,CAAA,IAAA,EAAM,MAAM,CAAA;AAAA;AACtC,EAEA,MAAc,cAA8C,GAAA;AAC1D,IAAA,OAAO,MAAMC,gCAAA,CAAqB,IAAK,CAAA,MAAA,EAAQ,KAAK,aAAa,CAAA;AAAA;AACnE,EAEA,MAAM,2BAAiD,GAAA;AACrD,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,aAAA,CAAc,UAAU,CAAA,cAAA,CAAA;AAC1D,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,gCAAgC,MAAmC,EAAA;AACvE,IAAA,MAAM,OAAO,CAAgB,aAAA,EAAA,IAAA,CAAK,aAAc,CAAA,UAAU,SAAS,MAAM,CAAA,cAAA,CAAA;AACzE,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,oBAA4C,GAAA;AAChD,IAAA,MAAM,IAAO,GAAA,CAAA,aAAA,EAAgB,IAAK,CAAA,aAAA,CAAc,UAAU,CAAA,MAAA,CAAA;AAC1D,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,6BAAmD,GAAA;AACvD,IAAA,MAAM,IAAO,GAAA,CAAA,MAAA,EAAS,IAAK,CAAA,aAAA,CAAc,YAAY,CAAA,cAAA,CAAA;AACrD,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,kCAAkC,MAAmC,EAAA;AACzE,IAAA,MAAM,OAAO,CAAS,MAAA,EAAA,IAAA,CAAK,aAAc,CAAA,YAAY,SAAS,MAAM,CAAA,cAAA,CAAA;AACpE,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAM,sBAA8C,GAAA;AAClD,IAAA,MAAM,IAAO,GAAA,CAAA,MAAA,EAAS,IAAK,CAAA,aAAA,CAAc,YAAY,CAAA,MAAA,CAAA;AACrD,IAAO,OAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA;AACtB,EAEA,MAAc,IAAO,IAA0B,EAAA;AAC7C,IAAM,MAAA,WAAA,GAAc,MAAM,IAAA,CAAK,cAAe,EAAA;AAC9C,IAAM,MAAA,OAAA,GAAU,KAAK,UAAW,CAAA,cAAc,IAC1C,WAAY,CAAA,UAAA,EAAY,OACxB,GAAA,WAAA,CAAY,YAAc,EAAA,OAAA;AAE9B,IAAM,MAAA,QAAA,GAAW,MAAMC,sBAAM,CAAA,CAAA,EAAG,KAAK,aAAc,CAAA,UAAU,CAAG,EAAA,IAAI,CAAI,CAAA,EAAA;AAAA,MACtE,OAAS,EAAA;AAAA,QACP,GAAG,OAAA;AAAA,QACH,MAAQ,EAAA,6BAAA;AAAA,QACR,sBAAwB,EAAA;AAAA;AAC1B,KACD,CAAA;AAED,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,MAAMC,oBAAc,CAAA,YAAA,CAAa,QAAQ,CAAA;AAAA;AAGjD,IAAA,OAAO,SAAS,IAAK,EAAA;AAAA;AAEzB;;;;"}
@@ -2,33 +2,83 @@
2
2
 
3
3
  var integration = require('@backstage/integration');
4
4
 
5
- const getGithubInfo = async (config) => {
6
- const integrations = integration.ScmIntegrations.fromConfig(config);
5
+ const getCopilotConfig = (config) => {
7
6
  const host = config.getString("copilot.host");
8
7
  const enterprise = config.getOptionalString("copilot.enterprise");
9
8
  const organization = config.getOptionalString("copilot.organization");
10
- if (!host) {
11
- throw new Error("The host configuration is missing from the config.");
9
+ const integrations = integration.ScmIntegrations.fromConfig(config);
10
+ const githubConfig = integrations.github.byHost(host)?.config;
11
+ if (!githubConfig) {
12
+ throw new Error(
13
+ `GitHub configuration for host "${host}" is missing or incomplete. Please check the integretions configuration section.`
14
+ );
15
+ }
16
+ if (enterprise && !githubConfig.token) {
17
+ throw new Error(
18
+ `Enterprise API for copilot only works with "classic PAT" tokens. No token is configured for "${host}" in the config.`
19
+ );
20
+ }
21
+ if (organization && !(githubConfig.token || githubConfig.apps)) {
22
+ throw new Error(
23
+ `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for "${host}" in the config.`
24
+ );
12
25
  }
26
+ return {
27
+ host,
28
+ enterprise,
29
+ organization,
30
+ apiBaseUrl: githubConfig.apiBaseUrl ?? "https://api.github.com"
31
+ };
32
+ };
33
+ const getGithubCredentials = async (config, copilotConfig) => {
34
+ const integrations = integration.ScmIntegrations.fromConfig(config);
35
+ const { host, enterprise, organization } = copilotConfig;
13
36
  const githubConfig = integrations.github.byHost(host)?.config;
14
37
  if (!githubConfig) {
15
38
  throw new Error(
16
39
  `GitHub configuration for host "${host}" is missing or incomplete.`
17
40
  );
18
41
  }
19
- const apiBaseUrl = githubConfig.apiBaseUrl ?? "https://api.github.com";
20
42
  const credentials = {
21
- type: "token",
22
- headers: { Authorization: `Bearer ${githubConfig.token}` },
23
- token: githubConfig.token
24
- };
25
- return {
26
- apiBaseUrl,
27
- credentials,
28
- enterprise,
29
- organization
43
+ enterprise: void 0,
44
+ organization: void 0
30
45
  };
46
+ if (enterprise) {
47
+ if (!githubConfig.token) {
48
+ throw new Error(
49
+ `Enterprise API for copilot only works with "classic PAT" tokens. No token is configured for "${host}" in the config.`
50
+ );
51
+ } else {
52
+ credentials.enterprise = {
53
+ type: "token",
54
+ headers: { Authorization: `Bearer ${githubConfig.token}` },
55
+ token: githubConfig.token
56
+ };
57
+ }
58
+ }
59
+ if (organization) {
60
+ if (githubConfig.apps) {
61
+ const githubCredentialsProvider = integration.DefaultGithubCredentialsProvider.fromIntegrations(integrations);
62
+ credentials.organization = await githubCredentialsProvider.getCredentials(
63
+ {
64
+ url: `https://${host}/${organization}`
65
+ }
66
+ );
67
+ } else if (githubConfig.token) {
68
+ credentials.organization = {
69
+ type: "token",
70
+ headers: { Authorization: `Bearer ${githubConfig.token}` },
71
+ token: githubConfig.token
72
+ };
73
+ } else {
74
+ throw new Error(
75
+ `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for "${host}" in the config.`
76
+ );
77
+ }
78
+ }
79
+ return credentials;
31
80
  };
32
81
 
33
- exports.getGithubInfo = getGithubInfo;
82
+ exports.getCopilotConfig = getCopilotConfig;
83
+ exports.getGithubCredentials = getGithubCredentials;
34
84
  //# sourceMappingURL=GithubUtils.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"GithubUtils.cjs.js","sources":["../../src/utils/GithubUtils.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { GithubCredentials, ScmIntegrations } from '@backstage/integration';\n\nexport type GithubInfo = {\n credentials: GithubCredentials;\n apiBaseUrl: string;\n enterprise?: string;\n organization?: string;\n};\n\nexport const getGithubInfo = async (config: Config): Promise<GithubInfo> => {\n const integrations = ScmIntegrations.fromConfig(config);\n\n const host = config.getString('copilot.host');\n const enterprise = config.getOptionalString('copilot.enterprise');\n const organization = config.getOptionalString('copilot.organization');\n\n if (!host) {\n throw new Error('The host configuration is missing from the config.');\n }\n\n const githubConfig = integrations.github.byHost(host)?.config;\n\n if (!githubConfig) {\n throw new Error(\n `GitHub configuration for host \"${host}\" is missing or incomplete.`,\n );\n }\n\n const apiBaseUrl = githubConfig.apiBaseUrl ?? 'https://api.github.com';\n\n const credentials: GithubCredentials = {\n type: 'token',\n headers: { Authorization: `Bearer ${githubConfig.token}` },\n token: githubConfig.token,\n };\n\n return {\n apiBaseUrl,\n credentials,\n enterprise,\n organization,\n };\n};\n"],"names":["ScmIntegrations"],"mappings":";;;;AA0Ba,MAAA,aAAA,GAAgB,OAAO,MAAwC,KAAA;AAC1E,EAAM,MAAA,YAAA,GAAeA,2BAAgB,CAAA,UAAA,CAAW,MAAM,CAAA;AAEtD,EAAM,MAAA,IAAA,GAAO,MAAO,CAAA,SAAA,CAAU,cAAc,CAAA;AAC5C,EAAM,MAAA,UAAA,GAAa,MAAO,CAAA,iBAAA,CAAkB,oBAAoB,CAAA;AAChE,EAAM,MAAA,YAAA,GAAe,MAAO,CAAA,iBAAA,CAAkB,sBAAsB,CAAA;AAEpE,EAAA,IAAI,CAAC,IAAM,EAAA;AACT,IAAM,MAAA,IAAI,MAAM,oDAAoD,CAAA;AAAA;AAGtE,EAAA,MAAM,YAAe,GAAA,YAAA,CAAa,MAAO,CAAA,MAAA,CAAO,IAAI,CAAG,EAAA,MAAA;AAEvD,EAAA,IAAI,CAAC,YAAc,EAAA;AACjB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,kCAAkC,IAAI,CAAA,2BAAA;AAAA,KACxC;AAAA;AAGF,EAAM,MAAA,UAAA,GAAa,aAAa,UAAc,IAAA,wBAAA;AAE9C,EAAA,MAAM,WAAiC,GAAA;AAAA,IACrC,IAAM,EAAA,OAAA;AAAA,IACN,SAAS,EAAE,aAAA,EAAe,CAAU,OAAA,EAAA,YAAA,CAAa,KAAK,CAAG,CAAA,EAAA;AAAA,IACzD,OAAO,YAAa,CAAA;AAAA,GACtB;AAEA,EAAO,OAAA;AAAA,IACL,UAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
1
+ {"version":3,"file":"GithubUtils.cjs.js","sources":["../../src/utils/GithubUtils.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport {\n DefaultGithubCredentialsProvider,\n GithubCredentials,\n ScmIntegrations,\n} from '@backstage/integration';\n\nexport type CopilotCredentials = {\n enterprise?: GithubCredentials;\n organization?: GithubCredentials;\n};\n\nexport type CopilotConfig = {\n host: string;\n enterprise?: string;\n organization?: string;\n apiBaseUrl: string;\n};\n\nexport const getCopilotConfig = (config: Config): CopilotConfig => {\n const host = config.getString('copilot.host');\n const enterprise = config.getOptionalString('copilot.enterprise');\n const organization = config.getOptionalString('copilot.organization');\n\n const integrations = ScmIntegrations.fromConfig(config);\n\n const githubConfig = integrations.github.byHost(host)?.config;\n\n if (!githubConfig) {\n throw new Error(\n `GitHub configuration for host \"${host}\" is missing or incomplete. Please check the integretions configuration section.`,\n );\n }\n\n if (enterprise && !githubConfig.token) {\n throw new Error(\n `Enterprise API for copilot only works with \"classic PAT\" tokens. No token is configured for \"${host}\" in the config.`,\n );\n }\n\n if (organization && !(githubConfig.token || githubConfig.apps)) {\n throw new Error(\n `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for \"${host}\" in the config.`,\n );\n }\n\n return {\n host,\n enterprise,\n organization,\n apiBaseUrl: githubConfig.apiBaseUrl ?? 'https://api.github.com',\n };\n};\n\nexport const getGithubCredentials = async (\n config: Config,\n copilotConfig: CopilotConfig,\n): Promise<CopilotCredentials> => {\n const integrations = ScmIntegrations.fromConfig(config);\n const { host, enterprise, organization } = copilotConfig;\n\n const githubConfig = integrations.github.byHost(host)?.config;\n\n if (!githubConfig) {\n throw new Error(\n `GitHub configuration for host \"${host}\" is missing or incomplete.`,\n );\n }\n\n const credentials: CopilotCredentials = {\n enterprise: undefined,\n organization: undefined,\n };\n\n if (enterprise) {\n if (!githubConfig.token) {\n throw new Error(\n `Enterprise API for copilot only works with \"classic PAT\" tokens. No token is configured for \"${host}\" in the config.`,\n );\n } else {\n credentials.enterprise = {\n type: 'token',\n headers: { Authorization: `Bearer ${githubConfig.token}` },\n token: githubConfig.token,\n };\n }\n }\n\n if (organization) {\n if (githubConfig.apps) {\n const githubCredentialsProvider =\n DefaultGithubCredentialsProvider.fromIntegrations(integrations);\n\n credentials.organization = await githubCredentialsProvider.getCredentials(\n {\n url: `https://${host}/${organization}`,\n },\n );\n } else if (githubConfig.token) {\n credentials.organization = {\n type: 'token',\n headers: { Authorization: `Bearer ${githubConfig.token}` },\n token: githubConfig.token,\n };\n } else {\n throw new Error(\n `Organization API for copilot works with both classic and fine grained PAT tokens or GitHub apps. No token or app is configured for \"${host}\" in the config.`,\n );\n }\n }\n\n return credentials;\n};\n"],"names":["ScmIntegrations","DefaultGithubCredentialsProvider"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage-community/plugin-copilot-backend",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "homepage": "https://backstage.io",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.cjs.js",
@@ -43,7 +43,6 @@
43
43
  "@backstage/backend-app-api": "^1.1.0",
44
44
  "@backstage/backend-defaults": "^0.6.2",
45
45
  "@backstage/backend-plugin-api": "^1.1.0",
46
- "@backstage/backend-tasks": "^0.6.1",
47
46
  "@backstage/config": "^1.3.1",
48
47
  "@backstage/errors": "^1.2.6",
49
48
  "@backstage/integration": "^1.16.0",