mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-11 20:05:25 +00:00
feat(connections): Add Azure Devops Support (#514)
* initial ado pol * add support for ado logo * default to main instead of HEAD when generating file url * bump zoekt * fix(web) Fix "At least one project, user, or group must be specified" for GitLab configs in web configurator (#512) * feat(ask_sb): Fallback on fromNodeProviderChain if access key or sessionToken are not provided (#513) * Quote branches argument in zoekt.ts to fix Pipe (#506) * remove connections settings page * fix styling and remove additional components * add changelog * add docs * fix build error * bump zoekt * fix broken links for ado docs * fix HEAD support for ado * changelog --------- Co-authored-by: Brendan Kellam <bshizzle1234@gmail.com> Co-authored-by: Michael Dekoski <michaeldekoski@gmail.com>
This commit is contained in:
parent
db6e5d4841
commit
e7fa4c4765
34 changed files with 2488 additions and 25 deletions
|
|
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Added fallback to default the Node.JS AWS SDK's `fromNodeProviderChain` when no credentials are provided for a bedrock config. [#513](https://github.com/sourcebot-dev/sourcebot/pull/513)
|
- Added fallback to default the Node.JS AWS SDK's `fromNodeProviderChain` when no credentials are provided for a bedrock config. [#513](https://github.com/sourcebot-dev/sourcebot/pull/513)
|
||||||
|
- Added support for Azure Devops support. [#514](https://github.com/sourcebot-dev/sourcebot/pull/514)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed "At least one project, user, or group must be specified" for GitLab configs with `all` in web configurator. [#512](https://github.com/sourcebot-dev/sourcebot/pull/512)
|
- Fixed "At least one project, user, or group must be specified" for GitLab configs with `all` in web configurator. [#512](https://github.com/sourcebot-dev/sourcebot/pull/512)
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,8 @@
|
||||||
"docs/connections/gitlab",
|
"docs/connections/gitlab",
|
||||||
"docs/connections/bitbucket-cloud",
|
"docs/connections/bitbucket-cloud",
|
||||||
"docs/connections/bitbucket-data-center",
|
"docs/connections/bitbucket-data-center",
|
||||||
|
"docs/connections/ado-cloud",
|
||||||
|
"docs/connections/ado-server",
|
||||||
"docs/connections/gitea",
|
"docs/connections/gitea",
|
||||||
"docs/connections/gerrit",
|
"docs/connections/gerrit",
|
||||||
"docs/connections/generic-git-host",
|
"docs/connections/generic-git-host",
|
||||||
|
|
|
||||||
141
docs/docs/connections/ado-cloud.mdx
Normal file
141
docs/docs/connections/ado-cloud.mdx
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
---
|
||||||
|
title: Linking code from Azure Devops Cloud
|
||||||
|
sidebarTitle: Azure Devops Cloud
|
||||||
|
icon: https://www.svgrepo.com/show/448307/azure-devops.svg
|
||||||
|
---
|
||||||
|
|
||||||
|
import AzureDevopsSchema from '/snippets/schemas/v3/azuredevops.schema.mdx'
|
||||||
|
|
||||||
|
If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Sync individual repos">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"repos": [
|
||||||
|
"organizationName/projectName/repoName",
|
||||||
|
"organizationName/projectName/repoName2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Sync all repos in a organization">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"orgs": [
|
||||||
|
"organizationName",
|
||||||
|
"organizationName2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Sync all repos in a project">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"projects": [
|
||||||
|
"organizationName/projectName",
|
||||||
|
"organizationName/projectName2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Exclude repos from syncing">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
// Include all repos in my-org...
|
||||||
|
"orgs": [
|
||||||
|
"my-org"
|
||||||
|
],
|
||||||
|
// ...except:
|
||||||
|
"exclude": {
|
||||||
|
// repos that are disabled
|
||||||
|
"disabled": true,
|
||||||
|
// repos that match these glob patterns
|
||||||
|
"repos": [
|
||||||
|
"reposToExclude*"
|
||||||
|
],
|
||||||
|
// projects that match these glob patterns
|
||||||
|
"projects": [
|
||||||
|
"projectstoExclude*"
|
||||||
|
]
|
||||||
|
// repos less than the defined min OR larger than the defined max
|
||||||
|
"size": {
|
||||||
|
// repos that are less than 1MB (in bytes)...
|
||||||
|
"min": 1048576,
|
||||||
|
// or repos greater than 100MB (in bytes)
|
||||||
|
"max": 104857600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Authenticating with Azure Devops Cloud
|
||||||
|
|
||||||
|
Azure Devops Cloud requires you to provide a PAT in order to index your repositories. To learn how to create PAT, check out the [Azure Devops docs](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows).
|
||||||
|
Sourcebot needs the `Read` access for the `Code` scope in order to find and clone your repos.
|
||||||
|
|
||||||
|
Next, provide the access token via the `token` property, either as an environment variable or a secret:
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Environment Variable">
|
||||||
|
|
||||||
|
1. Add the `token` property to your connection config:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"token": {
|
||||||
|
// note: this env var can be named anything. It
|
||||||
|
// doesn't need to be `ADO_TOKEN`.
|
||||||
|
"env": "ADO_TOKEN"
|
||||||
|
}
|
||||||
|
// .. rest of config ..
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Pass this environment variable each time you run Sourcebot:
|
||||||
|
```bash
|
||||||
|
docker run \
|
||||||
|
-e ADO_TOKEN=<PAT> \
|
||||||
|
/* additional args */ \
|
||||||
|
ghcr.io/sourcebot-dev/sourcebot:latest
|
||||||
|
```
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Secret">
|
||||||
|
<Note>Secrets are only supported when [authentication](/docs/configuration/auth/overview) is enabled.</Note>
|
||||||
|
|
||||||
|
1. Navigate to **Secrets** in settings and create a new secret with your PAT:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. Add the `token` property to your connection config:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"token": {
|
||||||
|
"secret": "mysecret"
|
||||||
|
}
|
||||||
|
// .. rest of config ..
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Schema reference
|
||||||
|
|
||||||
|
<Accordion title="Reference">
|
||||||
|
[schemas/v3/azuredevops.json](https://github.com/sourcebot-dev/sourcebot/blob/main/schemas/v3/azuredevops.json)
|
||||||
|
|
||||||
|
<AzureDevopsSchema />
|
||||||
|
|
||||||
|
</Accordion>
|
||||||
154
docs/docs/connections/ado-server.mdx
Normal file
154
docs/docs/connections/ado-server.mdx
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
---
|
||||||
|
title: Linking code from Azure Devops Server
|
||||||
|
sidebarTitle: Azure Devops Server
|
||||||
|
icon: https://www.svgrepo.com/show/448307/azure-devops.svg
|
||||||
|
---
|
||||||
|
|
||||||
|
import AzureDevopsSchema from '/snippets/schemas/v3/azuredevops.schema.mdx'
|
||||||
|
|
||||||
|
If you're not familiar with Sourcebot [connections](/docs/connections/overview), please read that overview first.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<AccordionGroup>
|
||||||
|
<Accordion title="Enable TFS path support">
|
||||||
|
This is required if you're using an older version of ADO Server which has `/tfs` in the repo paths.
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"useTfsPath": true
|
||||||
|
"repos": [
|
||||||
|
"organizationName/projectName/repoName",
|
||||||
|
"organizationName/projectName/repoName2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Sync individual repos">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"repos": [
|
||||||
|
"organizationName/projectName/repoName",
|
||||||
|
"organizationName/projectName/repoName2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Sync all repos in a collection">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"orgs": [
|
||||||
|
"collectionName",
|
||||||
|
"collectionName2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Sync all repos in a project">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"projects": [
|
||||||
|
"collectionName/projectName",
|
||||||
|
"collectionName/projectName2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
<Accordion title="Exclude repos from syncing">
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
// Include all repos in my-org...
|
||||||
|
"orgs": [
|
||||||
|
"my-org"
|
||||||
|
],
|
||||||
|
// ...except:
|
||||||
|
"exclude": {
|
||||||
|
// repos that are disabled
|
||||||
|
"disabled": true,
|
||||||
|
// repos that match these glob patterns
|
||||||
|
"repos": [
|
||||||
|
"reposToExclude*"
|
||||||
|
],
|
||||||
|
// projects that match these glob patterns
|
||||||
|
"projects": [
|
||||||
|
"projectstoExclude*"
|
||||||
|
]
|
||||||
|
// repos less than the defined min OR larger than the defined max
|
||||||
|
"size": {
|
||||||
|
// repos that are less than 1MB (in bytes)...
|
||||||
|
"min": 1048576,
|
||||||
|
// or repos greater than 100MB (in bytes)
|
||||||
|
"max": 104857600
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</Accordion>
|
||||||
|
</AccordionGroup>
|
||||||
|
|
||||||
|
## Authenticating with Azure Devops Server
|
||||||
|
|
||||||
|
Azure Devops Server requires you to provide a PAT in order to index your repositories. To learn how to create PAT, check out the [Azure Devops docs](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?view=azure-devops&tabs=Windows).
|
||||||
|
Sourcebot needs the `Read` access for the `Code` scope in order to find and clone your repos.
|
||||||
|
|
||||||
|
Next, provide the access token via the `token` property, either as an environment variable or a secret:
|
||||||
|
|
||||||
|
<Tabs>
|
||||||
|
<Tab title="Environment Variable">
|
||||||
|
|
||||||
|
1. Add the `token` property to your connection config:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"token": {
|
||||||
|
// note: this env var can be named anything. It
|
||||||
|
// doesn't need to be `ADO_TOKEN`.
|
||||||
|
"env": "ADO_TOKEN"
|
||||||
|
}
|
||||||
|
// .. rest of config ..
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Pass this environment variable each time you run Sourcebot:
|
||||||
|
```bash
|
||||||
|
docker run \
|
||||||
|
-e ADO_TOKEN=<PAT> \
|
||||||
|
/* additional args */ \
|
||||||
|
ghcr.io/sourcebot-dev/sourcebot:latest
|
||||||
|
```
|
||||||
|
</Tab>
|
||||||
|
|
||||||
|
<Tab title="Secret">
|
||||||
|
<Note>Secrets are only supported when [authentication](/docs/configuration/auth/overview) is enabled.</Note>
|
||||||
|
|
||||||
|
1. Navigate to **Secrets** in settings and create a new secret with your PAT:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
2. Add the `token` property to your connection config:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "azuredevops",
|
||||||
|
"token": {
|
||||||
|
"secret": "mysecret"
|
||||||
|
}
|
||||||
|
// .. rest of config ..
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</Tab>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
|
## Schema reference
|
||||||
|
|
||||||
|
<Accordion title="Reference">
|
||||||
|
[schemas/v3/azuredevops.json](https://github.com/sourcebot-dev/sourcebot/blob/main/schemas/v3/azuredevops.json)
|
||||||
|
|
||||||
|
<AzureDevopsSchema />
|
||||||
|
|
||||||
|
</Accordion>
|
||||||
|
|
@ -85,7 +85,6 @@ Next, provide the access token via the `token` property, either as an environmen
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Environment Variable">
|
<Tab title="Environment Variable">
|
||||||
<Note>Environment variables are only supported in a [declarative config](/docs/configuration/declarative-config) and cannot be used in the web UI.</Note>
|
|
||||||
|
|
||||||
1. Add the `token` property to your connection config:
|
1. Add the `token` property to your connection config:
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,6 @@ Next, provide the access token via the `token` property, either as an environmen
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Environment Variable">
|
<Tab title="Environment Variable">
|
||||||
<Note>Environment variables are only supported in a [declarative config](/docs/configuration/declarative-config) and cannot be used in the web UI.</Note>
|
|
||||||
|
|
||||||
1. Add the `token` property to your connection config:
|
1. Add the `token` property to your connection config:
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,6 @@ Next, provide the PAT via the `token` property, either as an environment variabl
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Environment Variable">
|
<Tab title="Environment Variable">
|
||||||
<Note>Environment variables are only supported in a [declarative config](/docs/configuration/declarative-config) and cannot be used in the web UI.</Note>
|
|
||||||
|
|
||||||
1. Add the `token` property to your connection config:
|
1. Add the `token` property to your connection config:
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
3
docs/images/ado.svg
Normal file
3
docs/images/ado.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="none">
|
||||||
|
<path fill="currentColor" d="M15 3.622v8.512L11.5 15l-5.425-1.975v1.958L3.004 10.97l8.951.7V4.005L15 3.622zm-2.984.428L6.994 1v2.001L2.382 4.356 1 6.13v4.029l1.978.873V5.869l9.038-1.818z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 272 B |
|
|
@ -1,6 +1,5 @@
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Environment Variable">
|
<Tab title="Environment Variable">
|
||||||
<Note>Environment variables are only supported in a [declarative config](/docs/configuration/declarative-config) and cannot be used in the web UI.</Note>
|
|
||||||
|
|
||||||
1. Add the `token` and `user` (username associated with the app password you created) properties to your connection config:
|
1. Add the `token` and `user` (username associated with the app password you created) properties to your connection config:
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="Environment Variable">
|
<Tab title="Environment Variable">
|
||||||
<Note>Environment variables are only supported in a [declarative config](/docs/configuration/declarative-config) and cannot be used in the web UI.</Note>
|
|
||||||
|
|
||||||
1. Add the `token` property to your connection config:
|
1. Add the `token` property to your connection config:
|
||||||
```json
|
```json
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,43 @@
|
||||||
<Card horizontal title="GitLab" icon="gitlab" href="/docs/connections/gitlab" />
|
<Card horizontal title="GitLab" icon="gitlab" href="/docs/connections/gitlab" />
|
||||||
<Card horizontal title="Bitbucket Cloud" icon="bitbucket" href="/docs/connections/bitbucket-cloud" />
|
<Card horizontal title="Bitbucket Cloud" icon="bitbucket" href="/docs/connections/bitbucket-cloud" />
|
||||||
<Card horizontal title="Bitbucket Data Center" icon="bitbucket" href="/docs/connections/bitbucket-data-center" />
|
<Card horizontal title="Bitbucket Data Center" icon="bitbucket" href="/docs/connections/bitbucket-data-center" />
|
||||||
|
{/* Mintlify has a bug where linking to a file for the logo renders it with a white background, so we have to embed it directly */}
|
||||||
|
<Card
|
||||||
|
horizontal
|
||||||
|
title="Azure Dev Ops Cloud"
|
||||||
|
href="/docs/connections/ado-cloud"
|
||||||
|
icon={
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
className="w-6 h-6 text-white"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M15 3.622v8.512L11.5 15l-5.425-1.975v1.958L3.004 10.97l8.951.7V4.005L15 3.622zm-2.984.428L6.994 1v2.001L2.382 4.356 1 6.13v4.029l1.978.873V5.869l9.038-1.818z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
horizontal
|
||||||
|
title="Azure Dev Ops Server"
|
||||||
|
href="/docs/connections/ado-server"
|
||||||
|
icon={
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
className="w-6 h-6 text-white"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M15 3.622v8.512L11.5 15l-5.425-1.975v1.958L3.004 10.97l8.951.7V4.005L15 3.622zm-2.984.428L6.994 1v2.001L2.382 4.356 1 6.13v4.029l1.978.873V5.869l9.038-1.818z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Card horizontal title="Gitea" icon="mug-tea" href="/docs/connections/gitea" />
|
<Card horizontal title="Gitea" icon="mug-tea" href="/docs/connections/gitea" />
|
||||||
<Card horizontal title="Gerrit" icon="crow" href="/docs/connections/gerrit" />
|
<Card horizontal title="Gerrit" icon="crow" href="/docs/connections/gerrit" />
|
||||||
<Card horizontal title="Other Git hosts" icon="git-alt" href="/docs/connections/generic-git-host" />
|
<Card horizontal title="Other Git hosts" icon="git-alt" href="/docs/connections/generic-git-host" />
|
||||||
|
|
|
||||||
206
docs/snippets/schemas/v3/azuredevops.schema.mdx
Normal file
206
docs/snippets/schemas/v3/azuredevops.schema.mdx
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
{/* THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY! */}
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AzureDevOpsConnectionConfig",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "azuredevops",
|
||||||
|
"description": "Azure DevOps Configuration"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"description": "A Personal Access Token (PAT).",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"secret": "SECRET_KEY"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"secret": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the secret that contains the token."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"secret"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"env": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"env"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url",
|
||||||
|
"default": "https://dev.azure.com",
|
||||||
|
"description": "The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.",
|
||||||
|
"examples": [
|
||||||
|
"https://dev.azure.com",
|
||||||
|
"https://azuredevops.example.com"
|
||||||
|
],
|
||||||
|
"pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$"
|
||||||
|
},
|
||||||
|
"deploymentType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"cloud",
|
||||||
|
"server"
|
||||||
|
],
|
||||||
|
"default": "cloud",
|
||||||
|
"description": "The type of Azure DevOps deployment"
|
||||||
|
},
|
||||||
|
"useTfsPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...)."
|
||||||
|
},
|
||||||
|
"orgs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project",
|
||||||
|
"my-collection/my-project"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project/my-repo"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'."
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Exclude disabled repositories from syncing."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of repositories to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of projects to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exclude repositories based on their size.",
|
||||||
|
"properties": {
|
||||||
|
"min": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "The revisions (branches, tags) that should be included when indexing. The default branch (HEAD) is always indexed. A maximum of 64 revisions can be indexed, with any additional revisions being ignored.",
|
||||||
|
"properties": {
|
||||||
|
"branches": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of branches to include when indexing. For a given repo, only the branches that exist on the repo's remote *and* match at least one of the provided `branches` will be indexed. The default branch (HEAD) is always indexed. Glob patterns are supported. A maximum of 64 branches can be indexed, with any additional branches being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"main",
|
||||||
|
"release/*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of tags to include when indexing. For a given repo, only the tags that exist on the repo's remote *and* match at least one of the provided `tags` will be indexed. Glob patterns are supported. A maximum of 64 tags can be indexed, with any additional tags being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"latest",
|
||||||
|
"v2.*.*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -869,6 +869,209 @@
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AzureDevOpsConnectionConfig",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "azuredevops",
|
||||||
|
"description": "Azure DevOps Configuration"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"description": "A Personal Access Token (PAT).",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"secret": "SECRET_KEY"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"secret": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the secret that contains the token."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"secret"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"env": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"env"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url",
|
||||||
|
"default": "https://dev.azure.com",
|
||||||
|
"description": "The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.",
|
||||||
|
"examples": [
|
||||||
|
"https://dev.azure.com",
|
||||||
|
"https://azuredevops.example.com"
|
||||||
|
],
|
||||||
|
"pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$"
|
||||||
|
},
|
||||||
|
"deploymentType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"cloud",
|
||||||
|
"server"
|
||||||
|
],
|
||||||
|
"default": "cloud",
|
||||||
|
"description": "The type of Azure DevOps deployment"
|
||||||
|
},
|
||||||
|
"useTfsPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...)."
|
||||||
|
},
|
||||||
|
"orgs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project",
|
||||||
|
"my-collection/my-project"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project/my-repo"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'."
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Exclude disabled repositories from syncing."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of repositories to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of projects to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exclude repositories based on their size.",
|
||||||
|
"properties": {
|
||||||
|
"min": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "The revisions (branches, tags) that should be included when indexing. The default branch (HEAD) is always indexed. A maximum of 64 revisions can be indexed, with any additional revisions being ignored.",
|
||||||
|
"properties": {
|
||||||
|
"branches": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of branches to include when indexing. For a given repo, only the branches that exist on the repo's remote *and* match at least one of the provided `branches` will be indexed. The default branch (HEAD) is always indexed. Glob patterns are supported. A maximum of 64 branches can be indexed, with any additional branches being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"main",
|
||||||
|
"release/*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of tags to include when indexing. For a given repo, only the tags that exist on the repo's remote *and* match at least one of the provided `tags` will be indexed. Glob patterns are supported. A maximum of 64 tags can be indexed, with any additional tags being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"latest",
|
||||||
|
"v2.*.*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
||||||
|
|
@ -1132,6 +1132,209 @@
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AzureDevOpsConnectionConfig",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "azuredevops",
|
||||||
|
"description": "Azure DevOps Configuration"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"description": "A Personal Access Token (PAT).",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"secret": "SECRET_KEY"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"secret": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the secret that contains the token."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"secret"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"env": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"env"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url",
|
||||||
|
"default": "https://dev.azure.com",
|
||||||
|
"description": "The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.",
|
||||||
|
"examples": [
|
||||||
|
"https://dev.azure.com",
|
||||||
|
"https://azuredevops.example.com"
|
||||||
|
],
|
||||||
|
"pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$"
|
||||||
|
},
|
||||||
|
"deploymentType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"cloud",
|
||||||
|
"server"
|
||||||
|
],
|
||||||
|
"default": "cloud",
|
||||||
|
"description": "The type of Azure DevOps deployment"
|
||||||
|
},
|
||||||
|
"useTfsPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...)."
|
||||||
|
},
|
||||||
|
"orgs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project",
|
||||||
|
"my-collection/my-project"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project/my-repo"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'."
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Exclude disabled repositories from syncing."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of repositories to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of projects to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exclude repositories based on their size.",
|
||||||
|
"properties": {
|
||||||
|
"min": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "The revisions (branches, tags) that should be included when indexing. The default branch (HEAD) is always indexed. A maximum of 64 revisions can be indexed, with any additional revisions being ignored.",
|
||||||
|
"properties": {
|
||||||
|
"branches": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of branches to include when indexing. For a given repo, only the branches that exist on the repo's remote *and* match at least one of the provided `branches` will be indexed. The default branch (HEAD) is always indexed. Glob patterns are supported. A maximum of 64 branches can be indexed, with any additional branches being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"main",
|
||||||
|
"release/*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of tags to include when indexing. For a given repo, only the tags that exist on the repo's remote *and* match at least one of the provided `tags` will be indexed. Glob patterns are supported. A maximum of 64 tags can be indexed, with any additional tags being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"latest",
|
||||||
|
"v2.*.*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
"@t3-oss/env-core": "^0.12.0",
|
"@t3-oss/env-core": "^0.12.0",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"argparse": "^2.0.1",
|
"argparse": "^2.0.1",
|
||||||
|
"azure-devops-node-api": "^15.1.1",
|
||||||
"bullmq": "^5.34.10",
|
"bullmq": "^5.34.10",
|
||||||
"cross-fetch": "^4.0.0",
|
"cross-fetch": "^4.0.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
|
|
||||||
348
packages/backend/src/azuredevops.ts
Normal file
348
packages/backend/src/azuredevops.ts
Normal file
|
|
@ -0,0 +1,348 @@
|
||||||
|
import { AzureDevOpsConnectionConfig } from "@sourcebot/schemas/v3/azuredevops.type";
|
||||||
|
import { createLogger } from "@sourcebot/logger";
|
||||||
|
import { getTokenFromConfig, measure, fetchWithRetry } from "./utils.js";
|
||||||
|
import micromatch from "micromatch";
|
||||||
|
import { PrismaClient } from "@sourcebot/db";
|
||||||
|
import { BackendException, BackendError } from "@sourcebot/error";
|
||||||
|
import { processPromiseResults, throwIfAnyFailed } from "./connectionUtils.js";
|
||||||
|
import * as Sentry from "@sentry/node";
|
||||||
|
import * as azdev from "azure-devops-node-api";
|
||||||
|
import { GitRepository } from "azure-devops-node-api/interfaces/GitInterfaces.js";
|
||||||
|
|
||||||
|
const logger = createLogger('azuredevops');
|
||||||
|
const AZUREDEVOPS_CLOUD_HOSTNAME = "dev.azure.com";
|
||||||
|
|
||||||
|
|
||||||
|
function buildOrgUrl(baseUrl: string, org: string, useTfsPath: boolean): string {
|
||||||
|
const tfsSegment = useTfsPath ? '/tfs' : '';
|
||||||
|
return `${baseUrl}${tfsSegment}/${org}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAzureDevOpsConnection(
|
||||||
|
orgUrl: string,
|
||||||
|
token: string,
|
||||||
|
): azdev.WebApi {
|
||||||
|
const authHandler = azdev.getPersonalAccessTokenHandler(token);
|
||||||
|
return new azdev.WebApi(orgUrl, authHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getAzureDevOpsReposFromConfig = async (
|
||||||
|
config: AzureDevOpsConnectionConfig,
|
||||||
|
orgId: number,
|
||||||
|
db: PrismaClient
|
||||||
|
) => {
|
||||||
|
const baseUrl = config.url || `https://${AZUREDEVOPS_CLOUD_HOSTNAME}`;
|
||||||
|
|
||||||
|
const token = config.token ?
|
||||||
|
await getTokenFromConfig(config.token, orgId, db, logger) :
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
const e = new BackendException(BackendError.CONNECTION_SYNC_INVALID_TOKEN, {
|
||||||
|
message: 'Azure DevOps requires a Personal Access Token',
|
||||||
|
});
|
||||||
|
Sentry.captureException(e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useTfsPath = config.useTfsPath || false;
|
||||||
|
let allRepos: GitRepository[] = [];
|
||||||
|
let notFound: {
|
||||||
|
users: string[],
|
||||||
|
orgs: string[],
|
||||||
|
repos: string[],
|
||||||
|
} = {
|
||||||
|
users: [],
|
||||||
|
orgs: [],
|
||||||
|
repos: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (config.orgs) {
|
||||||
|
const { validRepos, notFoundOrgs } = await getReposForOrganizations(
|
||||||
|
config.orgs,
|
||||||
|
baseUrl,
|
||||||
|
token,
|
||||||
|
useTfsPath
|
||||||
|
);
|
||||||
|
allRepos = allRepos.concat(validRepos);
|
||||||
|
notFound.orgs = notFoundOrgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.projects) {
|
||||||
|
const { validRepos, notFoundProjects } = await getReposForProjects(
|
||||||
|
config.projects,
|
||||||
|
baseUrl,
|
||||||
|
token,
|
||||||
|
useTfsPath
|
||||||
|
);
|
||||||
|
allRepos = allRepos.concat(validRepos);
|
||||||
|
notFound.repos = notFound.repos.concat(notFoundProjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.repos) {
|
||||||
|
const { validRepos, notFoundRepos } = await getRepos(
|
||||||
|
config.repos,
|
||||||
|
baseUrl,
|
||||||
|
token,
|
||||||
|
useTfsPath
|
||||||
|
);
|
||||||
|
allRepos = allRepos.concat(validRepos);
|
||||||
|
notFound.repos = notFound.repos.concat(notFoundRepos);
|
||||||
|
}
|
||||||
|
|
||||||
|
let repos = allRepos
|
||||||
|
.filter((repo) => {
|
||||||
|
const isExcluded = shouldExcludeRepo({
|
||||||
|
repo,
|
||||||
|
exclude: config.exclude,
|
||||||
|
});
|
||||||
|
|
||||||
|
return !isExcluded;
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.debug(`Found ${repos.length} total repositories.`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
validRepos: repos,
|
||||||
|
notFound,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shouldExcludeRepo = ({
|
||||||
|
repo,
|
||||||
|
exclude
|
||||||
|
}: {
|
||||||
|
repo: GitRepository,
|
||||||
|
exclude?: AzureDevOpsConnectionConfig['exclude']
|
||||||
|
}) => {
|
||||||
|
let reason = '';
|
||||||
|
const repoName = `${repo.project!.name}/${repo.name}`;
|
||||||
|
|
||||||
|
const shouldExclude = (() => {
|
||||||
|
if (!repo.remoteUrl) {
|
||||||
|
reason = 'remoteUrl is undefined';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!exclude?.disabled && repo.isDisabled) {
|
||||||
|
reason = `\`exclude.disabled\` is true`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exclude?.repos) {
|
||||||
|
if (micromatch.isMatch(repoName, exclude.repos)) {
|
||||||
|
reason = `\`exclude.repos\` contains ${repoName}`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exclude?.projects) {
|
||||||
|
if (micromatch.isMatch(repo.project!.name!, exclude.projects)) {
|
||||||
|
reason = `\`exclude.projects\` contains ${repo.project!.name}`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoSizeInBytes = repo.size || 0;
|
||||||
|
if (exclude?.size && repoSizeInBytes) {
|
||||||
|
const min = exclude.size.min;
|
||||||
|
const max = exclude.size.max;
|
||||||
|
|
||||||
|
if (min && repoSizeInBytes < min) {
|
||||||
|
reason = `repo is less than \`exclude.size.min\`=${min} bytes.`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max && repoSizeInBytes > max) {
|
||||||
|
reason = `repo is greater than \`exclude.size.max\`=${max} bytes.`;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (shouldExclude) {
|
||||||
|
logger.debug(`Excluding repo ${repoName}. Reason: ${reason}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getReposForOrganizations(
|
||||||
|
organizations: string[],
|
||||||
|
baseUrl: string,
|
||||||
|
token: string,
|
||||||
|
useTfsPath: boolean
|
||||||
|
) {
|
||||||
|
const results = await Promise.allSettled(organizations.map(async (org) => {
|
||||||
|
try {
|
||||||
|
logger.debug(`Fetching repositories for organization ${org}...`);
|
||||||
|
|
||||||
|
const { durationMs, data } = await measure(async () => {
|
||||||
|
const fetchFn = async () => {
|
||||||
|
const orgUrl = buildOrgUrl(baseUrl, org, useTfsPath);
|
||||||
|
const connection = createAzureDevOpsConnection(orgUrl, token); // useTfsPath already handled in orgUrl
|
||||||
|
|
||||||
|
const coreApi = await connection.getCoreApi();
|
||||||
|
const gitApi = await connection.getGitApi();
|
||||||
|
|
||||||
|
const projects = await coreApi.getProjects();
|
||||||
|
const allRepos: GitRepository[] = [];
|
||||||
|
for (const project of projects) {
|
||||||
|
if (!project.id) {
|
||||||
|
logger.warn(`Encountered project in org ${org} with no id: ${project.name}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const repos = await gitApi.getRepositories(project.id);
|
||||||
|
allRepos.push(...repos);
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn(`Failed to fetch repositories for project ${project.name}: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRepos;
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetchWithRetry(fetchFn, `organization ${org}`, logger);
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.debug(`Found ${data.length} repositories in organization ${org} in ${durationMs}ms.`);
|
||||||
|
return {
|
||||||
|
type: 'valid' as const,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
Sentry.captureException(error);
|
||||||
|
logger.error(`Failed to fetch repositories for organization ${org}.`, error);
|
||||||
|
|
||||||
|
// Check if it's a 404-like error (organization not found)
|
||||||
|
if (error && typeof error === 'object' && 'statusCode' in error && error.statusCode === 404) {
|
||||||
|
logger.error(`Organization ${org} not found or no access`);
|
||||||
|
return {
|
||||||
|
type: 'notFound' as const,
|
||||||
|
value: org
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
throwIfAnyFailed(results);
|
||||||
|
const { validItems: validRepos, notFoundItems: notFoundOrgs } = processPromiseResults<GitRepository>(results);
|
||||||
|
|
||||||
|
return {
|
||||||
|
validRepos,
|
||||||
|
notFoundOrgs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getReposForProjects(
|
||||||
|
projects: string[],
|
||||||
|
baseUrl: string,
|
||||||
|
token: string,
|
||||||
|
useTfsPath: boolean
|
||||||
|
) {
|
||||||
|
const results = await Promise.allSettled(projects.map(async (project) => {
|
||||||
|
try {
|
||||||
|
const [org, projectName] = project.split('/');
|
||||||
|
logger.debug(`Fetching repositories for project ${project}...`);
|
||||||
|
|
||||||
|
const { durationMs, data } = await measure(async () => {
|
||||||
|
const fetchFn = async () => {
|
||||||
|
const orgUrl = buildOrgUrl(baseUrl, org, useTfsPath);
|
||||||
|
const connection = createAzureDevOpsConnection(orgUrl, token);
|
||||||
|
const gitApi = await connection.getGitApi();
|
||||||
|
|
||||||
|
const repos = await gitApi.getRepositories(projectName);
|
||||||
|
return repos;
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetchWithRetry(fetchFn, `project ${project}`, logger);
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.debug(`Found ${data.length} repositories in project ${project} in ${durationMs}ms.`);
|
||||||
|
return {
|
||||||
|
type: 'valid' as const,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
Sentry.captureException(error);
|
||||||
|
logger.error(`Failed to fetch repositories for project ${project}.`, error);
|
||||||
|
|
||||||
|
if (error && typeof error === 'object' && 'statusCode' in error && error.statusCode === 404) {
|
||||||
|
logger.error(`Project ${project} not found or no access`);
|
||||||
|
return {
|
||||||
|
type: 'notFound' as const,
|
||||||
|
value: project
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
throwIfAnyFailed(results);
|
||||||
|
const { validItems: validRepos, notFoundItems: notFoundProjects } = processPromiseResults<GitRepository>(results);
|
||||||
|
|
||||||
|
return {
|
||||||
|
validRepos,
|
||||||
|
notFoundProjects,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getRepos(
|
||||||
|
repoList: string[],
|
||||||
|
baseUrl: string,
|
||||||
|
token: string,
|
||||||
|
useTfsPath: boolean
|
||||||
|
) {
|
||||||
|
const results = await Promise.allSettled(repoList.map(async (repo) => {
|
||||||
|
try {
|
||||||
|
const [org, projectName, repoName] = repo.split('/');
|
||||||
|
logger.info(`Fetching repository info for ${repo}...`);
|
||||||
|
|
||||||
|
const { durationMs, data: result } = await measure(async () => {
|
||||||
|
const fetchFn = async () => {
|
||||||
|
const orgUrl = buildOrgUrl(baseUrl, org, useTfsPath);
|
||||||
|
const connection = createAzureDevOpsConnection(orgUrl, token);
|
||||||
|
const gitApi = await connection.getGitApi();
|
||||||
|
|
||||||
|
const repo = await gitApi.getRepository(repoName, projectName);
|
||||||
|
return repo;
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetchWithRetry(fetchFn, repo, logger);
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Found info for repository ${repo} in ${durationMs}ms`);
|
||||||
|
return {
|
||||||
|
type: 'valid' as const,
|
||||||
|
data: [result]
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
Sentry.captureException(error);
|
||||||
|
logger.error(`Failed to fetch repository ${repo}.`, error);
|
||||||
|
|
||||||
|
if (error && typeof error === 'object' && 'statusCode' in error && error.statusCode === 404) {
|
||||||
|
logger.error(`Repository ${repo} not found or no access`);
|
||||||
|
return {
|
||||||
|
type: 'notFound' as const,
|
||||||
|
value: repo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
throwIfAnyFailed(results);
|
||||||
|
const { validItems: validRepos, notFoundItems: notFoundRepos } = processPromiseResults<GitRepository>(results);
|
||||||
|
|
||||||
|
return {
|
||||||
|
validRepos,
|
||||||
|
notFoundRepos,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ import { Settings } from "./types.js";
|
||||||
import { ConnectionConfig } from "@sourcebot/schemas/v3/connection.type";
|
import { ConnectionConfig } from "@sourcebot/schemas/v3/connection.type";
|
||||||
import { createLogger } from "@sourcebot/logger";
|
import { createLogger } from "@sourcebot/logger";
|
||||||
import { Redis } from 'ioredis';
|
import { Redis } from 'ioredis';
|
||||||
import { RepoData, compileGithubConfig, compileGitlabConfig, compileGiteaConfig, compileGerritConfig, compileBitbucketConfig, compileGenericGitHostConfig } from "./repoCompileUtils.js";
|
import { RepoData, compileGithubConfig, compileGitlabConfig, compileGiteaConfig, compileGerritConfig, compileBitbucketConfig, compileAzureDevOpsConfig, compileGenericGitHostConfig } from "./repoCompileUtils.js";
|
||||||
import { BackendError, BackendException } from "@sourcebot/error";
|
import { BackendError, BackendException } from "@sourcebot/error";
|
||||||
import { captureEvent } from "./posthog.js";
|
import { captureEvent } from "./posthog.js";
|
||||||
import { env } from "./env.js";
|
import { env } from "./env.js";
|
||||||
|
|
@ -177,6 +177,9 @@ export class ConnectionManager implements IConnectionManager {
|
||||||
case 'bitbucket': {
|
case 'bitbucket': {
|
||||||
return await compileBitbucketConfig(config, job.data.connectionId, orgId, this.db);
|
return await compileBitbucketConfig(config, job.data.connectionId, orgId, this.db);
|
||||||
}
|
}
|
||||||
|
case 'azuredevops': {
|
||||||
|
return await compileAzureDevOpsConfig(config, job.data.connectionId, orgId, this.db, abortController);
|
||||||
|
}
|
||||||
case 'git': {
|
case 'git': {
|
||||||
return await compileGenericGitHostConfig(config, job.data.connectionId, orgId);
|
return await compileGenericGitHostConfig(config, job.data.connectionId, orgId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,15 @@ import { getGitLabReposFromConfig } from "./gitlab.js";
|
||||||
import { getGiteaReposFromConfig } from "./gitea.js";
|
import { getGiteaReposFromConfig } from "./gitea.js";
|
||||||
import { getGerritReposFromConfig } from "./gerrit.js";
|
import { getGerritReposFromConfig } from "./gerrit.js";
|
||||||
import { BitbucketRepository, getBitbucketReposFromConfig } from "./bitbucket.js";
|
import { BitbucketRepository, getBitbucketReposFromConfig } from "./bitbucket.js";
|
||||||
|
import { getAzureDevOpsReposFromConfig } from "./azuredevops.js";
|
||||||
import { SchemaRestRepository as BitbucketServerRepository } from "@coderabbitai/bitbucket/server/openapi";
|
import { SchemaRestRepository as BitbucketServerRepository } from "@coderabbitai/bitbucket/server/openapi";
|
||||||
import { SchemaRepository as BitbucketCloudRepository } from "@coderabbitai/bitbucket/cloud/openapi";
|
import { SchemaRepository as BitbucketCloudRepository } from "@coderabbitai/bitbucket/cloud/openapi";
|
||||||
import { Prisma, PrismaClient } from '@sourcebot/db';
|
import { Prisma, PrismaClient } from '@sourcebot/db';
|
||||||
import { WithRequired } from "./types.js"
|
import { WithRequired } from "./types.js"
|
||||||
import { marshalBool } from "./utils.js";
|
import { marshalBool } from "./utils.js";
|
||||||
import { createLogger } from '@sourcebot/logger';
|
import { createLogger } from '@sourcebot/logger';
|
||||||
import { BitbucketConnectionConfig, GerritConnectionConfig, GiteaConnectionConfig, GitlabConnectionConfig, GenericGitHostConnectionConfig } from '@sourcebot/schemas/v3/connection.type';
|
import { BitbucketConnectionConfig, GerritConnectionConfig, GiteaConnectionConfig, GitlabConnectionConfig, GenericGitHostConnectionConfig, AzureDevOpsConnectionConfig } from '@sourcebot/schemas/v3/connection.type';
|
||||||
|
import { ProjectVisibility } from "azure-devops-node-api/interfaces/CoreInterfaces.js";
|
||||||
import { RepoMetadata } from './types.js';
|
import { RepoMetadata } from './types.js';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { glob } from 'glob';
|
import { glob } from 'glob';
|
||||||
|
|
@ -544,6 +546,87 @@ export const compileGenericGitHostConfig_file = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const compileAzureDevOpsConfig = async (
|
||||||
|
config: AzureDevOpsConnectionConfig,
|
||||||
|
connectionId: number,
|
||||||
|
orgId: number,
|
||||||
|
db: PrismaClient,
|
||||||
|
abortController: AbortController) => {
|
||||||
|
|
||||||
|
const azureDevOpsReposResult = await getAzureDevOpsReposFromConfig(config, orgId, db);
|
||||||
|
const azureDevOpsRepos = azureDevOpsReposResult.validRepos;
|
||||||
|
const notFound = azureDevOpsReposResult.notFound;
|
||||||
|
|
||||||
|
const hostUrl = config.url ?? 'https://dev.azure.com';
|
||||||
|
const repoNameRoot = new URL(hostUrl)
|
||||||
|
.toString()
|
||||||
|
.replace(/^https?:\/\//, '');
|
||||||
|
|
||||||
|
const repos = azureDevOpsRepos.map((repo) => {
|
||||||
|
if (!repo.project) {
|
||||||
|
throw new Error(`No project found for repository ${repo.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const repoDisplayName = `${repo.project.name}/${repo.name}`;
|
||||||
|
const repoName = path.join(repoNameRoot, repoDisplayName);
|
||||||
|
|
||||||
|
if (!repo.remoteUrl) {
|
||||||
|
throw new Error(`No remoteUrl found for repository ${repoDisplayName}`);
|
||||||
|
}
|
||||||
|
if (!repo.id) {
|
||||||
|
throw new Error(`No id found for repository ${repoDisplayName}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct web URL for the repository
|
||||||
|
const webUrl = repo.webUrl || `${hostUrl}/${repo.project.name}/_git/${repo.name}`;
|
||||||
|
|
||||||
|
logger.debug(`Found Azure DevOps repo ${repoDisplayName} with webUrl: ${webUrl}`);
|
||||||
|
|
||||||
|
const record: RepoData = {
|
||||||
|
external_id: repo.id.toString(),
|
||||||
|
external_codeHostType: 'azuredevops',
|
||||||
|
external_codeHostUrl: hostUrl,
|
||||||
|
cloneUrl: webUrl,
|
||||||
|
webUrl: webUrl,
|
||||||
|
name: repoName,
|
||||||
|
displayName: repoDisplayName,
|
||||||
|
imageUrl: null,
|
||||||
|
isFork: !!repo.isFork,
|
||||||
|
isArchived: false,
|
||||||
|
org: {
|
||||||
|
connect: {
|
||||||
|
id: orgId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
connections: {
|
||||||
|
create: {
|
||||||
|
connectionId: connectionId,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
gitConfig: {
|
||||||
|
'zoekt.web-url-type': 'azuredevops',
|
||||||
|
'zoekt.web-url': webUrl,
|
||||||
|
'zoekt.name': repoName,
|
||||||
|
'zoekt.archived': marshalBool(false),
|
||||||
|
'zoekt.fork': marshalBool(!!repo.isFork),
|
||||||
|
'zoekt.public': marshalBool(repo.project.visibility === ProjectVisibility.Public),
|
||||||
|
'zoekt.display-name': repoDisplayName,
|
||||||
|
},
|
||||||
|
branches: config.revisions?.branches ?? undefined,
|
||||||
|
tags: config.revisions?.tags ?? undefined,
|
||||||
|
} satisfies RepoMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
return record;
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
repoData: repos,
|
||||||
|
notFound,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const compileGenericGitHostConfig_url = async (
|
export const compileGenericGitHostConfig_url = async (
|
||||||
config: GenericGitHostConnectionConfig,
|
config: GenericGitHostConnectionConfig,
|
||||||
orgId: number,
|
orgId: number,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { Job, Queue, Worker } from 'bullmq';
|
||||||
import { Redis } from 'ioredis';
|
import { Redis } from 'ioredis';
|
||||||
import { createLogger } from "@sourcebot/logger";
|
import { createLogger } from "@sourcebot/logger";
|
||||||
import { Connection, PrismaClient, Repo, RepoToConnection, RepoIndexingStatus, StripeSubscriptionStatus } from "@sourcebot/db";
|
import { Connection, PrismaClient, Repo, RepoToConnection, RepoIndexingStatus, StripeSubscriptionStatus } from "@sourcebot/db";
|
||||||
import { GithubConnectionConfig, GitlabConnectionConfig, GiteaConnectionConfig, BitbucketConnectionConfig } from '@sourcebot/schemas/v3/connection.type';
|
import { GithubConnectionConfig, GitlabConnectionConfig, GiteaConnectionConfig, BitbucketConnectionConfig, AzureDevOpsConnectionConfig } from '@sourcebot/schemas/v3/connection.type';
|
||||||
import { AppContext, Settings, repoMetadataSchema } from "./types.js";
|
import { AppContext, Settings, repoMetadataSchema } from "./types.js";
|
||||||
import { getRepoPath, getTokenFromConfig, measure, getShardPrefix } from "./utils.js";
|
import { getRepoPath, getTokenFromConfig, measure, getShardPrefix } from "./utils.js";
|
||||||
import { cloneRepository, fetchRepository, unsetGitConfig, upsertGitConfig } from "./git.js";
|
import { cloneRepository, fetchRepository, unsetGitConfig, upsertGitConfig } from "./git.js";
|
||||||
|
|
@ -186,9 +186,7 @@ export class RepoManager implements IRepoManager {
|
||||||
password: token,
|
password: token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (connection.connectionType === 'gitlab') {
|
||||||
|
|
||||||
else if (connection.connectionType === 'gitlab') {
|
|
||||||
const config = connection.config as unknown as GitlabConnectionConfig;
|
const config = connection.config as unknown as GitlabConnectionConfig;
|
||||||
if (config.token) {
|
if (config.token) {
|
||||||
const token = await getTokenFromConfig(config.token, connection.orgId, db, logger);
|
const token = await getTokenFromConfig(config.token, connection.orgId, db, logger);
|
||||||
|
|
@ -197,9 +195,7 @@ export class RepoManager implements IRepoManager {
|
||||||
password: token,
|
password: token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (connection.connectionType === 'gitea') {
|
||||||
|
|
||||||
else if (connection.connectionType === 'gitea') {
|
|
||||||
const config = connection.config as unknown as GiteaConnectionConfig;
|
const config = connection.config as unknown as GiteaConnectionConfig;
|
||||||
if (config.token) {
|
if (config.token) {
|
||||||
const token = await getTokenFromConfig(config.token, connection.orgId, db, logger);
|
const token = await getTokenFromConfig(config.token, connection.orgId, db, logger);
|
||||||
|
|
@ -207,9 +203,7 @@ export class RepoManager implements IRepoManager {
|
||||||
password: token,
|
password: token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if (connection.connectionType === 'bitbucket') {
|
||||||
|
|
||||||
else if (connection.connectionType === 'bitbucket') {
|
|
||||||
const config = connection.config as unknown as BitbucketConnectionConfig;
|
const config = connection.config as unknown as BitbucketConnectionConfig;
|
||||||
if (config.token) {
|
if (config.token) {
|
||||||
const token = await getTokenFromConfig(config.token, connection.orgId, db, logger);
|
const token = await getTokenFromConfig(config.token, connection.orgId, db, logger);
|
||||||
|
|
@ -219,6 +213,14 @@ export class RepoManager implements IRepoManager {
|
||||||
password: token,
|
password: token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (connection.connectionType === 'azuredevops') {
|
||||||
|
const config = connection.config as unknown as AzureDevOpsConnectionConfig;
|
||||||
|
if (config.token) {
|
||||||
|
const token = await getTokenFromConfig(config.token, connection.orgId, db, logger);
|
||||||
|
return {
|
||||||
|
password: token,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,11 @@ enum ChatVisibility {
|
||||||
|
|
||||||
model Repo {
|
model Repo {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String // Full repo name, including the vcs hostname (ex. github.com/sourcebot-dev/sourcebot)
|
||||||
displayName String?
|
displayName String? // Display name of the repo for UI (ex. sourcebot-dev/sourcebot)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
/// When the repo was last indexed successfully.
|
/// When the repo was last indexed successfully.
|
||||||
indexedAt DateTime?
|
indexedAt DateTime?
|
||||||
isFork Boolean
|
isFork Boolean
|
||||||
|
|
|
||||||
205
packages/schemas/src/v3/azuredevops.schema.ts
Normal file
205
packages/schemas/src/v3/azuredevops.schema.ts
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY!
|
||||||
|
const schema = {
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AzureDevOpsConnectionConfig",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "azuredevops",
|
||||||
|
"description": "Azure DevOps Configuration"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"description": "A Personal Access Token (PAT).",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"secret": "SECRET_KEY"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"secret": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the secret that contains the token."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"secret"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"env": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"env"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url",
|
||||||
|
"default": "https://dev.azure.com",
|
||||||
|
"description": "The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.",
|
||||||
|
"examples": [
|
||||||
|
"https://dev.azure.com",
|
||||||
|
"https://azuredevops.example.com"
|
||||||
|
],
|
||||||
|
"pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$"
|
||||||
|
},
|
||||||
|
"deploymentType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"cloud",
|
||||||
|
"server"
|
||||||
|
],
|
||||||
|
"default": "cloud",
|
||||||
|
"description": "The type of Azure DevOps deployment"
|
||||||
|
},
|
||||||
|
"useTfsPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...)."
|
||||||
|
},
|
||||||
|
"orgs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project",
|
||||||
|
"my-collection/my-project"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project/my-repo"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'."
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Exclude disabled repositories from syncing."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of repositories to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of projects to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exclude repositories based on their size.",
|
||||||
|
"properties": {
|
||||||
|
"min": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "The revisions (branches, tags) that should be included when indexing. The default branch (HEAD) is always indexed. A maximum of 64 revisions can be indexed, with any additional revisions being ignored.",
|
||||||
|
"properties": {
|
||||||
|
"branches": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of branches to include when indexing. For a given repo, only the branches that exist on the repo's remote *and* match at least one of the provided `branches` will be indexed. The default branch (HEAD) is always indexed. Glob patterns are supported. A maximum of 64 branches can be indexed, with any additional branches being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"main",
|
||||||
|
"release/*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of tags to include when indexing. For a given repo, only the tags that exist on the repo's remote *and* match at least one of the provided `tags` will be indexed. Glob patterns are supported. A maximum of 64 tags can be indexed, with any additional tags being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"latest",
|
||||||
|
"v2.*.*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
} as const;
|
||||||
|
export { schema as azuredevopsSchema };
|
||||||
89
packages/schemas/src/v3/azuredevops.type.ts
Normal file
89
packages/schemas/src/v3/azuredevops.type.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// THIS IS A AUTO-GENERATED FILE. DO NOT MODIFY MANUALLY!
|
||||||
|
|
||||||
|
export interface AzureDevOpsConnectionConfig {
|
||||||
|
/**
|
||||||
|
* Azure DevOps Configuration
|
||||||
|
*/
|
||||||
|
type: "azuredevops";
|
||||||
|
/**
|
||||||
|
* A Personal Access Token (PAT).
|
||||||
|
*/
|
||||||
|
token:
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The name of the secret that contains the token.
|
||||||
|
*/
|
||||||
|
secret: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The name of the environment variable that contains the token. Only supported in declarative connection configs.
|
||||||
|
*/
|
||||||
|
env: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.
|
||||||
|
*/
|
||||||
|
url?: string;
|
||||||
|
/**
|
||||||
|
* The type of Azure DevOps deployment
|
||||||
|
*/
|
||||||
|
deploymentType?: "cloud" | "server";
|
||||||
|
/**
|
||||||
|
* Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...).
|
||||||
|
*/
|
||||||
|
useTfsPath?: boolean;
|
||||||
|
/**
|
||||||
|
* List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property.
|
||||||
|
*/
|
||||||
|
orgs?: string[];
|
||||||
|
/**
|
||||||
|
* List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server.
|
||||||
|
*/
|
||||||
|
projects?: string[];
|
||||||
|
/**
|
||||||
|
* List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'.
|
||||||
|
*/
|
||||||
|
repos?: string[];
|
||||||
|
exclude?: {
|
||||||
|
/**
|
||||||
|
* Exclude disabled repositories from syncing.
|
||||||
|
*/
|
||||||
|
disabled?: boolean;
|
||||||
|
/**
|
||||||
|
* List of repositories to exclude from syncing. Glob patterns are supported.
|
||||||
|
*/
|
||||||
|
repos?: string[];
|
||||||
|
/**
|
||||||
|
* List of projects to exclude from syncing. Glob patterns are supported.
|
||||||
|
*/
|
||||||
|
projects?: string[];
|
||||||
|
/**
|
||||||
|
* Exclude repositories based on their size.
|
||||||
|
*/
|
||||||
|
size?: {
|
||||||
|
/**
|
||||||
|
* Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing.
|
||||||
|
*/
|
||||||
|
min?: number;
|
||||||
|
/**
|
||||||
|
* Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing.
|
||||||
|
*/
|
||||||
|
max?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
revisions?: GitRevisions;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* The revisions (branches, tags) that should be included when indexing. The default branch (HEAD) is always indexed. A maximum of 64 revisions can be indexed, with any additional revisions being ignored.
|
||||||
|
*/
|
||||||
|
export interface GitRevisions {
|
||||||
|
/**
|
||||||
|
* List of branches to include when indexing. For a given repo, only the branches that exist on the repo's remote *and* match at least one of the provided `branches` will be indexed. The default branch (HEAD) is always indexed. Glob patterns are supported. A maximum of 64 branches can be indexed, with any additional branches being ignored.
|
||||||
|
*/
|
||||||
|
branches?: string[];
|
||||||
|
/**
|
||||||
|
* List of tags to include when indexing. For a given repo, only the tags that exist on the repo's remote *and* match at least one of the provided `tags` will be indexed. Glob patterns are supported. A maximum of 64 tags can be indexed, with any additional tags being ignored.
|
||||||
|
*/
|
||||||
|
tags?: string[];
|
||||||
|
}
|
||||||
|
|
@ -868,6 +868,209 @@ const schema = {
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AzureDevOpsConnectionConfig",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "azuredevops",
|
||||||
|
"description": "Azure DevOps Configuration"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"description": "A Personal Access Token (PAT).",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"secret": "SECRET_KEY"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"secret": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the secret that contains the token."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"secret"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"env": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"env"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url",
|
||||||
|
"default": "https://dev.azure.com",
|
||||||
|
"description": "The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.",
|
||||||
|
"examples": [
|
||||||
|
"https://dev.azure.com",
|
||||||
|
"https://azuredevops.example.com"
|
||||||
|
],
|
||||||
|
"pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$"
|
||||||
|
},
|
||||||
|
"deploymentType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"cloud",
|
||||||
|
"server"
|
||||||
|
],
|
||||||
|
"default": "cloud",
|
||||||
|
"description": "The type of Azure DevOps deployment"
|
||||||
|
},
|
||||||
|
"useTfsPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...)."
|
||||||
|
},
|
||||||
|
"orgs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project",
|
||||||
|
"my-collection/my-project"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project/my-repo"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'."
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Exclude disabled repositories from syncing."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of repositories to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of projects to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exclude repositories based on their size.",
|
||||||
|
"properties": {
|
||||||
|
"min": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "The revisions (branches, tags) that should be included when indexing. The default branch (HEAD) is always indexed. A maximum of 64 revisions can be indexed, with any additional revisions being ignored.",
|
||||||
|
"properties": {
|
||||||
|
"branches": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of branches to include when indexing. For a given repo, only the branches that exist on the repo's remote *and* match at least one of the provided `branches` will be indexed. The default branch (HEAD) is always indexed. Glob patterns are supported. A maximum of 64 branches can be indexed, with any additional branches being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"main",
|
||||||
|
"release/*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of tags to include when indexing. For a given repo, only the tags that exist on the repo's remote *and* match at least one of the provided `tags` will be indexed. Glob patterns are supported. A maximum of 64 tags can be indexed, with any additional tags being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"latest",
|
||||||
|
"v2.*.*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ export type ConnectionConfig =
|
||||||
| GiteaConnectionConfig
|
| GiteaConnectionConfig
|
||||||
| GerritConnectionConfig
|
| GerritConnectionConfig
|
||||||
| BitbucketConnectionConfig
|
| BitbucketConnectionConfig
|
||||||
|
| AzureDevOpsConnectionConfig
|
||||||
| GenericGitHostConnectionConfig;
|
| GenericGitHostConnectionConfig;
|
||||||
|
|
||||||
export interface GithubConnectionConfig {
|
export interface GithubConnectionConfig {
|
||||||
|
|
@ -311,6 +312,80 @@ export interface BitbucketConnectionConfig {
|
||||||
};
|
};
|
||||||
revisions?: GitRevisions;
|
revisions?: GitRevisions;
|
||||||
}
|
}
|
||||||
|
export interface AzureDevOpsConnectionConfig {
|
||||||
|
/**
|
||||||
|
* Azure DevOps Configuration
|
||||||
|
*/
|
||||||
|
type: "azuredevops";
|
||||||
|
/**
|
||||||
|
* A Personal Access Token (PAT).
|
||||||
|
*/
|
||||||
|
token:
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The name of the secret that contains the token.
|
||||||
|
*/
|
||||||
|
secret: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The name of the environment variable that contains the token. Only supported in declarative connection configs.
|
||||||
|
*/
|
||||||
|
env: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.
|
||||||
|
*/
|
||||||
|
url?: string;
|
||||||
|
/**
|
||||||
|
* The type of Azure DevOps deployment
|
||||||
|
*/
|
||||||
|
deploymentType?: "cloud" | "server";
|
||||||
|
/**
|
||||||
|
* Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...).
|
||||||
|
*/
|
||||||
|
useTfsPath?: boolean;
|
||||||
|
/**
|
||||||
|
* List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property.
|
||||||
|
*/
|
||||||
|
orgs?: string[];
|
||||||
|
/**
|
||||||
|
* List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server.
|
||||||
|
*/
|
||||||
|
projects?: string[];
|
||||||
|
/**
|
||||||
|
* List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'.
|
||||||
|
*/
|
||||||
|
repos?: string[];
|
||||||
|
exclude?: {
|
||||||
|
/**
|
||||||
|
* Exclude disabled repositories from syncing.
|
||||||
|
*/
|
||||||
|
disabled?: boolean;
|
||||||
|
/**
|
||||||
|
* List of repositories to exclude from syncing. Glob patterns are supported.
|
||||||
|
*/
|
||||||
|
repos?: string[];
|
||||||
|
/**
|
||||||
|
* List of projects to exclude from syncing. Glob patterns are supported.
|
||||||
|
*/
|
||||||
|
projects?: string[];
|
||||||
|
/**
|
||||||
|
* Exclude repositories based on their size.
|
||||||
|
*/
|
||||||
|
size?: {
|
||||||
|
/**
|
||||||
|
* Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing.
|
||||||
|
*/
|
||||||
|
min?: number;
|
||||||
|
/**
|
||||||
|
* Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing.
|
||||||
|
*/
|
||||||
|
max?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
revisions?: GitRevisions;
|
||||||
|
}
|
||||||
export interface GenericGitHostConnectionConfig {
|
export interface GenericGitHostConnectionConfig {
|
||||||
/**
|
/**
|
||||||
* Generic Git host configuration
|
* Generic Git host configuration
|
||||||
|
|
|
||||||
|
|
@ -1131,6 +1131,209 @@ const schema = {
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AzureDevOpsConnectionConfig",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "azuredevops",
|
||||||
|
"description": "Azure DevOps Configuration"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"description": "A Personal Access Token (PAT).",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"secret": "SECRET_KEY"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"secret": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the secret that contains the token."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"secret"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"env": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the environment variable that contains the token. Only supported in declarative connection configs."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"env"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url",
|
||||||
|
"default": "https://dev.azure.com",
|
||||||
|
"description": "The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.",
|
||||||
|
"examples": [
|
||||||
|
"https://dev.azure.com",
|
||||||
|
"https://azuredevops.example.com"
|
||||||
|
],
|
||||||
|
"pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$"
|
||||||
|
},
|
||||||
|
"deploymentType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"cloud",
|
||||||
|
"server"
|
||||||
|
],
|
||||||
|
"default": "cloud",
|
||||||
|
"description": "The type of Azure DevOps deployment"
|
||||||
|
},
|
||||||
|
"useTfsPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...)."
|
||||||
|
},
|
||||||
|
"orgs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project",
|
||||||
|
"my-collection/my-project"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project/my-repo"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'."
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Exclude disabled repositories from syncing."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of repositories to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of projects to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exclude repositories based on their size.",
|
||||||
|
"properties": {
|
||||||
|
"min": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "The revisions (branches, tags) that should be included when indexing. The default branch (HEAD) is always indexed. A maximum of 64 revisions can be indexed, with any additional revisions being ignored.",
|
||||||
|
"properties": {
|
||||||
|
"branches": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of branches to include when indexing. For a given repo, only the branches that exist on the repo's remote *and* match at least one of the provided `branches` will be indexed. The default branch (HEAD) is always indexed. Glob patterns are supported. A maximum of 64 branches can be indexed, with any additional branches being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"main",
|
||||||
|
"release/*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of tags to include when indexing. For a given repo, only the tags that exist on the repo's remote *and* match at least one of the provided `tags` will be indexed. Glob patterns are supported. A maximum of 64 tags can be indexed, with any additional tags being ignored.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"latest",
|
||||||
|
"v2.*.*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"**"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"default": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ export type ConnectionConfig =
|
||||||
| GiteaConnectionConfig
|
| GiteaConnectionConfig
|
||||||
| GerritConnectionConfig
|
| GerritConnectionConfig
|
||||||
| BitbucketConnectionConfig
|
| BitbucketConnectionConfig
|
||||||
|
| AzureDevOpsConnectionConfig
|
||||||
| GenericGitHostConnectionConfig;
|
| GenericGitHostConnectionConfig;
|
||||||
export type LanguageModel =
|
export type LanguageModel =
|
||||||
| AmazonBedrockLanguageModel
|
| AmazonBedrockLanguageModel
|
||||||
|
|
@ -436,6 +437,80 @@ export interface BitbucketConnectionConfig {
|
||||||
};
|
};
|
||||||
revisions?: GitRevisions;
|
revisions?: GitRevisions;
|
||||||
}
|
}
|
||||||
|
export interface AzureDevOpsConnectionConfig {
|
||||||
|
/**
|
||||||
|
* Azure DevOps Configuration
|
||||||
|
*/
|
||||||
|
type: "azuredevops";
|
||||||
|
/**
|
||||||
|
* A Personal Access Token (PAT).
|
||||||
|
*/
|
||||||
|
token:
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The name of the secret that contains the token.
|
||||||
|
*/
|
||||||
|
secret: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* The name of the environment variable that contains the token. Only supported in declarative connection configs.
|
||||||
|
*/
|
||||||
|
env: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.
|
||||||
|
*/
|
||||||
|
url?: string;
|
||||||
|
/**
|
||||||
|
* The type of Azure DevOps deployment
|
||||||
|
*/
|
||||||
|
deploymentType?: "cloud" | "server";
|
||||||
|
/**
|
||||||
|
* Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...).
|
||||||
|
*/
|
||||||
|
useTfsPath?: boolean;
|
||||||
|
/**
|
||||||
|
* List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property.
|
||||||
|
*/
|
||||||
|
orgs?: string[];
|
||||||
|
/**
|
||||||
|
* List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server.
|
||||||
|
*/
|
||||||
|
projects?: string[];
|
||||||
|
/**
|
||||||
|
* List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'.
|
||||||
|
*/
|
||||||
|
repos?: string[];
|
||||||
|
exclude?: {
|
||||||
|
/**
|
||||||
|
* Exclude disabled repositories from syncing.
|
||||||
|
*/
|
||||||
|
disabled?: boolean;
|
||||||
|
/**
|
||||||
|
* List of repositories to exclude from syncing. Glob patterns are supported.
|
||||||
|
*/
|
||||||
|
repos?: string[];
|
||||||
|
/**
|
||||||
|
* List of projects to exclude from syncing. Glob patterns are supported.
|
||||||
|
*/
|
||||||
|
projects?: string[];
|
||||||
|
/**
|
||||||
|
* Exclude repositories based on their size.
|
||||||
|
*/
|
||||||
|
size?: {
|
||||||
|
/**
|
||||||
|
* Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing.
|
||||||
|
*/
|
||||||
|
min?: number;
|
||||||
|
/**
|
||||||
|
* Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing.
|
||||||
|
*/
|
||||||
|
max?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
revisions?: GitRevisions;
|
||||||
|
}
|
||||||
export interface GenericGitHostConnectionConfig {
|
export interface GenericGitHostConnectionConfig {
|
||||||
/**
|
/**
|
||||||
* Generic Git host configuration
|
* Generic Git host configuration
|
||||||
|
|
|
||||||
1
packages/web/public/azuredevops.svg
Normal file
1
packages/web/public/azuredevops.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg id="f4337506-5d95-4e80-b7ca-68498c6e008e" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><defs><linearGradient id="ba420277-700e-42cc-9de9-5388a5c16e54" x1="9" y1="16.97" x2="9" y2="1.03" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#0078d4" /><stop offset="0.16" stop-color="#1380da" /><stop offset="0.53" stop-color="#3c91e5" /><stop offset="0.82" stop-color="#559cec" /><stop offset="1" stop-color="#5ea0ef" /></linearGradient></defs><title>Icon-devops-261</title><path id="a91f0ca4-8fb7-4019-9c09-0a52e2c05754" d="M17,4v9.74l-4,3.28-6.2-2.26V17L3.29,12.41l10.23.8V4.44Zm-3.41.49L7.85,1V3.29L2.58,4.84,1,6.87v4.61l2.26,1V6.57Z" fill="url(#ba420277-700e-42cc-9de9-5388a5c16e54)" /></svg>
|
||||||
|
After Width: | Height: | Size: 740 B |
|
|
@ -14,6 +14,7 @@ import { gerritSchema } from "@sourcebot/schemas/v3/gerrit.schema";
|
||||||
import { giteaSchema } from "@sourcebot/schemas/v3/gitea.schema";
|
import { giteaSchema } from "@sourcebot/schemas/v3/gitea.schema";
|
||||||
import { githubSchema } from "@sourcebot/schemas/v3/github.schema";
|
import { githubSchema } from "@sourcebot/schemas/v3/github.schema";
|
||||||
import { gitlabSchema } from "@sourcebot/schemas/v3/gitlab.schema";
|
import { gitlabSchema } from "@sourcebot/schemas/v3/gitlab.schema";
|
||||||
|
import { azuredevopsSchema } from "@sourcebot/schemas/v3/azuredevops.schema";
|
||||||
import { GithubConnectionConfig } from "@sourcebot/schemas/v3/github.type";
|
import { GithubConnectionConfig } from "@sourcebot/schemas/v3/github.type";
|
||||||
import { GitlabConnectionConfig } from "@sourcebot/schemas/v3/gitlab.type";
|
import { GitlabConnectionConfig } from "@sourcebot/schemas/v3/gitlab.type";
|
||||||
import { GiteaConnectionConfig } from "@sourcebot/schemas/v3/gitea.type";
|
import { GiteaConnectionConfig } from "@sourcebot/schemas/v3/gitea.type";
|
||||||
|
|
@ -2187,6 +2188,8 @@ const parseConnectionConfig = (config: string) => {
|
||||||
return gerritSchema;
|
return gerritSchema;
|
||||||
case 'bitbucket':
|
case 'bitbucket':
|
||||||
return bitbucketSchema;
|
return bitbucketSchema;
|
||||||
|
case 'azuredevops':
|
||||||
|
return azuredevopsSchema;
|
||||||
case 'git':
|
case 'git':
|
||||||
return genericGitHostSchema;
|
return genericGitHostSchema;
|
||||||
}
|
}
|
||||||
|
|
@ -2221,7 +2224,8 @@ const parseConnectionConfig = (config: string) => {
|
||||||
switch (connectionType) {
|
switch (connectionType) {
|
||||||
case "gitea":
|
case "gitea":
|
||||||
case "github":
|
case "github":
|
||||||
case "bitbucket": {
|
case "bitbucket":
|
||||||
|
case "azuredevops": {
|
||||||
return {
|
return {
|
||||||
numRepos: parsedConfig.repos?.length,
|
numRepos: parsedConfig.repos?.length,
|
||||||
hasToken: !!parsedConfig.token,
|
hasToken: !!parsedConfig.token,
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName, domain }:
|
||||||
webUrl: repoInfoResponse.webUrl,
|
webUrl: repoInfoResponse.webUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// @todo: this is a hack to support linking to files for ADO. ADO doesn't support web urls with HEAD so we replace it with main. THis
|
||||||
|
// will break if the default branch is not main.
|
||||||
|
const fileWebUrl = repoInfoResponse.codeHostType === "azuredevops" && fileSourceResponse.webUrl ?
|
||||||
|
fileSourceResponse.webUrl.replace("version=GBHEAD", "version=GBmain") : fileSourceResponse.webUrl;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row py-1 px-2 items-center justify-between">
|
<div className="flex flex-row py-1 px-2 items-center justify-between">
|
||||||
|
|
@ -47,9 +52,11 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName, domain }:
|
||||||
}}
|
}}
|
||||||
branchDisplayName={revisionName}
|
branchDisplayName={revisionName}
|
||||||
/>
|
/>
|
||||||
{(fileSourceResponse.webUrl && codeHostInfo) && (
|
|
||||||
|
{(fileWebUrl && codeHostInfo) && (
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href={fileSourceResponse.webUrl}
|
href={fileWebUrl}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="flex flex-row items-center gap-2 px-2 py-0.5 rounded-md flex-shrink-0"
|
className="flex flex-row items-center gap-2 px-2 py-0.5 rounded-md flex-shrink-0"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { type ClassValue, clsx } from "clsx"
|
import { type ClassValue, clsx } from "clsx"
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge"
|
||||||
import githubLogo from "@/public/github.svg";
|
import githubLogo from "@/public/github.svg";
|
||||||
|
import azuredevopsLogo from "@/public/azuredevops.svg";
|
||||||
import gitlabLogo from "@/public/gitlab.svg";
|
import gitlabLogo from "@/public/gitlab.svg";
|
||||||
import giteaLogo from "@/public/gitea.svg";
|
import giteaLogo from "@/public/gitea.svg";
|
||||||
import gerritLogo from "@/public/gerrit.svg";
|
import gerritLogo from "@/public/gerrit.svg";
|
||||||
|
|
@ -69,6 +70,7 @@ export type CodeHostType =
|
||||||
"gerrit" |
|
"gerrit" |
|
||||||
"bitbucket-cloud" |
|
"bitbucket-cloud" |
|
||||||
"bitbucket-server" |
|
"bitbucket-server" |
|
||||||
|
"azuredevops" |
|
||||||
"generic-git-host";
|
"generic-git-host";
|
||||||
|
|
||||||
export type AuthProviderType =
|
export type AuthProviderType =
|
||||||
|
|
@ -210,6 +212,17 @@ export const getCodeHostInfoForRepo = (repo: {
|
||||||
iconClassName: className,
|
iconClassName: className,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 'azuredevops': {
|
||||||
|
const { src, className } = getCodeHostIcon('azuredevops')!;
|
||||||
|
return {
|
||||||
|
type: "azuredevops",
|
||||||
|
displayName: displayName ?? name,
|
||||||
|
codeHostName: "Azure DevOps",
|
||||||
|
repoLink: webUrl,
|
||||||
|
icon: src,
|
||||||
|
iconClassName: className,
|
||||||
|
}
|
||||||
|
}
|
||||||
case 'gitea': {
|
case 'gitea': {
|
||||||
const { src, className } = getCodeHostIcon('gitea')!;
|
const { src, className } = getCodeHostIcon('gitea')!;
|
||||||
return {
|
return {
|
||||||
|
|
@ -293,6 +306,10 @@ export const getCodeHostIcon = (codeHostType: string): { src: string, className?
|
||||||
return {
|
return {
|
||||||
src: bitbucketLogo,
|
src: bitbucketLogo,
|
||||||
}
|
}
|
||||||
|
case "azuredevops":
|
||||||
|
return {
|
||||||
|
src: azuredevopsLogo,
|
||||||
|
}
|
||||||
case "generic-git-host":
|
case "generic-git-host":
|
||||||
return {
|
return {
|
||||||
src: gitLogo,
|
src: gitLogo,
|
||||||
|
|
@ -309,6 +326,7 @@ export const isAuthSupportedForCodeHost = (codeHostType: CodeHostType): boolean
|
||||||
case "gitea":
|
case "gitea":
|
||||||
case "bitbucket-cloud":
|
case "bitbucket-cloud":
|
||||||
case "bitbucket-server":
|
case "bitbucket-server":
|
||||||
|
case "azuredevops":
|
||||||
return true;
|
return true;
|
||||||
case "generic-git-host":
|
case "generic-git-host":
|
||||||
case "gerrit":
|
case "gerrit":
|
||||||
|
|
|
||||||
135
schemas/v3/azuredevops.json
Normal file
135
schemas/v3/azuredevops.json
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"title": "AzureDevOpsConnectionConfig",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "azuredevops",
|
||||||
|
"description": "Azure DevOps Configuration"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"$ref": "./shared.json#/definitions/Token",
|
||||||
|
"description": "A Personal Access Token (PAT).",
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"secret": "SECRET_KEY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url",
|
||||||
|
"default": "https://dev.azure.com",
|
||||||
|
"description": "The URL of the Azure DevOps host. For Azure DevOps Cloud, use https://dev.azure.com. For Azure DevOps Server, use your server URL.",
|
||||||
|
"examples": [
|
||||||
|
"https://dev.azure.com",
|
||||||
|
"https://azuredevops.example.com"
|
||||||
|
],
|
||||||
|
"pattern": "^https?:\\/\\/[^\\s/$.?#].[^\\s]*$"
|
||||||
|
},
|
||||||
|
"deploymentType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["cloud", "server"],
|
||||||
|
"default": "cloud",
|
||||||
|
"description": "The type of Azure DevOps deployment"
|
||||||
|
},
|
||||||
|
"useTfsPath": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Use legacy TFS path format (/tfs) in API URLs. Required for older TFS installations (TFS 2018 and earlier). When true, API URLs will include /tfs in the path (e.g., https://server/tfs/collection/_apis/...)."
|
||||||
|
},
|
||||||
|
"orgs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of organizations to sync with. For Cloud, this is the organization name. For Server, this is the collection name. All projects and repositories visible to the provided `token` will be synced, unless explicitly defined in the `exclude` property."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project",
|
||||||
|
"my-collection/my-project"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of specific projects to sync with. Expected to be formatted as '{orgName}/{projectName}' for Cloud or '{collectionName}/{projectName}' for Server."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[\\w.-]+\\/[\\w.-]+\\/[\\w.-]+$"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"examples": [
|
||||||
|
[
|
||||||
|
"my-org/my-project/my-repo"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"description": "List of individual repositories to sync with. Expected to be formatted as '{orgName}/{projectName}/{repoName}'."
|
||||||
|
},
|
||||||
|
"exclude": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Exclude disabled repositories from syncing."
|
||||||
|
},
|
||||||
|
"repos": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of repositories to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"default": [],
|
||||||
|
"description": "List of projects to exclude from syncing. Glob patterns are supported."
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Exclude repositories based on their size.",
|
||||||
|
"properties": {
|
||||||
|
"min": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Minimum repository size (in bytes) to sync (inclusive). Repositories less than this size will be excluded from syncing."
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Maximum repository size (in bytes) to sync (inclusive). Repositories greater than this size will be excluded from syncing."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"revisions": {
|
||||||
|
"$ref": "./shared.json#/definitions/GitRevisions"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"token"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,9 @@
|
||||||
{
|
{
|
||||||
"$ref": "./bitbucket.json"
|
"$ref": "./bitbucket.json"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "./azuredevops.json"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "./genericGitHost.json"
|
"$ref": "./genericGitHost.json"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
vendor/zoekt
vendored
2
vendor/zoekt
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 12a2f4ad075359a09bd8a91793acb002211217aa
|
Subproject commit 07c64afd5c719b5c95aae21c71009aacadbc528e
|
||||||
66
yarn.lock
66
yarn.lock
|
|
@ -7562,6 +7562,7 @@ __metadata:
|
||||||
"@types/micromatch": "npm:^4.0.9"
|
"@types/micromatch": "npm:^4.0.9"
|
||||||
"@types/node": "npm:^22.7.5"
|
"@types/node": "npm:^22.7.5"
|
||||||
argparse: "npm:^2.0.1"
|
argparse: "npm:^2.0.1"
|
||||||
|
azure-devops-node-api: "npm:^15.1.1"
|
||||||
bullmq: "npm:^5.34.10"
|
bullmq: "npm:^5.34.10"
|
||||||
cross-env: "npm:^7.0.3"
|
cross-env: "npm:^7.0.3"
|
||||||
cross-fetch: "npm:^4.0.0"
|
cross-fetch: "npm:^4.0.0"
|
||||||
|
|
@ -9551,6 +9552,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"azure-devops-node-api@npm:^15.1.1":
|
||||||
|
version: 15.1.1
|
||||||
|
resolution: "azure-devops-node-api@npm:15.1.1"
|
||||||
|
dependencies:
|
||||||
|
tunnel: "npm:0.0.6"
|
||||||
|
typed-rest-client: "npm:2.1.0"
|
||||||
|
checksum: 10c0/e57de52745b322523c2f0220770fcbcb692032610d240644c96914ba0f08613416b701e56dc996b52c9bf6761270148a931f694630ed171b238555e237ee002c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"bail@npm:^2.0.0":
|
"bail@npm:^2.0.0":
|
||||||
version: 2.0.2
|
version: 2.0.2
|
||||||
resolution: "bail@npm:2.0.2"
|
resolution: "bail@npm:2.0.2"
|
||||||
|
|
@ -10905,6 +10916,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"des.js@npm:^1.1.0":
|
||||||
|
version: 1.1.0
|
||||||
|
resolution: "des.js@npm:1.1.0"
|
||||||
|
dependencies:
|
||||||
|
inherits: "npm:^2.0.1"
|
||||||
|
minimalistic-assert: "npm:^1.0.0"
|
||||||
|
checksum: 10c0/671354943ad67493e49eb4c555480ab153edd7cee3a51c658082fcde539d2690ed2a4a0b5d1f401f9cde822edf3939a6afb2585f32c091f2d3a1b1665cd45236
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"destroy@npm:1.2.0":
|
"destroy@npm:1.2.0":
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
resolution: "destroy@npm:1.2.0"
|
resolution: "destroy@npm:1.2.0"
|
||||||
|
|
@ -13445,7 +13466,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3, inherits@npm:^2.0.4":
|
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4":
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
resolution: "inherits@npm:2.0.4"
|
resolution: "inherits@npm:2.0.4"
|
||||||
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
|
checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
|
||||||
|
|
@ -13997,6 +14018,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"js-md4@npm:^0.3.2":
|
||||||
|
version: 0.3.2
|
||||||
|
resolution: "js-md4@npm:0.3.2"
|
||||||
|
checksum: 10c0/8313e00c45f710a53bdadc199c095b48ebaf54ea7b8cdb67a3f1863c270a5e9d0f89f204436b73866002af8c7ac4cacc872fdf271fc70e26614e424c7685b577
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
|
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
resolution: "js-tokens@npm:4.0.0"
|
resolution: "js-tokens@npm:4.0.0"
|
||||||
|
|
@ -15264,6 +15292,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"minimalistic-assert@npm:^1.0.0":
|
||||||
|
version: 1.0.1
|
||||||
|
resolution: "minimalistic-assert@npm:1.0.1"
|
||||||
|
checksum: 10c0/96730e5601cd31457f81a296f521eb56036e6f69133c0b18c13fe941109d53ad23a4204d946a0d638d7f3099482a0cec8c9bb6d642604612ce43ee536be3dddd
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"minimatch@npm:^10.0.0":
|
"minimatch@npm:^10.0.0":
|
||||||
version: 10.0.1
|
version: 10.0.1
|
||||||
resolution: "minimatch@npm:10.0.1"
|
resolution: "minimatch@npm:10.0.1"
|
||||||
|
|
@ -16939,7 +16974,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.14.0":
|
"qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.14.0":
|
||||||
version: 6.14.0
|
version: 6.14.0
|
||||||
resolution: "qs@npm:6.14.0"
|
resolution: "qs@npm:6.14.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -19436,6 +19471,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"tunnel@npm:0.0.6":
|
||||||
|
version: 0.0.6
|
||||||
|
resolution: "tunnel@npm:0.0.6"
|
||||||
|
checksum: 10c0/e27e7e896f2426c1c747325b5f54efebc1a004647d853fad892b46d64e37591ccd0b97439470795e5262b5c0748d22beb4489a04a0a448029636670bfd801b75
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
|
"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
|
||||||
version: 0.4.0
|
version: 0.4.0
|
||||||
resolution: "type-check@npm:0.4.0"
|
resolution: "type-check@npm:0.4.0"
|
||||||
|
|
@ -19533,6 +19575,19 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"typed-rest-client@npm:2.1.0":
|
||||||
|
version: 2.1.0
|
||||||
|
resolution: "typed-rest-client@npm:2.1.0"
|
||||||
|
dependencies:
|
||||||
|
des.js: "npm:^1.1.0"
|
||||||
|
js-md4: "npm:^0.3.2"
|
||||||
|
qs: "npm:^6.10.3"
|
||||||
|
tunnel: "npm:0.0.6"
|
||||||
|
underscore: "npm:^1.12.1"
|
||||||
|
checksum: 10c0/b9d29db5217b6d3d0ae9aa68e87e84be8c2d885e7a932f4df3eca070bb615ded5f390035f26857996911803830d28ba2296d6cb748072dbc6d8657916107132d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"typescript@npm:^5, typescript@npm:^5.6.2, typescript@npm:^5.7.3":
|
"typescript@npm:^5, typescript@npm:^5.6.2, typescript@npm:^5.7.3":
|
||||||
version: 5.8.2
|
version: 5.8.2
|
||||||
resolution: "typescript@npm:5.8.2"
|
resolution: "typescript@npm:5.8.2"
|
||||||
|
|
@ -19608,6 +19663,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"underscore@npm:^1.12.1":
|
||||||
|
version: 1.13.7
|
||||||
|
resolution: "underscore@npm:1.13.7"
|
||||||
|
checksum: 10c0/fad2b4aac48847674aaf3c30558f383399d4fdafad6dd02dd60e4e1b8103b52c5a9e5937e0cc05dacfd26d6a0132ed0410ab4258241240757e4a4424507471cd
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"undici-types@npm:~5.26.4":
|
"undici-types@npm:~5.26.4":
|
||||||
version: 5.26.5
|
version: 5.26.5
|
||||||
resolution: "undici-types@npm:5.26.5"
|
resolution: "undici-types@npm:5.26.5"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue