Merge pull request #85 from github/cschleiden/sort-secrets

Sort secrets from the remote secrets context provider
This commit is contained in:
Christopher Schleiden
2023-01-11 14:14:34 -08:00
committed by GitHub
4 changed files with 76 additions and 38 deletions
@@ -12,7 +12,8 @@ describe("description dictionary", () => {
it("does not add duplicate entries", () => {
const d = new DescriptionDictionary();
d.add("ABC", new StringData("val1"));
d.add("abc", new StringData("val2"));
d.add("ABC", new StringData("val2"));
d.add("abc", new StringData("val3"));
expect(d.pairs()).toEqual([{key: "ABC", value: new StringData("val1")}]);
});
@@ -19,6 +19,11 @@ export class DescriptionDictionary extends Dictionary {
}
override add(key: string, value: ExpressionData, description?: string): void {
if (this.get(key) !== undefined) {
// Key already added, ignore
return;
}
super.add(key, value);
if (description) {
this.descriptions.set(key, description);
@@ -1,7 +1,6 @@
import {data, DescriptionDictionary} from "@github/actions-expressions";
import {DescriptionDictionary} from "@github/actions-expressions";
import {ContextProviderConfig} from "@github/actions-languageservice";
import {WorkflowContext} from "@github/actions-languageservice/context/workflow-context";
import {isMapping, isString} from "@github/actions-workflow-parser";
import {Octokit} from "@octokit/rest";
import {getSecrets} from "./context-providers/secrets";
import {getStepsContext} from "./context-providers/steps";
@@ -27,43 +26,11 @@ export function contextProviders(
workflowContext: WorkflowContext
) => {
switch (name) {
case "secrets": {
let environmentName: string | undefined;
if (workflowContext?.job?.environment) {
if (isString(workflowContext.job.environment)) {
environmentName = workflowContext.job.environment.value;
} else if (isMapping(workflowContext.job.environment)) {
for (const x of workflowContext.job.environment) {
if (isString(x.key) && x.key.value === "name") {
if (isString(x.value)) {
environmentName = x.value.value;
}
break;
}
}
}
}
case "secrets":
return await getSecrets(workflowContext, octokit, cache, repo, defaultContext);
const secrets = await getSecrets(octokit, cache, repo, environmentName);
defaultContext = defaultContext || new DescriptionDictionary();
secrets.repoSecrets
.sort((a, b) => a.value.localeCompare(b.value))
.forEach(secret => defaultContext!.add(secret.value, new data.StringData("***"), "Repository secret"));
secrets.environmentSecrets
.sort((a, b) => a.value.localeCompare(b.value))
.forEach(secret =>
defaultContext!.add(
secret.value,
new data.StringData("***"),
`Secret for environment \`${environmentName}\``
)
);
return defaultContext;
}
case "steps": {
case "steps":
return await getStepsContext(octokit, cache, defaultContext, workflowContext);
}
}
};
@@ -1,9 +1,74 @@
import {data, DescriptionDictionary} from "@github/actions-expressions";
import {StringData} from "@github/actions-expressions/data/string";
import {WorkflowContext} from "@github/actions-languageservice/context/workflow-context";
import {isMapping, isString} from "@github/actions-workflow-parser";
import {Octokit} from "@octokit/rest";
import {RepositoryContext} from "../initializationOptions";
import {TTLCache} from "../utils/cache";
export async function getSecrets(
workflowContext: WorkflowContext,
octokit: Octokit,
cache: TTLCache,
repo: RepositoryContext,
defaultContext: DescriptionDictionary | undefined
): Promise<DescriptionDictionary> {
let environmentName: string | undefined;
if (workflowContext?.job?.environment) {
if (isString(workflowContext.job.environment)) {
environmentName = workflowContext.job.environment.value;
} else if (isMapping(workflowContext.job.environment)) {
for (const x of workflowContext.job.environment) {
if (isString(x.key) && x.key.value === "name") {
if (isString(x.value)) {
environmentName = x.value.value;
}
break;
}
}
}
}
const secrets = await getRemoteSecrets(octokit, cache, repo, environmentName);
// Build combined map of secrets
const secretsMap = new Map<
string,
{
key: string;
value: data.StringData;
description?: string;
}
>();
secrets.repoSecrets.forEach(secret =>
secretsMap.set(secret.value.toLowerCase(), {
key: secret.value,
value: new data.StringData("***"),
description: "Repository secret"
})
);
// Override repo secrets with environment secrets (if defined)
secrets.environmentSecrets.forEach(secret =>
secretsMap.set(secret.value.toLowerCase(), {
key: secret.value,
value: new data.StringData("***"),
description: `Secret for environment \`${environmentName}\``
})
);
const secretsContext = defaultContext || new DescriptionDictionary();
// Sort secrets by key and add to context
Array.from(secretsMap.values())
.sort((a, b) => a.key.localeCompare(b.key))
.forEach(secret => secretsContext?.add(secret.key, secret.value, secret.description));
return secretsContext;
}
async function getRemoteSecrets(
octokit: Octokit,
cache: TTLCache,
repo: RepositoryContext,