Skip to content

Commit

Permalink
add secrets option
Browse files Browse the repository at this point in the history
  • Loading branch information
maximtop committed Apr 27, 2024
1 parent adb87a1 commit 3b0e01e
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 27 deletions.
31 changes: 31 additions & 0 deletions __tests__/cli/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { program } from '../../src/cli/cli';

const mockRunAction = jest.fn().mockResolvedValue(undefined);

jest.mock('../../src/lib/GitHubActionsRunner', () => ({
GitHubActionsRunner: jest.fn().mockImplementation(() => ({
runAction: mockRunAction,
})),
}));

describe('CLI Integration Tests', () => {
it('should collect secrets correctly', async () => {
// Perform command parsing to trigger action
await program.parseAsync([
'node',
'github-actions-runner',
'run-action',
'-r', 'owner/repo',
'-w', 'workflow.yml',
'-b', 'main',
'-c', '1234',
'-s', 'KEY=VALUE',
'-s', 'KEY2=VALUE2',
]);

// Assert the mocked function was called with the correct arguments
expect(mockRunAction).toHaveBeenCalledWith(expect.objectContaining({
secrets: expect.arrayContaining(['KEY=VALUE', 'KEY2=VALUE2']),
}));
});
});
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"commander": "^12.0.0",
"dotenv": "^16.4.5",
"fs-extra": "^11.2.0",
"libsodium-wrappers": "^0.7.13",
"nanoid": "^5.0.7",
"octokit": "^3.2.0",
"unzipper": "^0.11.3",
Expand All @@ -69,6 +70,7 @@
"@swc/jest": "^0.2.36",
"@types/fs-extra": "^11.0.4",
"@types/jest": "^29.5.12",
"@types/libsodium-wrappers": "^0.7.14",
"@types/node": "^20.12.7",
"@types/unzipper": "^0.10.9",
"@typescript-eslint/eslint-plugin": "^7.7.0",
Expand Down
20 changes: 20 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const commonPlugins = [
];

const bin = {
input: 'src/bin/index.ts',
input: 'src/cli/index.ts',
output: [
{
dir: 'dist/bin',
Expand Down
23 changes: 21 additions & 2 deletions src/bin/index.ts → src/cli/cli.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env node
import { Command, Option } from 'commander';

import { GitHubActionsRunner } from '../lib/GitHubActionsRunner';
Expand All @@ -13,6 +12,17 @@ import {

import 'dotenv/config';

/**
* Collects repeatable values.
*
* @param value The value to collect.
* @param previous The previous values.
* @returns The new values.
*/
const collect = (value: string, previous: string[]): string[] => {
return previous.concat([value]);
};

const program = new Command();

program
Expand Down Expand Up @@ -77,6 +87,13 @@ program.command('run-action')
)
.argParser((value) => parseInt(value, 10) * 1000),
)
.option(
'-s, --secret <KEY=VALUE>',
'Secret key-value pair for the GitHub Action workflow, e.g., "API_KEY=12345".'
+ 'You can add multiple secrets by repeating the option.',
collect,
[],
)
.option('-v, --verbose', 'enable verbose mode', false)
.action(async (options) => {
const {
Expand All @@ -90,6 +107,7 @@ program.command('run-action')
branchTimeout,
workflowRunCreationTimeout,
workflowRunCompletionTimeout,
secret: secrets,
} = options;
const token = process.env.GITHUB_TOKEN;
if (!token) {
Expand All @@ -116,11 +134,12 @@ program.command('run-action')
branchTimeoutMs: branchTimeout,
workflowRunCreationTimeoutMs: workflowRunCreationTimeout,
workflowRunCompletionTimeoutMs: workflowRunCompletionTimeout,
secrets,
});
} catch (e) {
logger.error(e);
process.exit(1);
}
});

program.parse(process.argv);
export { program };
4 changes: 4 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node
import { program } from './cli';

program.parse(process.argv);
3 changes: 0 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
// TODO:
// - docker image

