Merge pull request #17233 from lukasfruntke-extern/feature/add-commit-hash-for-scaffolder

Add Commit Hash Output for Scaffolder SCM Actions
This commit is contained in:
Ben Lambert
2023-04-17 21:15:11 +02:00
committed by GitHub
26 changed files with 174 additions and 32 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend': minor
---
The non-PR/MR Git Actions now return the commit hash of the commit pushed as a new output called `commitHash`, isomorphic-git is now on version 1.23.0
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
The dependency isomorphic-git is now on version 1.23.0
+1 -1
View File
@@ -82,7 +82,7 @@
"fs-extra": "10.1.0",
"git-url-parse": "^13.0.0",
"helmet": "^6.0.0",
"isomorphic-git": "^1.8.0",
"isomorphic-git": "^1.23.0",
"jose": "^4.6.0",
"keyv": "^4.5.2",
"knex": "^2.0.0",
-1
View File
@@ -111,7 +111,6 @@ export class Git {
this.config.logger?.info(
`Committing file to repo {dir=${dir},message=${message}}`,
);
return git.commit({ fs, dir, message, author, committer });
}
+1 -1
View File
@@ -78,7 +78,7 @@
"git-url-parse": "^13.0.0",
"globby": "^11.0.0",
"isbinaryfile": "^5.0.0",
"isomorphic-git": "^1.8.0",
"isomorphic-git": "^1.23.0",
"jsonschema": "^1.2.6",
"knex": "^2.0.0",
"lodash": "^4.17.21",
@@ -14,10 +14,30 @@
* limitations under the License.
*/
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
const mockGit = {
init: jest.fn(),
add: jest.fn(),
checkout: jest.fn(),
commit: jest
.fn()
.mockResolvedValue('220f19cc36b551763d157f1b5e4a4b446165dbd6'),
fetch: jest.fn(),
addRemote: jest.fn(),
push: jest.fn(),
};
jest.mock('@backstage/backend-common', () => ({
Git: {
fromAuth() {
return mockGit;
},
},
getVoidLogger: jest.requireActual('@backstage/backend-common').getVoidLogger,
}));
jest.mock('../helpers');
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import {
@@ -32,6 +52,10 @@ import {
} from '../helpers';
import { createGithubRepoPushAction } from './githubRepoPush';
const initRepoAndPushMocked = initRepoAndPush as jest.Mock<
Promise<{ commitHash: string }>
>;
const mockOctokit = {
rest: {
repos: {
@@ -77,6 +101,9 @@ describe('github:repo:push', () => {
beforeEach(() => {
jest.resetAllMocks();
initRepoAndPushMocked.mockResolvedValue({ commitHash: 'test123' });
githubCredentialsProvider =
DefaultGithubCredentialsProvider.fromIntegrations(integrations);
action = createGithubRepoPushAction({
@@ -107,6 +107,7 @@ export function createGithubRepoPushAction(options: {
properties: {
remoteUrl: outputProps.remoteUrl,
repoContentsUrl: outputProps.repoContentsUrl,
commitHash: outputProps.commitHash,
},
},
},
@@ -151,7 +152,7 @@ export function createGithubRepoPushAction(options: {
const remoteUrl = targetRepo.data.clone_url;
const repoContentsUrl = `${targetRepo.data.html_url}/blob/${defaultBranch}`;
await initRepoPushAndProtect(
const { commitHash } = await initRepoPushAndProtect(
remoteUrl,
octokitOptions.auth,
ctx.workspacePath,
@@ -180,6 +181,7 @@ export function createGithubRepoPushAction(options: {
ctx.output('remoteUrl', remoteUrl);
ctx.output('repoContentsUrl', repoContentsUrl);
ctx.output('commitHash', commitHash);
},
});
}
@@ -294,7 +294,7 @@ export async function initRepoPushAndProtect(
gitAuthorEmail?: string,
dismissStaleReviews?: boolean,
requiredCommitSigning?: boolean,
) {
): Promise<{ commitHash: string }> {
const gitAuthorInfo = {
name: gitAuthorName
? gitAuthorName
@@ -308,7 +308,7 @@ export async function initRepoPushAndProtect(
? gitCommitMessage
: config.getOptionalString('scaffolder.defaultCommitMessage');
await initRepoAndPush({
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(workspacePath, sourcePath),
remoteUrl,
defaultBranch,
@@ -347,6 +347,8 @@ export async function initRepoPushAndProtect(
);
}
}
return { commitHash: commitResult.commitHash };
}
function extractCollaboratorName(
@@ -23,5 +23,11 @@ const repoContentsUrl = {
type: 'string',
};
const commitHash = {
title: 'The git commit hash of the initial commit',
type: 'string',
};
export { remoteUrl };
export { repoContentsUrl };
export { commitHash };
@@ -23,7 +23,9 @@ jest.mock('@backstage/backend-common', () => ({
init: jest.fn(),
add: jest.fn(),
checkout: jest.fn(),
commit: jest.fn(),
commit: jest
.fn()
.mockResolvedValue('220f19cc36b551763d157f1b5e4a4b446165dbd6'),
fetch: jest.fn(),
addRemote: jest.fn(),
push: jest.fn(),
@@ -91,7 +91,7 @@ export async function initRepoAndPush({
defaultBranch?: string;
commitMessage?: string;
gitAuthorInfo?: { name?: string; email?: string };
}): Promise<void> {
}): Promise<{ commitHash: string }> {
const git = Git.fromAuth({
...auth,
logger,
@@ -110,13 +110,12 @@ export async function initRepoAndPush({
email: gitAuthorInfo?.email ?? 'scaffolder@backstage.io',
};
await git.commit({
const commitHash = await git.commit({
dir,
message: commitMessage,
author: authorInfo,
committer: authorInfo,
});
await git.addRemote({
dir,
url: remoteUrl,
@@ -127,6 +126,8 @@ export async function initRepoAndPush({
dir,
remote: 'origin',
});
return { commitHash };
}
export async function commitAndPushRepo({
@@ -148,7 +149,7 @@ export async function commitAndPushRepo({
gitAuthorInfo?: { name?: string; email?: string };
branch?: string;
remoteRef?: string;
}): Promise<void> {
}): Promise<{ commitHash: string }> {
const git = Git.fromAuth({
...auth,
logger,
@@ -164,7 +165,7 @@ export async function commitAndPushRepo({
email: gitAuthorInfo?.email ?? 'scaffolder@backstage.io',
};
await git.commit({
const commitHash = await git.commit({
dir,
message: commitMessage,
author: authorInfo,
@@ -176,6 +177,8 @@ export async function commitAndPushRepo({
remote: 'origin',
remoteRef: remoteRef ?? `refs/heads/${branch}`,
});
return { commitHash };
}
type BranchProtectionOptions = {
@@ -19,7 +19,16 @@ jest.mock('azure-devops-node-api', () => ({
getPersonalAccessTokenHandler: jest.fn().mockReturnValue(() => {}),
}));
jest.mock('../helpers');
jest.mock('../helpers', () => {
return {
initRepoAndPush: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
commitAndPushRepo: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
};
});
import { createPublishAzureAction } from './azure';
import { ScmIntegrations } from '@backstage/integration';
@@ -108,6 +108,10 @@ export function createPublishAzureAction(options: {
title: 'The Id of the created repository',
type: 'string',
},
commitHash: {
title: 'The git commit hash of the initial commit',
type: 'string',
},
},
},
},
@@ -183,7 +187,7 @@ export function createPublishAzureAction(options: {
: config.getOptionalString('scaffolder.defaultAuthor.email'),
};
await initRepoAndPush({
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
remoteUrl,
defaultBranch,
@@ -198,6 +202,7 @@ export function createPublishAzureAction(options: {
gitAuthorInfo,
});
ctx.output('commitHash', commitResult?.commitHash);
ctx.output('remoteUrl', remoteUrl);
ctx.output('repoContentsUrl', repoContentsUrl);
ctx.output('repositoryId', repositoryId);
@@ -14,7 +14,16 @@
* limitations under the License.
*/
jest.mock('../helpers');
jest.mock('../helpers', () => {
return {
initRepoAndPush: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
commitAndPushRepo: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
};
});
import { createPublishBitbucketAction } from './bitbucket';
import { rest } from 'msw';
@@ -289,6 +289,10 @@ export function createPublishBitbucketAction(options: {
title: 'A URL to the root of the repository',
type: 'string',
},
commitHash: {
title: 'The git commit hash of the initial commit',
type: 'string',
},
},
},
},
@@ -391,7 +395,7 @@ export function createPublishBitbucketAction(options: {
};
}
await initRepoAndPush({
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
remoteUrl,
auth,
@@ -407,6 +411,7 @@ export function createPublishBitbucketAction(options: {
await performEnableLFS({ authorization, host, project, repo });
}
ctx.output('commitHash', commitResult?.commitHash);
ctx.output('remoteUrl', remoteUrl);
ctx.output('repoContentsUrl', repoContentsUrl);
},
@@ -14,7 +14,16 @@
* limitations under the License.
*/
jest.mock('../helpers');
jest.mock('../helpers', () => {
return {
initRepoAndPush: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
commitAndPushRepo: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
};
});
import { createPublishBitbucketCloudAction } from './bitbucketCloud';
import { rest } from 'msw';
@@ -182,6 +182,10 @@ export function createPublishBitbucketCloudAction(options: {
title: 'A URL to the root of the repository',
type: 'string',
},
commitHash: {
title: 'The git commit hash of the initial commit',
type: 'string',
},
},
},
},
@@ -262,7 +266,7 @@ export function createPublishBitbucketCloudAction(options: {
};
}
await initRepoAndPush({
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
remoteUrl,
auth,
@@ -274,6 +278,7 @@ export function createPublishBitbucketCloudAction(options: {
gitAuthorInfo,
});
ctx.output('commitHash', commitResult?.commitHash);
ctx.output('remoteUrl', remoteUrl);
ctx.output('repoContentsUrl', repoContentsUrl);
},
@@ -14,7 +14,16 @@
* limitations under the License.
*/
jest.mock('../helpers');
jest.mock('../helpers', () => {
return {
initRepoAndPush: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
commitAndPushRepo: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
};
});
import { createPublishBitbucketServerAction } from './bitbucketServer';
import { rest } from 'msw';
@@ -205,6 +205,10 @@ export function createPublishBitbucketServerAction(options: {
title: 'A URL to the root of the repository',
type: 'string',
},
commitHash: {
title: 'The git commit hash of the initial commit',
type: 'string',
},
},
},
},
@@ -279,7 +283,7 @@ export function createPublishBitbucketServerAction(options: {
password: authConfig.password!,
};
await initRepoAndPush({
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
remoteUrl,
auth,
@@ -295,6 +299,7 @@ export function createPublishBitbucketServerAction(options: {
await performEnableLFS({ authorization, host, project, repo });
}
ctx.output('commitHash', commitResult?.commitHash);
ctx.output('remoteUrl', remoteUrl);
ctx.output('repoContentsUrl', repoContentsUrl);
},
@@ -14,7 +14,16 @@
* limitations under the License.
*/
jest.mock('../helpers');
jest.mock('../helpers', () => {
return {
initRepoAndPush: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
commitAndPushRepo: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
};
});
import path from 'path';
import { createPublishGerritAction } from './gerrit';
@@ -149,6 +149,10 @@ export function createPublishGerritAction(options: {
title: 'A URL to the root of the repository',
type: 'string',
},
commitHash: {
title: 'The git commit hash of the initial commit',
type: 'string',
},
},
},
},
@@ -201,7 +205,7 @@ export function createPublishGerritAction(options: {
};
const remoteUrl = `${integrationConfig.config.cloneUrl}/a/${repo}`;
await initRepoAndPush({
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath),
remoteUrl,
auth,
@@ -213,6 +217,7 @@ export function createPublishGerritAction(options: {
const repoContentsUrl = `${integrationConfig.config.gitilesBaseUrl}/${repo}/+/refs/heads/${defaultBranch}`;
ctx.output('remoteUrl', remoteUrl);
ctx.output('commitHash', commitResult?.commitHash);
ctx.output('repoContentsUrl', repoContentsUrl);
},
});
@@ -13,11 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
jest.mock('../helpers');
import { TemplateAction } from '@backstage/plugin-scaffolder-node';
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import {
@@ -34,6 +32,10 @@ import {
} from '../helpers';
import { createPublishGithubAction } from './github';
const initRepoAndPushMocked = initRepoAndPush as jest.Mock<
Promise<{ commitHash: string }>
>;
const mockOctokit = {
rest: {
users: {
@@ -90,6 +92,9 @@ describe('publish:github', () => {
};
beforeEach(() => {
initRepoAndPushMocked.mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
});
githubCredentialsProvider =
DefaultGithubCredentialsProvider.fromIntegrations(integrations);
action = createPublishGithubAction({
@@ -156,6 +156,7 @@ export function createPublishGithubAction(options: {
properties: {
remoteUrl: outputProps.remoteUrl,
repoContentsUrl: outputProps.repoContentsUrl,
commitHash: outputProps.commitHash,
},
},
},
@@ -236,7 +237,7 @@ export function createPublishGithubAction(options: {
const remoteUrl = newRepo.clone_url;
const repoContentsUrl = `${newRepo.html_url}/blob/${defaultBranch}`;
await initRepoPushAndProtect(
const commitResult = await initRepoPushAndProtect(
remoteUrl,
octokitOptions.auth,
ctx.workspacePath,
@@ -263,6 +264,7 @@ export function createPublishGithubAction(options: {
requiredCommitSigning,
);
ctx.output('commitHash', commitResult?.commitHash);
ctx.output('remoteUrl', remoteUrl);
ctx.output('repoContentsUrl', repoContentsUrl);
},
@@ -14,7 +14,16 @@
* limitations under the License.
*/
jest.mock('../helpers');
jest.mock('../helpers', () => {
return {
initRepoAndPush: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
commitAndPushRepo: jest.fn().mockResolvedValue({
commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6',
}),
};
});
import { createPublishGitlabAction } from './gitlab';
import { ScmIntegrations } from '@backstage/integration';
@@ -125,6 +125,10 @@ export function createPublishGitlabAction(options: {
title: 'The ID of the project',
type: 'string',
},
commitHash: {
title: 'The git commit hash of the initial commit',
type: 'string',
},
},
},
},
@@ -212,7 +216,7 @@ export function createPublishGitlabAction(options: {
? gitAuthorEmail
: config.getOptionalString('scaffolder.defaultAuthor.email'),
};
await initRepoAndPush({
const commitResult = await initRepoAndPush({
dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath),
remoteUrl: http_url_to_repo as string,
defaultBranch,
@@ -227,6 +231,7 @@ export function createPublishGitlabAction(options: {
gitAuthorInfo,
});
ctx.output('commitHash', commitResult?.commitHash);
ctx.output('remoteUrl', remoteUrl);
ctx.output('repoContentsUrl', repoContentsUrl);
ctx.output('projectId', projectId);
+3 -3
View File
@@ -3567,7 +3567,7 @@ __metadata:
git-url-parse: ^13.0.0
helmet: ^6.0.0
http-errors: ^2.0.0
isomorphic-git: ^1.8.0
isomorphic-git: ^1.23.0
jose: ^4.6.0
keyv: ^4.5.2
knex: ^2.0.0
@@ -8171,7 +8171,7 @@ __metadata:
git-url-parse: ^13.0.0
globby: ^11.0.0
isbinaryfile: ^5.0.0
isomorphic-git: ^1.8.0
isomorphic-git: ^1.23.0
jest-when: ^3.1.0
jsonschema: ^1.2.6
knex: ^2.0.0
@@ -27394,7 +27394,7 @@ __metadata:
languageName: node
linkType: hard
"isomorphic-git@npm:^1.8.0":
"isomorphic-git@npm:^1.23.0":
version: 1.23.0
resolution: "isomorphic-git@npm:1.23.0"
dependencies: