Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions plugins/gitlab/src/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export class GitlabApi extends PluginApi {
groupId: number
ciConfigPath?: string
}) {
console.log(`[GITLAB] createEmptyRepository ${repoName} ${groupId}`)
const project = await this.api.Projects.create({
name: repoName,
path: repoName,
Expand All @@ -67,6 +68,7 @@ export class GitlabApi extends PluginApi {
branch: string = 'main',
comment: string = 'ci: :robot_face: Update file content',
): Promise<boolean> {
console.log(`[GITLAB] commitCreateOrUpdate ${repoId} ${filePath} ${branch}`)
let action: CommitAction['action'] = 'create'

const existingBranch = await find(offsetPaginate(opts => this.api.Branches.all(repoId, opts)), b => b.name === branch)
Expand Down Expand Up @@ -109,6 +111,7 @@ export class GitlabApi extends PluginApi {
branch: string = 'main',
comment: string = 'ci: :robot_face: Delete files',
): Promise<boolean> {
console.log(`[GITLAB] commitDelete ${repoId} ${branch}`)
if (files.length) {
const commitActions: CommitAction[] = files.map((filePath) => {
return {
Expand Down Expand Up @@ -138,6 +141,7 @@ export class GitlabApi extends PluginApi {
}

public async commitFiles() {
console.log(`[GITLAB] commitFiles`)
let filesUpdated: number = 0
for (const [id, repo] of objectEntries(this.pendingCommits)) {
for (const [branch, details] of objectEntries(repo.branches)) {
Expand All @@ -153,6 +157,7 @@ export class GitlabApi extends PluginApi {
}

public async listFiles(repoId: number, options: AllRepositoryTreesOptions = {}) {
console.log(`[GITLAB] listFiles ${repoId}`)
options.path = options?.path ?? '/'
options.ref = options?.ref ?? 'main'
options.recursive = options?.recursive ?? false
Expand Down Expand Up @@ -182,6 +187,7 @@ export class GitlabApi extends PluginApi {
}

public async deleteRepository(repoId: number, fullPath: string) {
console.log(`[GITLAB] deleteRepository ${repoId} ${fullPath}`)
await this.api.Projects.remove(repoId) // Marks for deletion
return this.api.Projects.remove(repoId, { permanentlyRemove: true, fullPath: `${fullPath}-deletion_scheduled-${repoId}` }) // Effective deletion
}
Expand All @@ -197,6 +203,7 @@ export class GitlabZoneApi extends GitlabApi {

// Group Infra
public async getOrCreateInfraGroup(): Promise<GroupSchema> {
console.log(`[GITLAB] getOrCreateInfraGroup`)
const rootId = await getGroupRootId()
// Get or create projects_root_dir/infra group
const existingParentGroup = await find(offsetPaginate(opts => this.api.Groups.all({
Expand All @@ -214,6 +221,7 @@ export class GitlabZoneApi extends GitlabApi {
}

public async getOrCreateInfraProject(zone: string): Promise<ProjectSchema> {
console.log(`[GITLAB] getOrCreateInfraProject ${zone}`)
if (this.infraProjectsByZoneSlug.has(zone)) {
return this.infraProjectsByZoneSlug.get(zone)!
}
Expand Down Expand Up @@ -249,6 +257,7 @@ export class GitlabProjectApi extends GitlabApi {

// Group Project
private async createProjectGroup(): Promise<GroupSchema> {
console.log(`[GITLAB] createProjectGroup`)
const parentId = await getGroupRootId()
const existingGroup = await find(offsetPaginate(opts => this.api.Groups.all({
search: this.project.slug,
Expand All @@ -267,28 +276,36 @@ export class GitlabProjectApi extends GitlabApi {
}

public async getProjectGroup(): Promise<GroupSchema | undefined> {
if (this.gitlabGroup) return this.gitlabGroup
const parentId = await getGroupRootId()
this.gitlabGroup = await find(offsetPaginate(opts => this.api.Groups.allSubgroups(parentId, opts)), group => group.name === this.project.slug)
console.log(`[GITLAB] getProjectGroup`)
if (!this.gitlabGroup) {
console.log(`[GITLAB] No gitlab group defined in internal state`)
const parentId = await getGroupRootId()
this.gitlabGroup = await find(offsetPaginate(opts => this.api.Groups.allSubgroups(parentId, opts)), group => group.name === this.project.slug)
}
console.log(`[GITLAB] FOUND gitlabGroup`)
return this.gitlabGroup
}

public async getOrCreateProjectGroup(): Promise<GroupSchema> {
console.log(`[GITLAB] getOrCreateProjectGroup`)
const group = await this.getProjectGroup()
if (group) return group
return this.createProjectGroup()
}

public async getPublicGroupUrl() {
console.log(`[GITLAB] getPublicGroupUrl`)
return `${config().publicUrl}/${config().projectsRootDir}/${this.project.slug}`
}

public async getInternalGroupUrl() {
console.log(`[GITLAB] getInternalGroupUrl`)
return `${config().internalUrl}/${config().projectsRootDir}/${this.project.slug}`
}

// Tokens
public async getProjectMirrorCreds(vaultApi: VaultProjectApi): Promise<GitlabMirrorSecret> {
console.log(`[GITLAB] getProjectMirrorCreds`)
const tokenName = `${this.project.slug}-bot`
const currentToken = await this.getProjectToken(tokenName)
const creds: GitlabMirrorSecret = {
Expand Down Expand Up @@ -325,6 +342,7 @@ export class GitlabProjectApi extends GitlabApi {
}

public async getProjectId(projectName: string) {
console.log(`[GITLAB] getProjectId ${projectName}`)
const projectGroup = await this.getProjectGroup()
if (!projectGroup) throw new Error(`Gitlab inaccessible, impossible de trouver le groupe ${this.project.slug}`)

Expand All @@ -338,20 +356,24 @@ export class GitlabProjectApi extends GitlabApi {
}

public async getProjectById(projectId: number) {
console.log(`[GITLAB] getProjectById ${projectId}`)
return this.api.Projects.show(projectId)
}

public async getOrCreateInfraProject(zone: string) {
console.log(`[GITLAB] getOrCreateInfraGroup ${zone}`)
return await this.zoneApi.getOrCreateInfraProject(zone)
}

public async getProjectToken(tokenName: string) {
console.log(`[GITLAB] getProjectToken ${tokenName}`)
const group = await this.getProjectGroup()
if (!group) throw new Error('Unable to retrieve gitlab project group')
return find(offsetPaginate(opts => this.api.GroupAccessTokens.all(group.id, opts)), token => token.name === tokenName)
}

public async createProjectToken(tokenName: string, scopes: AccessTokenScopes[]) {
console.log(`[GITLAB] createProjectToken ${tokenName}`)
const group = await this.getProjectGroup()
if (!group) throw new Error('Unable to retrieve gitlab project group')
const expiryDate = new Date()
Expand All @@ -360,13 +382,15 @@ export class GitlabProjectApi extends GitlabApi {
}

public async revokeProjectToken(tokenId: number) {
console.log(`[GITLAB] revokeProjectToken ${tokenId}`)
const group = await this.getProjectGroup()
if (!group) throw new Error('Unable to retrieve gitlab project group')
return this.api.GroupAccessTokens.revoke(group.id, tokenId)
}

// Triggers
public async getMirrorProjectTriggerToken(vaultApi: VaultProjectApi) {
console.log(`[GITLAB] getMirrorProjectTriggerToken`)
const tokenDescription = 'mirroring-from-external-repo'
const gitlabRepositories = await this.listRepositories()
const mirrorRepo = gitlabRepositories.find(repo => repo.name === internalMirrorRepoName)
Expand All @@ -384,14 +408,17 @@ export class GitlabProjectApi extends GitlabApi {

// Repositories
public async getPublicRepoUrl(repoName: string) {
console.log(`[GITLAB] getPublicRepoUrl ${repoName}`)
return `${await this.getPublicGroupUrl()}/${repoName}.git`
}

public async getInternalRepoUrl(repoName: string) {
console.log(`[GITLAB] getInternalRepoUrl ${repoName}`)
return `${await this.getInternalGroupUrl()}/${repoName}.git`
}

public async listRepositories() {
console.log(`[GITLAB] listRepositories`)
const group = await this.getOrCreateProjectGroup()
const projects = await getAll(offsetPaginate(opts => this.api.Groups.allProjects(group.id, { simple: false, ...opts }))) // to refactor with https://github.com/jdalrymple/gitbeaker/pull/3624
return Promise.all(projects.map(async (project) => {
Expand All @@ -403,6 +430,7 @@ export class GitlabProjectApi extends GitlabApi {
}

public async createEmptyProjectRepository({ repoName, description, clone }: CreateEmptyRepositoryArgs & { clone?: boolean }) {
console.log(`[GITLAB] createEmptyProjectRepository ${repoName}`)
const namespaceId = (await this.getOrCreateProjectGroup()).id
return this.createEmptyRepository({
repoName,
Expand All @@ -415,43 +443,51 @@ export class GitlabProjectApi extends GitlabApi {

// Special Repositories
public async getSpecialRepositories(): Promise<string[]> {
console.log(`[GITLAB] getSpecialRepositories`)
return this.specialRepositories
}

public async addSpecialRepositories(name: string) {
console.log(`[GITLAB] addSpecialRepositories ${name}`)
if (!this.specialRepositories.includes(name)) {
this.specialRepositories.push(name)
}
}

// Group members
public async getGroupMembers() {
console.log(`[GITLAB] getGroupMembers`)
const group = await this.getOrCreateProjectGroup()
return getAll(offsetPaginate(opts => this.api.GroupMembers.all(group.id, opts)))
}

public async addGroupMember(userId: number, accessLevel: AccessLevelAllowed = AccessLevel.DEVELOPER): Promise<MemberSchema> {
console.log(`[GITLAB] addGroupMember ${userId} ${accessLevel}`)
const group = await this.getOrCreateProjectGroup()
return this.api.GroupMembers.add(group.id, userId, accessLevel)
}

public async editGroupMember(userId: number, accessLevel: AccessLevelAllowed = AccessLevel.DEVELOPER): Promise<MemberSchema> {
console.log(`[GITLAB] editGroupMember ${userId} ${accessLevel}`)
const group = await this.getOrCreateProjectGroup()
return this.api.GroupMembers.edit(group.id, userId, accessLevel)
}

public async removeGroupMember(userId: number) {
console.log(`[GITLAB] removeGroupMember ${userId}`)
const group = await this.getOrCreateProjectGroup()
return this.api.GroupMembers.remove(group.id, userId)
}

// CI Variables
public async getGitlabGroupVariables(): Promise<VariableSchema[]> {
console.log(`[GITLAB] getGitlabGroupVariables`)
const group = await this.getOrCreateProjectGroup()
return await getAll(offsetPaginate(opts => this.api.GroupVariables.all(group.id, opts)))
}

public async setGitlabGroupVariable(listVars: VariableSchema[], toSetVariable: VariableSchema): Promise<setVariableResult> {
console.log(`[GITLAB] setGitlabGroupVariable`)
const group = await this.getOrCreateProjectGroup()
const currentVariable = listVars.find(v => v.key === toSetVariable.key)
if (currentVariable) {
Expand Down Expand Up @@ -491,10 +527,12 @@ export class GitlabProjectApi extends GitlabApi {
}

public async getGitlabRepoVariables(repoId: number): Promise<VariableSchema[]> {
console.log(`[GITLAB] getGitlabRepoVariables ${repoId}`)
return await getAll(offsetPaginate(opts => this.api.ProjectVariables.all(repoId, opts)))
}

public async setGitlabRepoVariable(repoId: number, listVars: VariableSchema[], toSetVariable: ProjectVariableSchema): Promise<setVariableResult | 'repository not found'> {
console.log(`[GITLAB] setGitlabRepoVariables ${repoId}`)
const currentVariable = listVars.find(v => v.key === toSetVariable.key)
if (currentVariable) {
if (
Expand Down Expand Up @@ -535,6 +573,7 @@ export class GitlabProjectApi extends GitlabApi {

// Mirror
public async triggerMirror(targetRepo: string, syncAllBranches: boolean, branchName?: string) {
console.log(`[GITLAB] triggerMirror ${targetRepo} ${syncAllBranches} ${branchName}`)
if ((await this.getSpecialRepositories()).includes(targetRepo)) {
throw new Error('User requested for invalid mirroring')
}
Expand Down
8 changes: 7 additions & 1 deletion plugins/gitlab/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@ let groupRootId: number
export async function getGroupRootId(throwIfNotFound?: true): Promise<number>
export async function getGroupRootId(throwIfNotFound?: false): Promise<number | undefined>
export async function getGroupRootId(throwIfNotFound?: boolean): Promise<number | undefined> {
console.log(`[GITLAB] getGroupRootId`)
const gitlabApi = getApi()
const projectRootDir = config().projectsRootDir
console.log(`[GITLAB] projectRootDir is ${projectRootDir}`)
if (groupRootId) return groupRootId
const groupRoot = await find(offsetPaginate(opts => gitlabApi.Groups.all({
search: projectRootDir,
orderBy: 'id',
...opts,
})), grp => grp.full_path === projectRootDir)
console.log(`[GITLAB] groupRoot is ${JSON.stringify(groupRoot)}`)
const searchId = groupRoot?.id
if (typeof searchId === 'undefined') {
if (throwIfNotFound) {
throw new Error(`Gitlab inaccessible, impossible de trouver le groupe ${projectRootDir}`)
throw new Error(`Gitlab inaccessible, impossible de trouver le groupe RACINE ${projectRootDir}`)
}
return searchId
}
Expand All @@ -30,6 +33,7 @@ export async function getGroupRootId(throwIfNotFound?: boolean): Promise<number
}

async function createGroupRoot(): Promise<number> {
console.log(`[GITLAB] createGroupRoot`)
const gitlabApi = getApi()
const projectRootDir = config().projectsRootDir
const projectRootDirArray = projectRootDir.split('/')
Expand Down Expand Up @@ -65,10 +69,12 @@ async function createGroupRoot(): Promise<number> {
}

export async function getOrCreateGroupRoot(): Promise<number> {
console.log(`[GITLAB] getOrCreateGroupRoot`)
return await getGroupRootId(false) ?? createGroupRoot()
}

export function getApi(): IGitlab {
console.log(`[GITLAB] getApi`)
api ??= new Gitlab({ token: config().token, host: config().internalUrl })
return api
}
Expand Down
5 changes: 5 additions & 0 deletions plugins/harbor/src/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from './utils.js'

export const createDsoProject: StepCall<Project> = async (payload) => {
console.log(`[HARBOR] createDsoProject`)
const returnResult: PluginResult = {
status: {
result: 'OK',
Expand Down Expand Up @@ -78,6 +79,7 @@ export const createDsoProject: StepCall<Project> = async (payload) => {
}
return returnResult
} catch (error) {
console.log(`[HARBOR] Error while creating DSO project`)
return {
error: parseError(error),
status: {
Expand All @@ -89,6 +91,7 @@ export const createDsoProject: StepCall<Project> = async (payload) => {
}

export const deleteDsoProject: StepCall<Project> = async (payload) => {
console.log(`[HARBOR] deleteDsoProject`)
try {
const project = payload.args
const projectName = project.slug
Expand All @@ -102,6 +105,7 @@ export const deleteDsoProject: StepCall<Project> = async (payload) => {
},
}
} catch (error) {
console.log(`[HARBOR] Error while deleting DSO project`)
return {
error: parseError(error),
status: {
Expand All @@ -117,6 +121,7 @@ export const getProjectSecrets: StepCall<ProjectLite> = async ({
apis: { vault: vaultApi },
config,
}) => {
console.log(`[HARBOR] getProjectSecrets`)
const publishRoRobotProject = project.store.registry?.publishProjectRobot
const publishRoRobotConfig = config.registry?.publishProjectRobot
const projectRobotEnabled
Expand Down
1 change: 1 addition & 0 deletions plugins/harbor/src/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Project as HarborProject, Quota } from './api/Api.js'
import { getApi } from './utils.js'

export async function createProject(projectName: string, storageLimit: number = -1): Promise<HarborProject> {
console.log(`[HARBOR] createProject`)
const api = getApi()
const existingProject = await api.projects.getProject(projectName, {
headers: {
Expand Down
6 changes: 6 additions & 0 deletions plugins/harbor/src/robot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface VaultRobotSecret {
export const getRobot = async (projectName: string, robotName: string, api: HarborApi) => getRobotByName(projectName, `robot$${projectName}+${robotName}`, api)

export async function ensureRobot(projectName: string, robotName: string, vaultApi: VaultProjectApi, access: Access[], api: HarborApi): Promise<VaultRobotSecret> {
console.log(`[HARBOR] ensureRobot ${projectName} ${robotName}`)
const vaultPath = `REGISTRY/${robotName}`
const robot = await getRobot(projectName, robotName, api)
const vaultRobotSecret = await vaultApi.read(vaultPath, { throwIfNoEntry: false }) as { data: VaultRobotSecret } | undefined
Expand All @@ -35,6 +36,7 @@ export async function ensureRobot(projectName: string, robotName: string, vaultA
}

export async function deleteRobot(projectName: string, robotName: string, vaultApi: VaultProjectApi, api: HarborApi) {
console.log(`[HARBOR] deleteRobot ${projectName} ${robotName}`)
const vaultPath = `REGISTRY/${robotName}`
const robot = await getRobot(projectName, robotName, api)
if (robot?.id) {
Expand All @@ -48,22 +50,26 @@ export async function deleteRobot(projectName: string, robotName: string, vaultA
}

export async function createRobot(projectName: string, robotName: string, access: Access[], api: HarborApi) {
console.log(`[HARBOR] createRobot ${projectName} ${robotName}`)
return (await api.robots.createRobot(getRobotPermissions(projectName, robotName, access))).data
}

export async function regenerateRobot(projectName: string, robotName: string, access: Access[], api: HarborApi) {
console.log(`[HARBOR] regenerateRobot ${projectName} ${robotName}`)
const robot = await getRobot(projectName, robotName, api)
if (robot?.id)
await api.projects.deleteRobotV1(projectName, robot.id)
return createRobot(projectName, robotName, access, api)
}

export async function getRobotByName(project: string | number, robotName: string, api: HarborApi): Promise<Robot | undefined> {
console.log(`[HARBOR] getRobotByName ${project} ${robotName}`)
const listRobots = await api.projects.listRobotV1(String(project))
return listRobots.data.find(({ name }) => name === robotName)
}

function getRobotPermissions(projectName: string, robotName: string, access: Access[]) {
console.log(`[HARBOR] getRobotPermissions ${projectName} ${robotName}`)
return {
name: robotName,
duration: -1,
Expand Down
Loading