Add repository variables and secrets
Signed-off-by: Andrew Ochsner <andrew.ochsner@cognizant.com>
This commit is contained in:
@@ -308,6 +308,16 @@ export function createGithubRepoCreateAction(options: {
|
||||
hasIssues?: boolean | undefined;
|
||||
token?: string | undefined;
|
||||
topics?: string[] | undefined;
|
||||
repoVariables?:
|
||||
| {
|
||||
[key: string]: string;
|
||||
}
|
||||
| undefined;
|
||||
secrets?:
|
||||
| {
|
||||
[key: string]: string;
|
||||
}
|
||||
| undefined;
|
||||
requireCommitSigning?: boolean | undefined;
|
||||
},
|
||||
JsonObject
|
||||
@@ -552,6 +562,16 @@ export function createPublishGithubAction(options: {
|
||||
hasIssues?: boolean | undefined;
|
||||
token?: string | undefined;
|
||||
topics?: string[] | undefined;
|
||||
repoVariables?:
|
||||
| {
|
||||
[key: string]: string;
|
||||
}
|
||||
| undefined;
|
||||
secrets?:
|
||||
| {
|
||||
[key: string]: string;
|
||||
}
|
||||
| undefined;
|
||||
requiredCommitSigning?: boolean | undefined;
|
||||
},
|
||||
JsonObject
|
||||
|
||||
@@ -81,12 +81,13 @@
|
||||
"isomorphic-git": "^1.23.0",
|
||||
"jsonschema": "^1.2.6",
|
||||
"knex": "^2.0.0",
|
||||
"libsodium-wrappers": "^0.7.11",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.0",
|
||||
"morgan": "^1.10.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"nunjucks": "^3.2.3",
|
||||
"octokit": "^2.0.0",
|
||||
"octokit": "^2.0.3",
|
||||
"octokit-plugin-create-pull-request": "^3.10.0",
|
||||
"p-limit": "^3.1.0",
|
||||
"p-queue": "^6.6.2",
|
||||
@@ -104,6 +105,7 @@
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"@types/git-url-parse": "^9.0.0",
|
||||
"@types/libsodium-wrappers": "^0.7.10",
|
||||
"@types/mock-fs": "^4.13.0",
|
||||
"@types/nunjucks": "^3.1.4",
|
||||
"@types/supertest": "^2.0.8",
|
||||
|
||||
+76
@@ -30,6 +30,8 @@ import { PassThrough } from 'stream';
|
||||
import { createGithubRepoCreateAction } from './githubRepoCreate';
|
||||
import { entityRefToName } from '../helpers';
|
||||
|
||||
const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU=';
|
||||
|
||||
const mockOctokit = {
|
||||
rest: {
|
||||
users: {
|
||||
@@ -45,6 +47,11 @@ const mockOctokit = {
|
||||
addOrUpdateRepoPermissionsInOrg: jest.fn(),
|
||||
getByName: jest.fn(),
|
||||
},
|
||||
actions: {
|
||||
createRepoVariable: jest.fn(),
|
||||
createOrUpdateRepoSecret: jest.fn(),
|
||||
getRepoPublicKey: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock('octokit', () => ({
|
||||
@@ -91,6 +98,12 @@ describe('github:repo:create', () => {
|
||||
githubCredentialsProvider,
|
||||
});
|
||||
(entityRefToName as jest.Mock).mockImplementation((s: string) => s);
|
||||
mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({
|
||||
data: {
|
||||
key: publicKey,
|
||||
key_id: 'keyid',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(jest.resetAllMocks);
|
||||
@@ -571,6 +584,69 @@ describe('github:repo:create', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should add variables when provided', async () => {
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
},
|
||||
});
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoVariables: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockOctokit.rest.actions.createRepoVariable).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
it('should add secrets when provided', async () => {
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
},
|
||||
});
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
secrets: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
mockOctokit.rest.actions.createOrUpdateRepoSecret,
|
||||
).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
secret_name: 'foo',
|
||||
key_id: 'keyid',
|
||||
encrypted_value: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should call output with the remoteUrl', async () => {
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
|
||||
@@ -90,6 +90,8 @@ export function createGithubRepoCreateAction(options: {
|
||||
hasIssues?: boolean;
|
||||
token?: string;
|
||||
topics?: string[];
|
||||
repoVariables?: { [key: string]: string };
|
||||
secrets?: { [key: string]: string };
|
||||
requireCommitSigning?: boolean;
|
||||
}>({
|
||||
id: 'github:repo:create',
|
||||
@@ -125,6 +127,8 @@ export function createGithubRepoCreateAction(options: {
|
||||
hasIssues: inputProps.hasIssues,
|
||||
token: inputProps.token,
|
||||
topics: inputProps.topics,
|
||||
repoVariables: inputProps.repoVariables,
|
||||
secrets: inputProps.secrets,
|
||||
requiredCommitSigning: inputProps.requiredCommitSigning,
|
||||
},
|
||||
},
|
||||
@@ -155,6 +159,8 @@ export function createGithubRepoCreateAction(options: {
|
||||
hasWiki = undefined,
|
||||
hasIssues = undefined,
|
||||
topics,
|
||||
repoVariables,
|
||||
secrets,
|
||||
token: providedToken,
|
||||
} = ctx.input;
|
||||
|
||||
@@ -192,6 +198,8 @@ export function createGithubRepoCreateAction(options: {
|
||||
hasWiki,
|
||||
hasIssues,
|
||||
topics,
|
||||
repoVariables,
|
||||
secrets,
|
||||
ctx.logger,
|
||||
);
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
} from '../helpers';
|
||||
import { getRepoSourceDirectory, parseRepoUrl } from '../publish/util';
|
||||
import { entityRefToName } from '../../builtin/helpers';
|
||||
import Sodium from 'libsodium-wrappers';
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 60_000;
|
||||
|
||||
@@ -129,6 +130,8 @@ export async function createGithubRepoWithCollaboratorsAndTopics(
|
||||
hasWiki: boolean | undefined,
|
||||
hasIssues: boolean | undefined,
|
||||
topics: string[] | undefined,
|
||||
repoVariables: { [key: string]: string } | undefined,
|
||||
secrets: { [key: string]: string } | undefined,
|
||||
logger: Logger,
|
||||
) {
|
||||
// eslint-disable-next-line testing-library/no-await-sync-query
|
||||
@@ -146,6 +149,7 @@ export async function createGithubRepoWithCollaboratorsAndTopics(
|
||||
name: repo,
|
||||
org: owner,
|
||||
private: repoVisibility === 'private',
|
||||
// @ts-ignore
|
||||
visibility: repoVisibility,
|
||||
description: description,
|
||||
delete_branch_on_merge: deleteBranchOnMerge,
|
||||
@@ -254,6 +258,47 @@ export async function createGithubRepoWithCollaboratorsAndTopics(
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(repoVariables ?? {})) {
|
||||
await client.rest.actions.createRepoVariable({
|
||||
owner,
|
||||
repo,
|
||||
name: key,
|
||||
value: value,
|
||||
});
|
||||
}
|
||||
|
||||
if (secrets) {
|
||||
const publicKeyResponse = await client.rest.actions.getRepoPublicKey({
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
|
||||
await Sodium.ready;
|
||||
const binaryKey = Sodium.from_base64(
|
||||
publicKeyResponse.data.key,
|
||||
Sodium.base64_variants.ORIGINAL,
|
||||
);
|
||||
for (const [key, value] of Object.entries(secrets)) {
|
||||
const binarySecret = Sodium.from_string(value);
|
||||
const encryptedBinarySecret = Sodium.crypto_box_seal(
|
||||
binarySecret,
|
||||
binaryKey,
|
||||
);
|
||||
const encryptedBase64Secret = Sodium.to_base64(
|
||||
encryptedBinarySecret,
|
||||
Sodium.base64_variants.ORIGINAL,
|
||||
);
|
||||
|
||||
await client.rest.actions.createOrUpdateRepoSecret({
|
||||
owner,
|
||||
repo,
|
||||
secret_name: key,
|
||||
encrypted_value: encryptedBase64Secret,
|
||||
key_id: publicKeyResponse.data.key_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return newRepo;
|
||||
}
|
||||
|
||||
|
||||
@@ -265,6 +265,18 @@ const requiredCommitSigning = {
|
||||
description: `Require commit signing so that you must sign commits on this branch.`,
|
||||
};
|
||||
|
||||
const repoVariables = {
|
||||
title: 'Repository Variables',
|
||||
description: `Variables attached to the repository`,
|
||||
type: 'object',
|
||||
};
|
||||
|
||||
const secrets = {
|
||||
title: 'Repository Secrets',
|
||||
description: `Secrets attached to the repository`,
|
||||
type: 'object',
|
||||
};
|
||||
|
||||
export { access };
|
||||
export { allowMergeCommit };
|
||||
export { allowRebaseMerge };
|
||||
@@ -299,3 +311,5 @@ export { sourcePath };
|
||||
export { token };
|
||||
export { topics };
|
||||
export { requiredCommitSigning };
|
||||
export { repoVariables };
|
||||
export { secrets };
|
||||
|
||||
@@ -32,6 +32,8 @@ import {
|
||||
} from '../helpers';
|
||||
import { createPublishGithubAction } from './github';
|
||||
|
||||
const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU=';
|
||||
|
||||
const initRepoAndPushMocked = initRepoAndPush as jest.Mock<
|
||||
Promise<{ commitHash: string }>
|
||||
>;
|
||||
@@ -51,6 +53,11 @@ const mockOctokit = {
|
||||
getByName: jest.fn(),
|
||||
addOrUpdateRepoPermissionsInOrg: jest.fn(),
|
||||
},
|
||||
actions: {
|
||||
createRepoVariable: jest.fn(),
|
||||
createOrUpdateRepoSecret: jest.fn(),
|
||||
getRepoPublicKey: jest.fn(),
|
||||
},
|
||||
},
|
||||
};
|
||||
jest.mock('octokit', () => ({
|
||||
@@ -107,6 +114,12 @@ describe('publish:github', () => {
|
||||
(entityRefToName as jest.Mock).mockImplementation(
|
||||
realFamiliarizeEntityName,
|
||||
);
|
||||
mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({
|
||||
data: {
|
||||
key: publicKey,
|
||||
key_id: 'keyid',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(jest.resetAllMocks);
|
||||
@@ -836,6 +849,69 @@ describe('publish:github', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should add variables when provided', async () => {
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
},
|
||||
});
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
repoVariables: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(mockOctokit.rest.actions.createRepoVariable).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
name: 'foo',
|
||||
value: 'bar',
|
||||
});
|
||||
});
|
||||
|
||||
it('should add secrets when provided', async () => {
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
});
|
||||
|
||||
mockOctokit.rest.repos.createForAuthenticatedUser.mockResolvedValue({
|
||||
data: {
|
||||
clone_url: 'https://github.com/clone/url.git',
|
||||
html_url: 'https://github.com/html/url',
|
||||
},
|
||||
});
|
||||
|
||||
await action.handler({
|
||||
...mockContext,
|
||||
input: {
|
||||
...mockContext.input,
|
||||
secrets: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(
|
||||
mockOctokit.rest.actions.createOrUpdateRepoSecret,
|
||||
).toHaveBeenCalledWith({
|
||||
owner: 'owner',
|
||||
repo: 'repo',
|
||||
secret_name: 'foo',
|
||||
key_id: 'keyid',
|
||||
encrypted_value: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should call output with the remoteUrl and the repoContentsUrl', async () => {
|
||||
mockOctokit.rest.users.getByUsername.mockResolvedValue({
|
||||
data: { type: 'User' },
|
||||
|
||||
@@ -104,6 +104,8 @@ export function createPublishGithubAction(options: {
|
||||
hasIssues?: boolean | undefined;
|
||||
token?: string;
|
||||
topics?: string[];
|
||||
repoVariables?: { [key: string]: string };
|
||||
secrets?: { [key: string]: string };
|
||||
requiredCommitSigning?: boolean;
|
||||
}>({
|
||||
id: 'publish:github',
|
||||
@@ -148,6 +150,8 @@ export function createPublishGithubAction(options: {
|
||||
hasIssues: inputProps.hasIssues,
|
||||
token: inputProps.token,
|
||||
topics: inputProps.topics,
|
||||
repoVariables: inputProps.repoVariables,
|
||||
secrets: inputProps.secrets,
|
||||
requiredCommitSigning: inputProps.requiredCommitSigning,
|
||||
},
|
||||
},
|
||||
@@ -193,6 +197,8 @@ export function createPublishGithubAction(options: {
|
||||
hasWiki = undefined,
|
||||
hasIssues = undefined,
|
||||
topics,
|
||||
repoVariables,
|
||||
secrets,
|
||||
token: providedToken,
|
||||
requiredCommitSigning = false,
|
||||
} = ctx.input;
|
||||
@@ -231,6 +237,8 @@ export function createPublishGithubAction(options: {
|
||||
hasWiki,
|
||||
hasIssues,
|
||||
topics,
|
||||
repoVariables,
|
||||
secrets,
|
||||
ctx.logger,
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user