Add auto commitAction to pushRepo action

Signed-off-by: Joao Pedro Goulart <joaopedromgoulart@gmail.com>
Signed-off-by: bi003731 <bi003731@inter.co>
This commit is contained in:
bi003731
2025-12-01 20:53:36 -03:00
parent c353de0b9b
commit f2d034b0ef
4 changed files with 100 additions and 52 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': minor
---
In the gitlabRepoPush action, add 'auto' possibility for commitAction input.
@@ -31,57 +31,11 @@ import {
import path from 'path';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { InputError } from '@backstage/errors';
import {
LoggerService,
resolveSafeChildPath,
} from '@backstage/backend-plugin-api';
import { resolveSafeChildPath } from '@backstage/backend-plugin-api';
import { createGitlabApi, getErrorMessage } from './helpers';
import { examples } from './gitlabMergeRequest.examples';
import { createHash } from 'crypto';
function computeSha256(file: SerializedFile): string {
const hash = createHash('sha256');
hash.update(file.content);
return hash.digest('hex');
}
async function getFileAction(
fileInfo: { file: SerializedFile; targetPath?: string },
target: { repoID: string; branch: string },
api: InstanceType<typeof Gitlab>,
logger: LoggerService,
remoteFiles: RepositoryTreeSchema[],
defaultCommitAction:
| 'create'
| 'delete'
| 'update'
| 'skip'
| 'auto' = 'auto',
): Promise<'create' | 'delete' | 'update' | 'skip'> {
if (defaultCommitAction === 'auto') {
const filePath = path.join(fileInfo.targetPath ?? '', fileInfo.file.path);
if (remoteFiles?.some(remoteFile => remoteFile.path === filePath)) {
try {
const targetFile = await api.RepositoryFiles.show(
target.repoID,
filePath,
target.branch,
);
if (computeSha256(fileInfo.file) === targetFile.content_sha256) {
return 'skip';
}
} catch (error) {
logger.warn(
`Unable to retrieve detailed information for remote file ${filePath}`,
);
}
return 'update';
}
return 'create';
}
return defaultCommitAction;
}
import { getFileAction } from '../util';
async function getReviewersFromApprovalRules(
api: InstanceType<typeof Gitlab>,
@@ -26,6 +26,9 @@ import {
import { CommitAction } from '@gitbeaker/rest';
import { createGitlabApi, getErrorMessage } from './helpers';
import { examples } from './gitlabRepoPush.examples';
import { getFileAction } from '../util';
import { SerializedFile } from '@backstage/plugin-scaffolder-node';
import { RepositoryTreeSchema } from '@gitbeaker/rest';
/**
* Create a new action that commits into a gitlab repository.
@@ -75,7 +78,7 @@ export const createGitlabRepoPushAction = (options: {
.optional(),
commitAction: z =>
z
.enum(['create', 'update', 'delete'], {
.enum(['create', 'update', 'delete', 'auto'], {
description:
'The action to be used for git commit. Defaults to create, but can be set to update or delete',
})
@@ -126,8 +129,44 @@ export const createGitlabRepoPushAction = (options: {
gitignore: true,
});
const actions: CommitAction[] = fileContents.map(file => ({
action: commitAction ?? 'create',
let remoteFiles: RepositoryTreeSchema[] = [];
if ((ctx.input.commitAction ?? 'auto') === 'auto') {
try {
remoteFiles = await api.Repositories.allRepositoryTrees(repoID, {
ref: branchName,
recursive: true,
path: targetPath ?? undefined,
});
} catch (e) {
ctx.logger.warn(
`Could not retrieve the list of files for ${repoID} (branch: ${branchName}) : ${getErrorMessage(
e,
)}`,
);
}
}
const actions: CommitAction[] = (
(
await Promise.all(
fileContents.map(async file => {
const action = await getFileAction(
{ file, targetPath },
{ repoID, branch: branchName },
api,
ctx.logger,
remoteFiles,
ctx.input.commitAction,
);
return { file, action };
}),
)
).filter(o => o.action !== 'skip') as {
file: SerializedFile;
action: CommitAction['action'];
}[]
).map(({ file, action }) => ({
action,
filePath: targetPath
? path.posix.join(targetPath, file.path)
: file.path,
@@ -14,15 +14,21 @@
* limitations under the License.
*/
import { LoggerService } from '@backstage/backend-plugin-api';
import { InputError } from '@backstage/errors';
import {
GitLabIntegration,
ScmIntegrationRegistry,
} from '@backstage/integration';
import { Gitlab, GroupSchema } from '@gitbeaker/rest';
import { Gitlab, GroupSchema, RepositoryTreeSchema } from '@gitbeaker/rest';
import { z } from 'zod';
import commonGitlabConfig from './commonGitlabConfig';
import { SerializedFile } from '@backstage/plugin-scaffolder-node';
import { createHash } from 'crypto';
import path from 'path';
export const parseRepoHost = (repoUrl: string): string => {
let parsed;
try {
@@ -184,3 +190,47 @@ export async function checkEpicScope(
throw new InputError(`Could not find epic scope: ${error.message}`);
}
}
function computeSha256(file: SerializedFile): string {
const hash = createHash('sha256');
hash.update(file.content);
return hash.digest('hex');
}
export async function getFileAction(
fileInfo: { file: SerializedFile; targetPath?: string },
target: { repoID: string; branch: string },
api: InstanceType<typeof Gitlab>,
logger: LoggerService,
remoteFiles: RepositoryTreeSchema[],
defaultCommitAction:
| 'create'
| 'delete'
| 'update'
| 'skip'
| 'auto' = 'auto',
): Promise<'create' | 'delete' | 'update' | 'skip'> {
if (defaultCommitAction === 'auto') {
const filePath = path.join(fileInfo.targetPath ?? '', fileInfo.file.path);
if (remoteFiles?.some(remoteFile => remoteFile.path === filePath)) {
try {
const targetFile = await api.RepositoryFiles.show(
target.repoID,
filePath,
target.branch,
);
if (computeSha256(fileInfo.file) === targetFile.content_sha256) {
return 'skip';
}
} catch (error) {
logger.warn(
`Unable to retrieve detailed information for remote file ${filePath}`,
);
}
return 'update';
}
return 'create';
}
return defaultCommitAction;
}