-
Notifications
You must be signed in to change notification settings - Fork 1
feat: new build page #678
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: new build page #678
Changes from all commits
9c8ed8e
742ac23
2b636e0
5b8f54f
c3203b1
77d777f
e26e8cd
2f120b5
9035230
44a4e08
e36cd36
66d7e8f
dd8c395
a2d1053
a6d51ae
e21daff
22946bc
7c41e70
44ccc11
861ac4d
af3330b
2f9f2ad
3f296fa
0be59e5
5066c49
ff8754c
5e4703f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import Debug from 'debug' | ||
| import { Operation, OperationHandlerArray } from 'express-openapi' | ||
| import { OpenApiRequestExt } from 'src/otomi-models' | ||
|
|
||
| const debug = Debug('otomi:api:repoBranches') | ||
|
|
||
| export default function (): OperationHandlerArray { | ||
| const get: Operation = [ | ||
| async ({ otomi, query }: OpenApiRequestExt, res): Promise<void> => { | ||
| debug(`getRepoBranches`, query) | ||
| const { codeRepoName, teamId }: { codeRepoName: string; teamId: string } = query as any | ||
| res.json(await otomi.getRepoBranches(codeRepoName, teamId)) | ||
| }, | ||
| ] | ||
| const api = { | ||
| get, | ||
| } | ||
| return api | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| RepoBranches: | ||
| x-acl: | ||
| platformAdmin: [read-any] | ||
| teamAdmin: [read-any] | ||
| teamMember: [read] | ||
| properties: {} | ||
| type: object |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -105,15 +105,17 @@ import { TeamConfigService } from './services/TeamConfigService' | |
| import { validateBackupFields } from './utils/backupUtils' | ||
| import { | ||
| getGiteaRepoUrls, | ||
| getPrivateRepoBranches, | ||
| getPublicRepoBranches, | ||
| normalizeRepoUrl, | ||
| testPrivateRepoConnect, | ||
| testPublicRepoConnect, | ||
| } from './utils/codeRepoUtils' | ||
| import { getAplObjectFromV1, getV1MergeObject, getV1ObjectFromApl, getV1SealedSecretFromApl } from './utils/manifests' | ||
| import { getEncryptedData, sealedSecretManifest, SealedSecretManifestType } from './utils/sealedSecretUtils' | ||
| import { getKeycloakUsers, isValidUsername } from './utils/userUtils' | ||
| import { ObjectStorageClient } from './utils/wizardUtils' | ||
| import { fetchChartYaml, fetchWorkloadCatalog, NewHelmChartValues, sparseCloneChart } from './utils/workloadUtils' | ||
| import { getAplObjectFromV1, getV1MergeObject, getV1ObjectFromApl, getV1SealedSecretFromApl } from './utils/manifests' | ||
|
|
||
| interface ExcludedApp extends App { | ||
| managed: boolean | ||
|
|
@@ -1275,8 +1277,11 @@ export default class OtomiStack { | |
| } | ||
|
|
||
| async createAplCodeRepo(teamId: string, data: AplCodeRepoRequest): Promise<AplCodeRepoResponse> { | ||
| const allRepoUrls = this.getAllAplCodeRepos().map((repo) => repo.spec.repositoryUrl) || [] | ||
| if (allRepoUrls.includes(data.spec.repositoryUrl)) throw new AlreadyExists('Code repository URL already exists') | ||
| if (!data.spec.private) unset(data.spec, 'secret') | ||
| if (data.spec.gitService === 'gitea') unset(data.spec, 'private') | ||
| try { | ||
| if (!data.spec.private) unset(data.spec, 'secret') | ||
| const codeRepo = this.repoService.getTeamConfigService(teamId).createCodeRepo(data) | ||
| await this.saveTeamConfigItem(codeRepo) | ||
| await this.doTeamDeployment( | ||
|
|
@@ -1288,7 +1293,9 @@ export default class OtomiStack { | |
| ) | ||
| return codeRepo | ||
| } catch (err) { | ||
| if (err.code === 409) err.publicMessage = 'Code repo label already exists' | ||
| if (err.code === 409) { | ||
| err.publicMessage = 'Code repo name already exists' | ||
| } | ||
| throw err | ||
| } | ||
| } | ||
|
|
@@ -1314,6 +1321,7 @@ export default class OtomiStack { | |
| patch = false, | ||
| ): Promise<AplCodeRepoResponse> { | ||
| if (!data.spec?.private) unset(data.spec, 'secret') | ||
| if (data.spec?.gitService === 'gitea') unset(data.spec, 'private') | ||
| const codeRepo = patch | ||
| ? this.repoService.getTeamConfigService(teamId).patchCodeRepo(name, data) | ||
| : this.repoService.getTeamConfigService(teamId).updateCodeRepo(name, data as AplCodeRepoRequest) | ||
|
|
@@ -1341,7 +1349,10 @@ export default class OtomiStack { | |
| ) | ||
| } | ||
|
|
||
| async getTestRepoConnect(url: string, teamId: string, secretName: string): Promise<TestRepoConnect> { | ||
| async getRepoBranches(codeRepoName: string, teamId: string): Promise<string[]> { | ||
| if (!codeRepoName) return ['HEAD'] | ||
| const coderepo = this.getCodeRepo(teamId, codeRepoName) | ||
| const { repositoryUrl, secret: secretName } = coderepo | ||
| try { | ||
| let sshPrivateKey = '', | ||
| username = '', | ||
|
|
@@ -1355,6 +1366,38 @@ export default class OtomiStack { | |
| } | ||
|
|
||
| const isPrivate = !!secretName | ||
| const isSSH = !!sshPrivateKey | ||
| const repoUrl = repositoryUrl.startsWith('https://gitea') | ||
| ? repositoryUrl | ||
| : normalizeRepoUrl(repositoryUrl, isPrivate, isSSH) | ||
|
|
||
| if (!repoUrl) return ['HEAD'] | ||
|
|
||
| if (isPrivate) return await getPrivateRepoBranches(repoUrl, sshPrivateKey, username, accessToken) | ||
|
||
|
|
||
| return await getPublicRepoBranches(repoUrl) | ||
| } catch (error) { | ||
| const errorMessage = error.response?.data?.message || error?.message || 'Failed to get repo branches' | ||
| debug('Error getting branches:', errorMessage) | ||
| return [] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No error log? |
||
| } | ||
| } | ||
|
|
||
| async getTestRepoConnect(url: string, teamId: string, secretName: string): Promise<TestRepoConnect> { | ||
| try { | ||
| let sshPrivateKey = '', | ||
| username = '', | ||
| accessToken = '' | ||
|
|
||
| const isPrivate = !!secretName | ||
|
|
||
| if (isPrivate) { | ||
| const secret = await getSecretValues(secretName, `team-${teamId}`) | ||
| sshPrivateKey = secret?.['ssh-privatekey'] || '' | ||
| username = secret?.username || '' | ||
| accessToken = secret?.password || '' | ||
| } | ||
|
|
||
| const isSSH = !!sshPrivateKey | ||
| const repoUrl = normalizeRepoUrl(url, isPrivate, isSSH) | ||
|
|
||
|
|
@@ -1422,6 +1465,12 @@ export default class OtomiStack { | |
| } | ||
|
|
||
| async createAplBuild(teamId: string, data: AplBuildRequest): Promise<AplBuildResponse> { | ||
| const buildName = `${data?.spec?.imageName}-${data?.spec?.tag}` | ||
| if (buildName.length > 128) | ||
| throw new HttpError( | ||
| 400, | ||
| 'Invalid container image name, the combined image name and tag must not exceed 128 characters.', | ||
| ) | ||
| try { | ||
| const build = this.repoService.getTeamConfigService(teamId).createBuild(data) | ||
| await this.saveTeamConfigItem(build) | ||
|
|
@@ -1434,7 +1483,8 @@ export default class OtomiStack { | |
| ) | ||
| return build | ||
| } catch (err) { | ||
| if (err.code === 409) err.publicMessage = 'Build name already exists' | ||
| if (err.code === 409) | ||
| err.publicMessage = 'Container image name already exists, the combined image name and tag must be unique.' | ||
| throw err | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the
tryblock should start from here. Then you do not need a complicated catch statement and throw AlreadyExists exception.