Merge branch 'master' into canon-tables

This commit is contained in:
Charles de Dreuille
2024-12-16 10:24:07 +00:00
18 changed files with 257 additions and 68 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-github': patch
---
adding requiredLinearHistory property for branch protection settings
@@ -53,7 +53,7 @@ describe('createRouter readonly disabled', () => {
let locationAnalyzer: jest.Mocked<LocationAnalyzer>;
let permissionsService: jest.Mocked<PermissionsService>;
beforeAll(async () => {
beforeEach(async () => {
entitiesCatalog = {
entities: jest.fn(),
entitiesBatch: jest.fn(),
@@ -135,6 +135,38 @@ describe('createRouter readonly disabled', () => {
{ apiVersion: 'a', kind: 'b', metadata: { name: 'n' } },
];
entitiesCatalog.entities.mockResolvedValueOnce({
entities: { type: 'object', entities: [entities[0]] },
pageInfo: { hasNextPage: false },
});
const response = await request(app).get('/entities');
expect(response.status).toEqual(200);
expect(response.body).toEqual(entities);
});
it('happy path: lists entities when by-entities emulation is enabled', async () => {
const router = await createRouter({
entitiesCatalog,
locationService,
orchestrator,
logger: mockServices.logger.mock(),
refreshService,
config: new ConfigReader(undefined),
permissionIntegrationRouter: express.Router(),
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
locationAnalyzer,
permissionsService: permissionsService,
disableRelationsCompatibility: true, // added
});
app = await wrapServer(express().use(router));
const entities: Entity[] = [
{ apiVersion: 'a', kind: 'b', metadata: { name: 'n' } },
];
entitiesCatalog.queryEntities.mockResolvedValueOnce({
items: { type: 'object', entities: [entities[0]] },
pageInfo: {},
@@ -148,6 +180,49 @@ describe('createRouter readonly disabled', () => {
});
it('parses single and multiple request parameters and passes them down', async () => {
entitiesCatalog.entities.mockResolvedValueOnce({
entities: { type: 'object', entities: [] },
pageInfo: { hasNextPage: false },
});
const response = await request(app).get(
'/entities?filter=a=1,a=2,b=3&filter=c=4',
);
expect(response.status).toEqual(200);
expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1);
expect(entitiesCatalog.entities).toHaveBeenCalledWith({
filter: {
anyOf: [
{
allOf: [
{ key: 'a', values: ['1', '2'] },
{ key: 'b', values: ['3'] },
],
},
{ key: 'c', values: ['4'] },
],
},
credentials: mockCredentials.user(),
});
});
it('parses single and multiple request parameters and passes them down when by-entities emulation is enabled', async () => {
const router = await createRouter({
entitiesCatalog,
locationService,
orchestrator,
logger: mockServices.logger.mock(),
refreshService,
config: new ConfigReader(undefined),
permissionIntegrationRouter: express.Router(),
auth: mockServices.auth(),
httpAuth: mockServices.httpAuth(),
locationAnalyzer,
permissionsService: permissionsService,
disableRelationsCompatibility: true, // added
});
app = await wrapServer(express().use(router));
entitiesCatalog.queryEntities.mockResolvedValueOnce({
items: { type: 'object', entities: [] },
pageInfo: {},
@@ -43,7 +43,6 @@ import { LocationService, RefreshService } from './types';
import {
disallowReadonlyMode,
encodeCursor,
expandLegacyCompoundRelationsInEntity,
locationInput,
validateRequestBody,
} from './util';
@@ -58,10 +57,8 @@ import {
} from '@backstage/backend-plugin-api';
import { LocationAnalyzer } from '@backstage/plugin-catalog-node';
import { AuthorizedValidationService } from './AuthorizedValidationService';
import { DeferredPromise, createDeferred } from '@backstage/types';
import {
createEntityArrayJsonStream,
processEntitiesResponseItems,
writeEntitiesResponse,
writeSingleEntityResponse,
} from './response';
@@ -154,7 +151,7 @@ export async function createRouter(
// When pagination parameters are passed in, use the legacy slow path
// that loads all entities into memory
if (pagination) {
if (pagination || disableRelationsCompatibility !== true) {
const { entities, pageInfo } = await entitiesCatalog.entities({
filter,
fields,
@@ -175,22 +172,6 @@ export async function createRouter(
return;
}
// For other read-the-entire-world cases, use queryEntities and stream
// out results.
// The write lock is used for back pressure, preventing slow readers
// from forcing our read loop to pile up response data in userspace
// buffers faster than the kernel buffer is emptied.
// https://nodejs.org/api/http.html#http_response_write_chunk_encoding_callback
const locks: { writeLock?: DeferredPromise } = {};
const controller = new AbortController();
const signal = controller.signal;
req.on('end', () => {
controller.abort(new Error('Client closed connection'));
locks.writeLock?.resolve();
delete locks.writeLock;
});
const responseStream = createEntityArrayJsonStream(res);
const limit = 10000;
let cursor: Cursor | undefined;
@@ -211,31 +192,11 @@ export async function createRouter(
);
if (result.items.entities.length) {
await locks?.writeLock;
signal.throwIfAborted();
if (!disableRelationsCompatibility) {
result.items = processEntitiesResponseItems(
result.items,
expandLegacyCompoundRelationsInEntity,
);
}
if (!responseStream.send(result.items)) {
// The kernel buffer is full. Create the lock but do not await it
// yet - we can better spend our time going to the next round of
// the loop and read from the database while we wait for it to
// drain.
locks.writeLock = createDeferred();
res.once('drain', () => {
locks.writeLock?.resolve();
delete locks.writeLock;
});
if (await responseStream.send(result.items)) {
return; // Client closed connection
}
}
signal.throwIfAborted();
cursor = result.pageInfo?.nextCursor;
} while (cursor);
@@ -16,9 +16,10 @@
import { EntitiesResponseItems } from '../../catalog/types';
import { Response } from 'express';
import { writeResponseData } from './write';
export interface EntityArrayJsonStream {
send(entities: EntitiesResponseItems): boolean;
send(entities: EntitiesResponseItems): Promise<boolean>;
complete(): void;
close(): void;
}
@@ -33,7 +34,7 @@ export function createEntityArrayJsonStream(
let completed = false;
return {
send(response) {
async send(response) {
if (firstSend) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.status(200);
@@ -41,13 +42,15 @@ export function createEntityArrayJsonStream(
}
if (response.type === 'raw') {
let needsDrain = false;
for (const item of response.entities) {
const prefix = firstSend ? '[' : ',';
firstSend = false;
needsDrain ||= !res.write(prefix + item, 'utf8');
if (await writeResponseData(res, prefix + item)) {
return true;
}
}
return !needsDrain;
return false;
}
let data: string;
@@ -60,7 +63,7 @@ export function createEntityArrayJsonStream(
}
firstSend = false;
return res.write(data, 'utf8');
return writeResponseData(res, data);
},
complete() {
if (firstSend) {
@@ -78,26 +78,40 @@ export async function writeEntitiesResponse(
const prefix = first ? '[' : ',';
first = false;
const needsDrain = !res.write(prefix + entity, 'utf8');
if (needsDrain) {
const closed = await new Promise<boolean>(resolve => {
function onContinue() {
res.off('drain', onContinue);
res.off('close', onClose);
resolve(false);
}
function onClose() {
res.off('drain', onContinue);
res.off('close', onClose);
resolve(true);
}
res.on('drain', onContinue);
res.on('close', onClose);
});
if (closed) {
return;
}
if (await writeResponseData(res, prefix + entity)) {
return;
}
}
res.end(`${first ? '[' : ''}]${trailing}`);
}
/**
* Writes a data to the response and waits if the response buffer needs draining.
*
* @internal
* @returns true if the response was closed while waiting for the buffer to drain
*/
export async function writeResponseData(res: Response, data: string | Buffer) {
const ok = res.write(data, 'utf8');
if (!ok) {
if (!res.writableNeedDrain) {
return true;
}
const closed = await new Promise<boolean>(resolve => {
function onContinue() {
res.off('drain', onContinue);
res.off('close', onClose);
resolve(false);
}
function onClose() {
res.off('drain', onContinue);
res.off('close', onClose);
resolve(true);
}
res.on('drain', onContinue);
res.on('close', onClose);
});
return closed;
}
return false;
}
@@ -80,6 +80,7 @@ export function createGithubBranchProtectionAction(options: {
requiredConversationResolution?: boolean | undefined;
requireLastPushApproval?: boolean | undefined;
requiredCommitSigning?: boolean | undefined;
requiredLinearHistory?: boolean | undefined;
token?: string | undefined;
},
JsonObject
@@ -272,6 +273,7 @@ export function createGithubRepoCreateAction(options: {
}
| undefined;
requireCommitSigning?: boolean | undefined;
requiredLinearHistory?: boolean | undefined;
customProperties?:
| {
[key: string]: string;
@@ -319,6 +321,7 @@ export function createGithubRepoPushAction(options: {
sourcePath?: string | undefined;
token?: string | undefined;
requiredCommitSigning?: boolean | undefined;
requiredLinearHistory?: boolean | undefined;
requireLastPushApproval?: boolean | undefined;
},
JsonObject
@@ -432,6 +435,7 @@ export function createPublishGithubAction(options: {
}
| undefined;
requiredCommitSigning?: boolean | undefined;
requiredLinearHistory?: boolean | undefined;
customProperties?:
| {
[key: string]: string;
@@ -43,6 +43,7 @@ type BranchProtectionOptions = {
enforceAdmins?: boolean;
dismissStaleReviews?: boolean;
requiredCommitSigning?: boolean;
requiredLinearHistory?: boolean;
};
export const enableBranchProtectionOnDefaultRepoBranch = async ({
@@ -62,6 +63,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({
enforceAdmins = true,
dismissStaleReviews = false,
requiredCommitSigning = false,
requiredLinearHistory = false,
}: BranchProtectionOptions): Promise<void> => {
const tryOnce = async () => {
try {
@@ -93,6 +95,7 @@ export const enableBranchProtectionOnDefaultRepoBranch = async ({
require_last_push_approval: requireLastPushApproval,
},
required_conversation_resolution: requiredConversationResolution,
required_linear_history: requiredLinearHistory,
});
if (requiredCommitSigning) {
@@ -1097,6 +1097,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1126,6 +1127,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1154,6 +1156,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1181,6 +1184,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
});
@@ -1269,6 +1273,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1302,6 +1307,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1335,6 +1341,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1370,6 +1377,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1405,6 +1413,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1440,6 +1449,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
});
it('should call enableBranchProtectionOnDefaultRepoBranch with the correct values of bypassPullRequestAllowances', async () => {
@@ -1472,6 +1482,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1503,6 +1514,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1534,6 +1546,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1565,6 +1578,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1600,6 +1614,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
await action.handler({
@@ -1635,6 +1650,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
});
});
@@ -1664,6 +1680,11 @@ describe('publish:github', () => {
defaultValue: false,
overrideValue: true,
},
{
inputProperty: 'requiredLinearHistory',
defaultValue: false,
overrideValue: true,
},
{
inputProperty: 'protectEnforceAdmins',
defaultValue: true,
@@ -1712,6 +1733,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
[octokitParameter || inputProperty]: defaultValue,
});
@@ -1740,6 +1762,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
[octokitParameter || inputProperty]: overrideValue,
});
@@ -1768,6 +1791,7 @@ describe('publish:github', () => {
enforceAdmins: true,
dismissStaleReviews: false,
requiredCommitSigning: false,
requiredLinearHistory: false,
[octokitParameter || inputProperty]: defaultValue,
});
},
@@ -115,6 +115,7 @@ export function createPublishGithubAction(options: {
includeClaimKeys?: string[];
};
requiredCommitSigning?: boolean;
requiredLinearHistory?: boolean;
customProperties?: { [key: string]: string };
}>({
id: 'publish:github',
@@ -165,6 +166,7 @@ export function createPublishGithubAction(options: {
secrets: inputProps.secrets,
oidcCustomization: inputProps.oidcCustomization,
requiredCommitSigning: inputProps.requiredCommitSigning,
requiredLinearHistory: inputProps.requiredLinearHistory,
customProperties: inputProps.customProperties,
},
},
@@ -217,6 +219,7 @@ export function createPublishGithubAction(options: {
token: providedToken,
customProperties,
requiredCommitSigning = false,
requiredLinearHistory = false,
} = ctx.input;
const octokitOptions = await getOctokitOptions({
@@ -289,6 +292,7 @@ export function createPublishGithubAction(options: {
gitAuthorEmail,
dismissStaleReviews,
requiredCommitSigning,
requiredLinearHistory,
);
ctx.output('commitHash', commitResult?.commitHash);
@@ -98,6 +98,7 @@ describe('github:branch-protection:create', () => {
require_last_push_approval: false,
},
required_conversation_resolution: false,
required_linear_history: false,
});
expect(
mockOctokit.rest.repos.createCommitSignatureProtection,
@@ -130,6 +131,7 @@ describe('github:branch-protection:create', () => {
require_last_push_approval: false,
},
required_conversation_resolution: false,
required_linear_history: false,
});
expect(
mockOctokit.rest.repos.createCommitSignatureProtection,
@@ -162,6 +164,44 @@ describe('github:branch-protection:create', () => {
require_last_push_approval: true,
},
required_conversation_resolution: true,
required_linear_history: false,
});
expect(
mockOctokit.rest.repos.createCommitSignatureProtection,
).toHaveBeenCalledWith({
owner: 'owner',
repo: 'repo',
branch: 'master',
});
});
it('should create branch protection with params and require linear history', async () => {
const input = yaml.parse(examples[3].example).steps[0].input;
const ctx = Object.assign({}, mockContext, { input });
await action.handler(ctx);
expect(mockOctokit.rest.repos.updateBranchProtection).toHaveBeenCalledWith({
mediaType: {
previews: ['luke-cage-preview'],
},
owner: 'owner',
repo: 'repo',
branch: 'master',
required_status_checks: {
strict: true,
contexts: ['test'],
},
restrictions: null,
enforce_admins: true,
required_pull_request_reviews: {
required_approving_review_count: 1,
require_code_owner_reviews: true,
bypass_pull_request_allowances: undefined,
dismiss_stale_reviews: true,
require_last_push_approval: true,
},
required_conversation_resolution: true,
required_linear_history: true,
});
expect(
mockOctokit.rest.repos.createCommitSignatureProtection,
@@ -67,4 +67,25 @@ export const examples: TemplateExample[] = [
],
}),
},
{
description: `GitHub Branch Protection and required linear history on default branch.`,
example: yaml.stringify({
steps: [
{
action: 'github:branch-protection:create',
name: 'Setup Branch Protection',
input: {
repoUrl: 'github.com?repo=repo&owner=owner',
requireCodeOwnerReviews: true,
requiredStatusCheckContexts: ['test'],
dismissStaleReviews: true,
requireLastPushApproval: true,
requiredConversationResolution: true,
requiredCommitSigning: true,
requiredLinearHistory: true,
},
},
],
}),
},
];
@@ -96,6 +96,7 @@ describe('github:branch-protection:create', () => {
require_last_push_approval: false,
},
required_conversation_resolution: false,
required_linear_history: false,
});
expect(
mockOctokit.rest.repos.createCommitSignatureProtection,
@@ -132,6 +133,7 @@ describe('github:branch-protection:create', () => {
require_last_push_approval: false,
},
required_conversation_resolution: false,
required_linear_history: false,
});
expect(
mockOctokit.rest.repos.createCommitSignatureProtection,
@@ -167,6 +169,7 @@ describe('github:branch-protection:create', () => {
requiredConversationResolution: true,
requireLastPushApproval: true,
requiredCommitSigning: true,
requiredLinearHistory: true,
},
});
@@ -199,6 +202,7 @@ describe('github:branch-protection:create', () => {
require_last_push_approval: true,
},
required_conversation_resolution: true,
required_linear_history: true,
});
expect(
mockOctokit.rest.repos.createCommitSignatureProtection,
@@ -62,6 +62,7 @@ export function createGithubBranchProtectionAction(options: {
requiredConversationResolution?: boolean;
requireLastPushApproval?: boolean;
requiredCommitSigning?: boolean;
requiredLinearHistory?: boolean;
token?: string;
}>({
id: 'github:branch-protection:create',
@@ -90,6 +91,7 @@ export function createGithubBranchProtectionAction(options: {
inputProps.requiredConversationResolution,
requireLastPushApproval: inputProps.requireLastPushApproval,
requiredCommitSigning: inputProps.requiredCommitSigning,
requiredLinearHistory: inputProps.requiredLinearHistory,
token: inputProps.token,
},
},
@@ -109,6 +111,7 @@ export function createGithubBranchProtectionAction(options: {
requiredConversationResolution = false,
requireLastPushApproval = false,
requiredCommitSigning = false,
requiredLinearHistory = false,
token: providedToken,
} = ctx.input;
@@ -147,6 +150,7 @@ export function createGithubBranchProtectionAction(options: {
enforceAdmins,
dismissStaleReviews,
requiredCommitSigning,
requiredLinearHistory,
});
},
});
@@ -100,6 +100,7 @@ export function createGithubRepoCreateAction(options: {
includeClaimKeys?: string[];
};
requireCommitSigning?: boolean;
requiredLinearHistory?: boolean;
customProperties?: { [key: string]: string };
}>({
id: 'github:repo:create',
@@ -140,6 +141,7 @@ export function createGithubRepoCreateAction(options: {
secrets: inputProps.secrets,
oidcCustomization: inputProps.oidcCustomization,
requiredCommitSigning: inputProps.requiredCommitSigning,
requiredLinearHistory: inputProps.requiredLinearHistory,
customProperties: inputProps.customProperties,
},
},
@@ -331,6 +331,7 @@ describe('github:repo:push', () => {
bypassPullRequestAllowances: undefined,
requiredApprovingReviewCount: 1,
requiredCommitSigning: false,
requiredLinearHistory: false,
restrictions: undefined,
});
@@ -359,6 +360,7 @@ describe('github:repo:push', () => {
bypassPullRequestAllowances: undefined,
requiredApprovingReviewCount: 1,
requiredCommitSigning: false,
requiredLinearHistory: false,
restrictions: undefined,
});
@@ -387,6 +389,7 @@ describe('github:repo:push', () => {
bypassPullRequestAllowances: undefined,
requiredApprovingReviewCount: 1,
requiredCommitSigning: false,
requiredLinearHistory: false,
restrictions: undefined,
});
@@ -415,6 +418,7 @@ describe('github:repo:push', () => {
bypassPullRequestAllowances: undefined,
requiredApprovingReviewCount: 1,
requiredCommitSigning: false,
requiredLinearHistory: false,
restrictions: undefined,
});
});
@@ -464,6 +468,11 @@ describe('github:repo:push', () => {
defaultValue: false,
overrideValue: true,
},
{
inputProperty: 'requiredLinearHistory',
defaultValue: false,
overrideValue: true,
},
{
inputProperty: 'protectEnforceAdmins',
defaultValue: true,
@@ -508,6 +517,7 @@ describe('github:repo:push', () => {
bypassPullRequestAllowances: undefined,
requiredApprovingReviewCount: 1,
requiredCommitSigning: false,
requiredLinearHistory: false,
restrictions: undefined,
[octokitParameter || inputProperty]: defaultValue,
});
@@ -536,6 +546,7 @@ describe('github:repo:push', () => {
bypassPullRequestAllowances: undefined,
requiredApprovingReviewCount: 1,
requiredCommitSigning: false,
requiredLinearHistory: false,
restrictions: undefined,
[octokitParameter || inputProperty]: overrideValue,
});
@@ -564,6 +575,7 @@ describe('github:repo:push', () => {
bypassPullRequestAllowances: undefined,
requiredApprovingReviewCount: 1,
requiredCommitSigning: false,
requiredLinearHistory: false,
restrictions: undefined,
[octokitParameter || inputProperty]: defaultValue,
});
@@ -75,6 +75,7 @@ export function createGithubRepoPushAction(options: {
sourcePath?: string;
token?: string;
requiredCommitSigning?: boolean;
requiredLinearHistory?: boolean;
requireLastPushApproval?: boolean;
}>({
id: 'github:repo:push',
@@ -106,6 +107,7 @@ export function createGithubRepoPushAction(options: {
sourcePath: inputProps.sourcePath,
token: inputProps.token,
requiredCommitSigning: inputProps.requiredCommitSigning,
requiredLinearHistory: inputProps.requiredLinearHistory,
},
},
output: {
@@ -137,6 +139,7 @@ export function createGithubRepoPushAction(options: {
requireLastPushApproval = false,
token: providedToken,
requiredCommitSigning = false,
requiredLinearHistory = false,
} = ctx.input;
const { owner, repo } = parseRepoUrl(repoUrl, integrations);
@@ -185,6 +188,7 @@ export function createGithubRepoPushAction(options: {
gitAuthorEmail,
dismissStaleReviews,
requiredCommitSigning,
requiredLinearHistory,
);
ctx.output('remoteUrl', remoteUrl);
@@ -371,6 +371,7 @@ export async function initRepoPushAndProtect(
gitAuthorEmail?: string,
dismissStaleReviews?: boolean,
requiredCommitSigning?: boolean,
requiredLinearHistory?: boolean,
): Promise<{ commitHash: string }> {
const gitAuthorInfo = {
name: gitAuthorName
@@ -416,6 +417,7 @@ export async function initRepoPushAndProtect(
enforceAdmins: protectEnforceAdmins,
dismissStaleReviews: dismissStaleReviews,
requiredCommitSigning: requiredCommitSigning,
requiredLinearHistory: requiredLinearHistory,
});
} catch (e) {
assertError(e);
@@ -270,6 +270,12 @@ const requiredCommitSigning = {
description: `Require commit signing so that you must sign commits on this branch.`,
};
const requiredLinearHistory = {
title: 'Require linear history',
type: 'boolean',
description: `Prevent merge commits from being pushed to matching branches.`,
};
const repoVariables = {
title: 'Repository Variables',
description: `Variables attached to the repository`,
@@ -346,6 +352,7 @@ export { sourcePath };
export { token };
export { topics };
export { requiredCommitSigning };
export { requiredLinearHistory };
export { repoVariables };
export { secrets };
export { oidcCustomization };