export { GitHubActionsRunner } from './lib/GitHubActionsRunner';
27 changes: 19 additions & 8 deletions src/lib/GitHubActionsRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,44 +32,49 @@ interface GitHubActionsRunnerOptions {
*/
interface RunActionOptions {
/**
* Branch name.
* The name of the branch on which the GitHub Action workflow will run.
*/
branch: string;

/**
* Workflow name.
* The name of the GitHub Action workflow file to trigger (e.g., "test.yml").
*/
workflow: string;

/**
* Revision.
* The commit revision to be used in the GitHub Action workflow.
*/
rev: string;

/**
* Wait for commit timeout.
* Timeout in milliseconds to wait for the specified commit revision to appear in the repository.
*/
commitTimeoutMs: number;

/**
* Wait for branch timeout.
* Timeout in milliseconds to wait for the specified branch to appear in the repository.
*/
branchTimeoutMs: number;

/**
* Workflow run creation timeout.
* Timeout in milliseconds to wait for the workflow run to be created after triggering.
*/
workflowRunCreationTimeoutMs: number;

/**
* Workflow run completion timeout.
* Timeout in milliseconds to wait for the workflow run to complete.
*/
workflowRunCompletionTimeoutMs: number;

/**
* Artifacts path for saving artifacts. Optional. If not provided, artifacts will not be saved.
* Optional path to download artifacts from the workflow. If not specified, artifacts won't be saved.
*/
artifactsPath?: string;

/**
* Optional array of secret key-value pairs to pass to the workflow. Each pair should be formatted as "KEY=VALUE".
*/
secrets?: string[];
}

/**
Expand Down Expand Up @@ -134,6 +139,7 @@ export class GitHubActionsRunner {
* @param root0.branchTimeoutMs Wait for branch timeout.
* @param root0.workflowRunCreationTimeoutMs Wait for workflow run creation timeout.
* @param root0.workflowRunCompletionTimeoutMs Wait for workflow run completion timeout.
* @param root0.secrets Secrets to pass to the action.
* @returns A promise that resolves when the action is completed.
* @throws Error if something went wrong.
*/
Expand All @@ -146,6 +152,7 @@ export class GitHubActionsRunner {
branchTimeoutMs,
workflowRunCreationTimeoutMs,
workflowRunCompletionTimeoutMs,
secrets,
}: RunActionOptions): Promise<void> {
logger.info(`Starting action for repository "${this.owner}/${this.repo}"`);
logger.info(`Workflow: "${workflow}"`);
Expand All @@ -157,6 +164,10 @@ export class GitHubActionsRunner {
await this.githubApiManager.waitForCommit(rev, commitTimeoutMs);
await this.githubApiManager.waitForBranch(branch, branchTimeoutMs);

if (secrets) {
await this.githubApiManager.setSecrets(secrets);
}

const customWorkflowRunId = await this.githubApiManager.triggerWorkflow(workflow, branch);
const workflowRunInfo = await this.githubApiManager.waitForWorkflowRunCreation(
branch,
Expand Down
36 changes: 36 additions & 0 deletions src/lib/github/GithubApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,40 @@ export class GithubApiClient {
run_id: workflowRunId,
}) as ResponseWithDownloadUrl;
}

/**
* Gets the public key for the repository.
* The public key is used to encrypt secrets.
* @returns A promise that resolves to the public key.
*/
async getPublicKey(): Promise<RestEndpointMethodTypes['actions']['getRepoPublicKey']['response']> {
return this.octokit.request('GET /repos/{owner}/{repo}/actions/secrets/public-key', {
owner: this.owner,
repo: this.repo,
headers: {
'X-GitHub-Api-Version': '2022-11-28',
},
});
}

/**
* Sets a secret for the repository.
* @param key The secret key.
* @param encryptedValue The encrypted value.
* @param publicKeyId The public key id.
* @returns A promise that resolves to the response.
*/
async setSecret(
key: string,
encryptedValue: string,
publicKeyId: string,
): Promise<RestEndpointMethodTypes['actions']['createOrUpdateRepoSecret']['response']> {
return this.octokit.request('PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}', {
owner: this.owner,
repo: this.repo,
secret_name: key,
encrypted_value: encryptedValue,
key_id: publicKeyId,
});
}
}
Loading

0 comments on commit 3b0e01e

Please sign in to comment.