mirror of
https://github.com/harness/drone.git
synced 2025-05-04 14:30:28 +08:00
408 lines
11 KiB
TypeScript
408 lines
11 KiB
TypeScript
/*
|
|
* Copyright 2023 Harness, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// Code copied from https://github.com/vweevers/is-git-ref-name-valid and
|
|
// https://github.com/vweevers/is-git-branch-name-valid (MIT, © Vincent Weevers)
|
|
// Last updated for git 2.29.0.
|
|
|
|
import type { IconName } from '@harnessio/icons'
|
|
import type {
|
|
EnumWebhookTrigger,
|
|
OpenapiContentInfo,
|
|
OpenapiDirContent,
|
|
OpenapiGetContentOutput,
|
|
TypesCommit,
|
|
TypesPullReq,
|
|
TypesRepository
|
|
} from 'services/code'
|
|
import { getErrorMessage } from './Utils'
|
|
|
|
export interface GitInfoProps {
|
|
repoMetadata: TypesRepository
|
|
gitRef: string
|
|
resourcePath: string
|
|
resourceContent: OpenapiGetContentOutput
|
|
commitRef: string
|
|
commits: TypesCommit[]
|
|
pullRequestMetadata: TypesPullReq
|
|
}
|
|
export interface RepoFormData {
|
|
name: string
|
|
description: string
|
|
license: string
|
|
defaultBranch: string
|
|
gitignore: string
|
|
addReadme: boolean
|
|
isPublic: RepoVisibility
|
|
}
|
|
export interface ImportFormData {
|
|
gitProvider: GitProviders
|
|
hostUrl: string
|
|
org: string
|
|
repo: string
|
|
username: string
|
|
password: string
|
|
name: string
|
|
description: string
|
|
isPublic: RepoVisibility
|
|
}
|
|
|
|
export interface ExportFormData {
|
|
accountId: string
|
|
token: string
|
|
organization: string
|
|
name: string
|
|
}
|
|
|
|
export interface ExportFormDataExtended extends ExportFormData {
|
|
repoCount: number
|
|
}
|
|
|
|
export interface ImportSpaceFormData {
|
|
gitProvider: GitProviders
|
|
username: string
|
|
password: string
|
|
name: string
|
|
description: string
|
|
organization: string
|
|
host: string
|
|
}
|
|
|
|
export enum RepoVisibility {
|
|
PUBLIC = 'public',
|
|
PRIVATE = 'private'
|
|
}
|
|
|
|
export enum RepoCreationType {
|
|
IMPORT = 'import',
|
|
CREATE = 'create',
|
|
IMPORT_MULTIPLE = 'import_multiple'
|
|
}
|
|
|
|
export enum SpaceCreationType {
|
|
IMPORT = 'import',
|
|
CREATE = 'create'
|
|
}
|
|
|
|
export enum GitContentType {
|
|
FILE = 'file',
|
|
DIR = 'dir',
|
|
SYMLINK = 'symlink',
|
|
SUBMODULE = 'submodule'
|
|
}
|
|
export enum SettingsTab {
|
|
webhooks = 'webhook',
|
|
general = '/',
|
|
branchProtection = 'rules'
|
|
}
|
|
|
|
export enum GitBranchType {
|
|
ACTIVE = 'active',
|
|
INACTIVE = 'inactive',
|
|
YOURS = 'yours',
|
|
ALL = 'all'
|
|
}
|
|
|
|
export enum GitRefType {
|
|
BRANCH = 'branch',
|
|
TAG = 'tag'
|
|
}
|
|
|
|
export enum PrincipalUserType {
|
|
USER = 'user',
|
|
SERVICE = 'service'
|
|
}
|
|
|
|
export enum SettingTypeMode {
|
|
EDIT = 'edit',
|
|
NEW = 'new'
|
|
}
|
|
|
|
export enum BranchTargetType {
|
|
INCLUDE = 'include',
|
|
EXCLUDE = 'exclude'
|
|
}
|
|
|
|
export interface BranchTargetOption {
|
|
type: BranchTargetType
|
|
title: string
|
|
}
|
|
|
|
export const branchTargetOptions: BranchTargetOption[] = [
|
|
{
|
|
type: BranchTargetType.INCLUDE,
|
|
title: 'Include'
|
|
},
|
|
{
|
|
type: BranchTargetType.EXCLUDE,
|
|
title: 'Exclude'
|
|
}
|
|
]
|
|
|
|
export enum GitCommitAction {
|
|
DELETE = 'DELETE',
|
|
CREATE = 'CREATE',
|
|
UPDATE = 'UPDATE',
|
|
MOVE = 'MOVE'
|
|
}
|
|
|
|
export enum PullRequestState {
|
|
OPEN = 'open',
|
|
MERGED = 'merged',
|
|
CLOSED = 'closed'
|
|
}
|
|
|
|
export enum GitProviders {
|
|
GITHUB = 'GitHub',
|
|
GITHUB_ENTERPRISE = 'GitHub Enterprise',
|
|
GITLAB = 'GitLab',
|
|
GITLAB_SELF_HOSTED = 'GitLab Self-Hosted',
|
|
BITBUCKET = 'Bitbucket',
|
|
BITBUCKET_SERVER = 'Bitbucket Server',
|
|
GITEA = 'Gitea',
|
|
GOGS = 'Gogs'
|
|
}
|
|
|
|
export const PullRequestFilterOption = {
|
|
...PullRequestState,
|
|
// REJECTED: 'rejected',
|
|
DRAFT: 'draft',
|
|
YOURS: 'yours',
|
|
ALL: 'all'
|
|
}
|
|
|
|
export const CodeIcon = {
|
|
Logo: 'code' as IconName,
|
|
PullRequest: 'git-pull' as IconName,
|
|
Merged: 'code-merged' as IconName,
|
|
Draft: 'code-draft' as IconName,
|
|
PullRequestRejected: 'main-close' as IconName,
|
|
Add: 'plus' as IconName,
|
|
BranchSmall: 'code-branch-small' as IconName,
|
|
Branch: 'code-branch' as IconName,
|
|
Tag: 'main-tags' as IconName,
|
|
Clone: 'code-clone' as IconName,
|
|
Close: 'code-close' as IconName,
|
|
CommitLight: 'code-commit-light' as IconName,
|
|
CommitSmall: 'code-commit-small' as IconName,
|
|
Commit: 'code-commit' as IconName,
|
|
Copy: 'code-copy' as IconName,
|
|
Delete: 'code-delete' as IconName,
|
|
Edit: 'code-edit' as IconName,
|
|
FileLight: 'code-file-light' as IconName,
|
|
File: 'code-file' as IconName,
|
|
Folder: 'code-folder' as IconName,
|
|
History: 'code-history' as IconName,
|
|
Info: 'code-info' as IconName,
|
|
More: 'code-more' as IconName,
|
|
Repo: 'code-repo' as IconName,
|
|
Settings: 'code-settings' as IconName,
|
|
Webhook: 'code-webhook' as IconName,
|
|
InputSpinner: 'steps-spinne' as IconName,
|
|
InputSearch: 'search' as IconName,
|
|
Chat: 'code-chat' as IconName,
|
|
Checks: 'main-tick' as IconName,
|
|
ChecksSuccess: 'success-tick' as IconName
|
|
}
|
|
|
|
export const normalizeGitRef = (gitRef: string | undefined) => {
|
|
if (isRefATag(gitRef)) {
|
|
return gitRef
|
|
} else if (isRefABranch(gitRef)) {
|
|
return gitRef
|
|
} else if (gitRef === '') {
|
|
return ''
|
|
} else if (gitRef && isGitRev(gitRef)) {
|
|
return gitRef
|
|
} else {
|
|
return `refs/heads/${gitRef}`
|
|
}
|
|
}
|
|
|
|
export const REFS_TAGS_PREFIX = 'refs/tags/'
|
|
export const REFS_BRANCH_PREFIX = 'refs/heads/'
|
|
|
|
export const FILE_VIEWED_OBSOLETE_SHA = 'ffffffffffffffffffffffffffffffffffffffff'
|
|
|
|
export function formatTriggers(triggers: EnumWebhookTrigger[]) {
|
|
return triggers.map(trigger => {
|
|
return trigger
|
|
.split('_')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ')
|
|
})
|
|
}
|
|
|
|
export const handleUpload = (
|
|
blob: File,
|
|
setMarkdownContent: (data: string) => void,
|
|
repoMetadata: TypesRepository | undefined,
|
|
showError: (message: React.ReactNode, timeout?: number | undefined, key?: string | undefined) => void
|
|
) => {
|
|
const reader = new FileReader()
|
|
// Set up a function to be called when the load event is triggered
|
|
reader.onload = async function () {
|
|
const markdown = await uploadImage(reader.result, showError, repoMetadata)
|
|
setMarkdownContent(markdown) // Set the markdown content
|
|
}
|
|
reader.readAsArrayBuffer(blob) // This will trigger the onload function when the reading is complete
|
|
}
|
|
|
|
export const uploadImage = async (
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
fileBlob: any,
|
|
showError: (message: React.ReactNode, timeout?: number | undefined, key?: string | undefined) => void,
|
|
repoMetadata: TypesRepository | undefined
|
|
) => {
|
|
try {
|
|
const response = await fetch(`${window.location.origin}/api/v1/repos/${repoMetadata?.path}/+/uploads/`, {
|
|
method: 'POST',
|
|
headers: {
|
|
Accept: 'application/json',
|
|
'content-type': 'application/octet-stream'
|
|
},
|
|
body: fileBlob,
|
|
redirect: 'follow'
|
|
})
|
|
const result = await response.json()
|
|
if (!response.ok && result) {
|
|
showError(getErrorMessage(result))
|
|
return ''
|
|
}
|
|
const filePath = result.file_path
|
|
return window.location.origin + '/' + 'api/v1/repos/' + repoMetadata?.path + '/+/uploads/' + filePath
|
|
} catch (exception) {
|
|
showError(getErrorMessage(exception))
|
|
return ''
|
|
}
|
|
}
|
|
|
|
// eslint-disable-next-line no-control-regex
|
|
const BAD_GIT_REF_REGREX = /(^|[/.])([/.]|$)|^@$|@{|[\x00-\x20\x7f~^:?*[\\]|\.lock(\/|$)/
|
|
const BAD_GIT_BRANCH_REGREX = /^(-|HEAD$)/
|
|
|
|
function isGitRefValid(name: string, onelevel: boolean): boolean {
|
|
return !BAD_GIT_REF_REGREX.test(name) && (!!onelevel || name.includes('/'))
|
|
}
|
|
|
|
export function isGitBranchNameValid(name: string): boolean {
|
|
return isGitRefValid(name, true) && !BAD_GIT_BRANCH_REGREX.test(name)
|
|
}
|
|
|
|
export const isDir = (content: Nullable<OpenapiGetContentOutput>): boolean => content?.type === GitContentType.DIR
|
|
export const isFile = (content: Nullable<OpenapiGetContentOutput>): boolean => content?.type === GitContentType.FILE
|
|
export const isSymlink = (content: Nullable<OpenapiGetContentOutput>): boolean =>
|
|
content?.type === GitContentType.SYMLINK
|
|
export const isSubmodule = (content: Nullable<OpenapiGetContentOutput>): boolean =>
|
|
content?.type === GitContentType.SUBMODULE
|
|
|
|
export const findReadmeInfo = (content: Nullable<OpenapiGetContentOutput>): OpenapiContentInfo | undefined =>
|
|
(content?.content as OpenapiDirContent)?.entries?.find(
|
|
entry => entry.type === GitContentType.FILE && /^readme(.md)?$/.test(entry?.name?.toLowerCase() || '')
|
|
)
|
|
|
|
export const findMarkdownInfo = (content: Nullable<OpenapiGetContentOutput>): OpenapiContentInfo | undefined =>
|
|
content?.type === GitContentType.FILE && /.md$/.test(content?.name?.toLowerCase() || '') ? content : undefined
|
|
|
|
export const isRefATag = (gitRef: string | undefined) => gitRef?.includes(REFS_TAGS_PREFIX) || false
|
|
export const isRefABranch = (gitRef: string | undefined) => gitRef?.includes(REFS_BRANCH_PREFIX) || false
|
|
|
|
/**
|
|
* Make a diff refs string to use in URL.
|
|
* @param targetGitRef target git ref (base ref).
|
|
* @param sourceGitRef source git ref (compare ref).
|
|
* @returns A concatenation string of `targetGitRef...sourceGitRef`.
|
|
*/
|
|
export const makeDiffRefs = (targetGitRef: string, sourceGitRef: string) => `${targetGitRef}...${sourceGitRef}`
|
|
|
|
/**
|
|
* Split a diff refs string into targetRef and sourceRef.
|
|
* @param diffRefs diff refs string.
|
|
* @returns An object of { targetGitRef, sourceGitRef }
|
|
*/
|
|
export const diffRefsToRefs = (diffRefs: string) => {
|
|
const parts = diffRefs.split('...')
|
|
|
|
return {
|
|
targetGitRef: parts[0] || '',
|
|
sourceGitRef: parts[1] || ''
|
|
}
|
|
}
|
|
|
|
export const decodeGitContent = (content = '') => {
|
|
try {
|
|
// Decode base64 content for text file
|
|
return decodeURIComponent(escape(window.atob(content)))
|
|
} catch (_exception) {
|
|
try {
|
|
// Return original base64 content for binary file
|
|
return content
|
|
} catch (exception) {
|
|
console.error(exception) // eslint-disable-line no-console
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
|
|
// Check if gitRef is a git commit hash (https://github.com/diegohaz/is-git-rev, MIT © Diego Haz)
|
|
export const isGitRev = (gitRef = ''): boolean => /^[0-9a-f]{7,40}$/i.test(gitRef)
|
|
|
|
export const getProviderTypeMapping = (provider: GitProviders): string => {
|
|
switch (provider) {
|
|
case GitProviders.BITBUCKET_SERVER:
|
|
return 'stash'
|
|
case GitProviders.GITHUB_ENTERPRISE:
|
|
return 'github'
|
|
case GitProviders.GITLAB_SELF_HOSTED:
|
|
return 'gitlab'
|
|
default:
|
|
return provider.toLowerCase()
|
|
}
|
|
}
|
|
|
|
export const getOrgLabel = (gitProvider: string) => {
|
|
switch (gitProvider) {
|
|
case GitProviders.BITBUCKET:
|
|
return 'importRepo.workspace'
|
|
case GitProviders.BITBUCKET_SERVER:
|
|
return 'importRepo.project'
|
|
case GitProviders.GITLAB:
|
|
case GitProviders.GITLAB_SELF_HOSTED:
|
|
return 'importRepo.group'
|
|
default:
|
|
return 'importRepo.org'
|
|
}
|
|
}
|
|
|
|
export const getOrgPlaceholder = (gitProvider: string) => {
|
|
switch (gitProvider) {
|
|
case GitProviders.BITBUCKET:
|
|
return 'importRepo.workspacePlaceholder'
|
|
case GitProviders.BITBUCKET_SERVER:
|
|
return 'importRepo.projectPlaceholder'
|
|
case GitProviders.GITLAB:
|
|
case GitProviders.GITLAB_SELF_HOSTED:
|
|
return 'importRepo.groupPlaceholder'
|
|
default:
|
|
return 'importRepo.orgPlaceholder'
|
|
}
|
|
}
|
|
|
|
export const getProviders = () =>
|
|
Object.values(GitProviders).map(value => {
|
|
return { value, label: value }
|
|
})
|