Compare commits

..

1 Commits

Author SHA1 Message Date
Jill Regan
dd76a122aa Update dists 2026-02-26 11:22:17 -05:00
15 changed files with 34887 additions and 37138 deletions

View File

@@ -105,6 +105,22 @@ jobs:
shell: bash
run: ./tests/assert-env-unset.sh
- name: Load secrets (invalid ref - expect failure)
id: load_invalid
continue-on-error: true
uses: ./
env:
BAD_REF: "op://x"
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
with:
export-env: true
- name: Assert invalid ref failed
shell: bash
run: ./tests/assert-invalid-ref-failed.sh
env:
STEP_OUTCOME: ${{ steps.load_invalid.outcome }}
test-connect:
name: Connect (ubuntu-latest, ${{ matrix.version }}, export-env=${{ matrix.export-env }})
runs-on: ubuntu-latest

View File

@@ -10,11 +10,6 @@ const jestConfig = {
rootDir: "../src/",
testEnvironment: "node",
testRegex: "(/__tests__/.*|(\\.|/)test)\\.ts",
moduleNameMapper: {
"^@actions/core$": "<rootDir>/__mocks__/actions-core.ts",
"^@actions/tool-cache$": "<rootDir>/__mocks__/actions-tool-cache.ts",
"^@actions/exec$": "<rootDir>/__mocks__/actions-exec.ts",
},
transform: {
".ts": [
"ts-jest",
@@ -30,4 +25,4 @@ const jestConfig = {
verbose: true,
};
module.exports = jestConfig;
export default jestConfig;

32618
configure/dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
import * as core from "@actions/core";
const core = require("@actions/core");
const configure = () => {
const OP_CONNECT_HOST =

BIN
dist/core_bg.wasm vendored Normal file

Binary file not shown.

38874
dist/index.js vendored

File diff suppressed because one or more lines are too long

113
package-lock.json generated
View File

@@ -1,18 +1,19 @@
{
"name": "load-secrets-action",
"version": "3.2.1",
"version": "3.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "load-secrets-action",
"version": "3.2.1",
"version": "3.1.0",
"license": "MIT",
"dependencies": {
"@1password/op-js": "^0.1.11",
"@actions/core": "^3.0.0",
"@actions/exec": "^3.0.0",
"@actions/tool-cache": "^4.0.0",
"@1password/sdk": "^0.4.0",
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"@actions/tool-cache": "^2.0.2",
"dotenv": "^17.2.2"
},
"devDependencies": {
@@ -72,50 +73,74 @@
"prettier": "^2.0.0 || ^3.0.0"
}
},
"node_modules/@actions/core": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.0.tgz",
"integrity": "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg==",
"node_modules/@1password/sdk": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@1password/sdk/-/sdk-0.4.0.tgz",
"integrity": "sha512-RIypujc9R/UeUaobjyClTYokqRFpcaIkHq+EO/X9XoHId98Vg+SbjwGV+yygRC4MyHwYNo1KP1iEbZcqJ4ZTdw==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^3.0.0",
"@actions/http-client": "^4.0.0"
"@1password/sdk-core": "0.4.0"
}
},
"node_modules/@1password/sdk-core": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@1password/sdk-core/-/sdk-core-0.4.0.tgz",
"integrity": "sha512-vjeI1o4wiONY+t1naA4dtUp6HktdLH1D2S+tN1Lh4l41S9XIUHxrljov9B5u6G+VHr7f2MUoxmzXA9zT3aokQQ==",
"license": "MIT"
},
"node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
}
},
"node_modules/@actions/exec": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
"integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"license": "MIT",
"dependencies": {
"@actions/io": "^3.0.2"
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/http-client": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.0.tgz",
"integrity": "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g==",
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^6.23.0"
"undici": "^5.25.4"
}
},
"node_modules/@actions/io": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
"integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw=="
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"license": "MIT"
},
"node_modules/@actions/tool-cache": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-4.0.0.tgz",
"integrity": "sha512-L8P9HbXvpvqjZDveb/fdsa55IVC0trfPgQ4ZwGo6r5af6YDVdM9vMGPZ7rgY2fAT9gGj4PSYd6bYlg3p3jD78A==",
"license": "MIT",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@actions/tool-cache/-/tool-cache-2.0.2.tgz",
"integrity": "sha512-fBhNNOWxuoLxztQebpOaWu6WeVmuwa77Z+DxIZ1B+OYvGkGQon6kTVg6Z32Cb13WCuw0szqonK+hh03mJV7Z6w==",
"dependencies": {
"@actions/core": "^3.0.0",
"@actions/exec": "^3.0.0",
"@actions/http-client": "^4.0.0",
"@actions/io": "^3.0.0",
"semver": "^7.7.3"
"@actions/core": "^1.11.1",
"@actions/exec": "^1.0.0",
"@actions/http-client": "^2.0.1",
"@actions/io": "^1.1.1",
"semver": "^6.1.0"
}
},
"node_modules/@actions/tool-cache/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@ampproject/remapping": {
@@ -750,6 +775,15 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@fastify/busboy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -7005,9 +7039,9 @@
}
},
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -7874,12 +7908,15 @@
}
},
"node_modules/undici": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"license": "MIT",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"engines": {
"node": ">=18.17"
"node": ">=14.0"
}
},
"node_modules/undici-types": {

View File

@@ -1,6 +1,6 @@
{
"name": "load-secrets-action",
"version": "3.2.1",
"version": "3.1.0",
"description": "Load Secrets from 1Password",
"main": "dist/index.js",
"directories": {
@@ -41,9 +41,10 @@
"homepage": "https://github.com/1Password/load-secrets-action#readme",
"dependencies": {
"@1password/op-js": "^0.1.11",
"@actions/core": "^3.0.0",
"@actions/exec": "^3.0.0",
"@actions/tool-cache": "^4.0.0",
"@1password/sdk": "^0.4.0",
"@actions/core": "^1.10.1",
"@actions/exec": "^1.1.1",
"@actions/tool-cache": "^2.0.2",
"dotenv": "^17.2.2"
},
"devDependencies": {

View File

@@ -1,14 +0,0 @@
module.exports = {
getInput: jest.fn(() => ""),
getBooleanInput: jest.fn(() => false),
setOutput: jest.fn(),
setSecret: jest.fn(),
exportVariable: jest.fn(),
setFailed: jest.fn(),
info: jest.fn(),
warning: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
addPath: jest.fn(),
isDebug: jest.fn(() => false),
};

View File

@@ -1,5 +0,0 @@
module.exports = {
getExecOutput: jest.fn(() => ({
stdout: "MOCK_SECRET",
})),
};

View File

@@ -1,10 +0,0 @@
module.exports = {
downloadTool: jest.fn(),
extractTar: jest.fn(),
extractZip: jest.fn(),
cacheDir: jest.fn<Promise<string>, [string]>(async (dir) => {
await Promise.resolve();
return dir;
}),
find: jest.fn<string, [string, string?, string?]>(() => ""),
};

View File

@@ -3,7 +3,7 @@ import * as core from "@actions/core";
import { validateCli } from "@1password/op-js";
import { installCliOnGithubActionRunner } from "./op-cli-installer";
import { loadSecrets, unsetPrevious, validateAuth } from "./utils";
import { envFilePath } from "./constants";
import { envFilePath, envConnectHost, envConnectToken } from "./constants";
const loadSecretsAction = async () => {
try {
@@ -26,8 +26,12 @@ const loadSecretsAction = async () => {
dotenv.config({ path: file });
}
// Download and install the CLI
await installCLI();
const isConnect =
process.env[envConnectHost] && process.env[envConnectToken];
// If Connect is used, download and install the CLI
if (isConnect) {
await installCLI();
}
// Load secrets
await loadSecrets(shouldExportEnv);

View File

@@ -1,6 +1,7 @@
import * as core from "@actions/core";
import * as exec from "@actions/exec";
import { read, setClientInfo } from "@1password/op-js";
import { createClient, Secrets } from "@1password/sdk";
import {
extractSecret,
loadSecrets,
@@ -15,7 +16,20 @@ import {
envServiceAccountToken,
} from "./constants";
jest.mock("@actions/core");
jest.mock("@actions/exec", () => ({
getExecOutput: jest.fn(() => ({
stdout: "MOCK_SECRET",
})),
}));
jest.mock("@1password/op-js");
jest.mock("@1password/sdk", () => ({
createClient: jest.fn(),
// eslint-disable-next-line @typescript-eslint/naming-convention
Secrets: {
validateSecretReference: jest.fn(),
},
}));
beforeEach(() => {
jest.clearAllMocks();
@@ -137,7 +151,13 @@ describe("extractSecret", () => {
});
});
describe("loadSecrets", () => {
describe("loadSecrets when using Connect", () => {
beforeEach(() => {
process.env[envConnectHost] = "https://localhost:8000";
process.env[envConnectToken] = "token";
process.env[envServiceAccountToken] = "";
});
it("sets the client info and gets the executed output", async () => {
await loadSecrets(true);
@@ -175,6 +195,199 @@ describe("loadSecrets", () => {
});
});
describe("loadSecrets when using Service Account", () => {
const mockResolve = jest.fn();
beforeEach(() => {
process.env[envConnectHost] = "";
process.env[envConnectToken] = "";
process.env[envServiceAccountToken] = "ops_token";
Object.keys(process.env).forEach((key) => {
if (
typeof process.env[key] === "string" &&
process.env[key]?.startsWith("op://")
) {
delete process.env[key];
}
});
process.env.MY_SECRET = "op://vault/item/field";
(createClient as jest.Mock).mockResolvedValue({
secrets: { resolve: mockResolve },
});
mockResolve.mockResolvedValue("resolved-secret-value");
});
it("does not call op env ls when using Service Account", async () => {
await loadSecrets(false);
expect(exec.getExecOutput).not.toHaveBeenCalled();
});
it("sets step output with resolved value when export-env is false", async () => {
await loadSecrets(false);
expect(core.setOutput).toHaveBeenCalledTimes(1);
expect(core.setOutput).toHaveBeenCalledWith(
"MY_SECRET",
"resolved-secret-value",
);
});
it("masks secret with setSecret when export-env is false", async () => {
await loadSecrets(false);
expect(core.setSecret).toHaveBeenCalledTimes(1);
expect(core.setSecret).toHaveBeenCalledWith("resolved-secret-value");
});
it("does not call exportVariable when export-env is false", async () => {
await loadSecrets(false);
expect(core.exportVariable).not.toHaveBeenCalled();
});
it("exports env and sets OP_MANAGED_VARIABLES when export-env is true", async () => {
await loadSecrets(true);
expect(core.exportVariable).toHaveBeenCalledWith(
"MY_SECRET",
"resolved-secret-value",
);
expect(core.exportVariable).toHaveBeenCalledWith(
envManagedVariables,
"MY_SECRET",
);
});
it("does not set step output when export-env is true", async () => {
await loadSecrets(true);
expect(core.setOutput).not.toHaveBeenCalledWith(
"MY_SECRET",
expect.anything(),
);
});
it("masks secret with setSecret when export-env is true", async () => {
await loadSecrets(true);
expect(core.setSecret).toHaveBeenCalledTimes(1);
expect(core.setSecret).toHaveBeenCalledWith("resolved-secret-value");
});
it("returns early when no env vars have op:// refs", async () => {
Object.keys(process.env).forEach((key) => {
if (
typeof process.env[key] === "string" &&
process.env[key]?.startsWith("op://")
) {
delete process.env[key];
}
});
await loadSecrets(true);
expect(exec.getExecOutput).not.toHaveBeenCalled();
expect(core.exportVariable).not.toHaveBeenCalled();
});
it("wraps createClient errors with a descriptive message", async () => {
(createClient as jest.Mock).mockRejectedValue(
new Error("invalid token format"),
);
await expect(loadSecrets(false)).rejects.toThrow(
"Service account authentication failed: invalid token format",
);
});
describe("multiple refs", () => {
const ref1 = "op://vault/item/field";
const ref2 = "op://vault/other/item";
const ref3 = "op://vault/file/secret";
beforeEach(() => {
process.env.MY_SECRET = ref1;
process.env.ANOTHER_SECRET = ref2;
process.env.FILE_SECRET = ref3;
mockResolve
.mockResolvedValueOnce("value1")
.mockResolvedValueOnce("value2")
.mockResolvedValueOnce("value3");
});
it("resolves each ref and sets step output for each when export-env is false", async () => {
await loadSecrets(false);
expect(mockResolve).toHaveBeenCalledTimes(3);
expect(mockResolve).toHaveBeenCalledWith(ref1);
expect(mockResolve).toHaveBeenCalledWith(ref2);
expect(mockResolve).toHaveBeenCalledWith(ref3);
expect(core.setOutput).toHaveBeenCalledTimes(3);
expect(core.setOutput).toHaveBeenCalledWith("MY_SECRET", "value1");
expect(core.setOutput).toHaveBeenCalledWith("ANOTHER_SECRET", "value2");
expect(core.setOutput).toHaveBeenCalledWith("FILE_SECRET", "value3");
expect(core.setSecret).toHaveBeenCalledTimes(3);
});
it("resolves each ref and exports each and sets OP_MANAGED_VARIABLES when export-env is true", async () => {
await loadSecrets(true);
expect(mockResolve).toHaveBeenCalledTimes(3);
expect(core.exportVariable).toHaveBeenCalledWith("MY_SECRET", "value1");
expect(core.exportVariable).toHaveBeenCalledWith(
"ANOTHER_SECRET",
"value2",
);
expect(core.exportVariable).toHaveBeenCalledWith("FILE_SECRET", "value3");
const exportVariableCalls = (core.exportVariable as jest.Mock).mock
.calls as [string, string][];
const managedVarsCall = exportVariableCalls.find(
([name]) => name === envManagedVariables,
);
expect(managedVarsCall).toBeDefined();
const managedList = (managedVarsCall as [string, string])[1].split(",");
expect(managedList).toContain("MY_SECRET");
expect(managedList).toContain("ANOTHER_SECRET");
expect(managedList).toContain("FILE_SECRET");
expect(managedList).toHaveLength(3);
expect(core.setSecret).toHaveBeenCalledTimes(3);
});
});
describe("secret reference validation", () => {
it("fails with clear message when a secret reference is invalid", async () => {
process.env.MY_SECRET = "op://x";
(Secrets.validateSecretReference as jest.Mock).mockImplementationOnce(
() => {
throw new Error("invalid reference format");
},
);
await expect(loadSecrets(true)).rejects.toThrow(
"Invalid secret reference(s): MY_SECRET",
);
expect(mockResolve).not.toHaveBeenCalled();
});
it("validates all refs before resolving any secrets", async () => {
process.env.MY_SECRET = "op://vault/item/field";
process.env.OTHER = "op://vault/other/item";
(Secrets.validateSecretReference as jest.Mock).mockImplementation(
(ref: string) => {
if (ref === "op://vault/other/item") {
throw new Error("invalid");
}
},
);
await expect(loadSecrets(false)).rejects.toThrow(
"Invalid secret reference(s): OTHER",
);
expect(mockResolve).not.toHaveBeenCalled();
});
});
});
describe("unsetPrevious", () => {
const testManagedEnv = "TEST_SECRET";
const testSecretValue = "MyS3cr#T";

View File

@@ -1,6 +1,7 @@
import * as core from "@actions/core";
import * as exec from "@actions/exec";
import { read, setClientInfo, semverToInt } from "@1password/op-js";
import { createClient, Secrets } from "@1password/sdk";
import { version } from "../package.json";
import {
authErr,
@@ -29,12 +30,60 @@ export const validateAuth = (): void => {
core.info(`Authenticated with ${authType}.`);
};
export const extractSecret = (
const getEnvVarNamesWithSecretRefs = (): string[] =>
Object.keys(process.env).filter(
(key) =>
typeof process.env[key] === "string" &&
process.env[key]?.startsWith("op://"),
);
const validateSecretRefs = (envNames: string[]): void => {
const invalid: { name: string; message: string }[] = [];
for (const envName of envNames) {
const ref = process.env[envName];
if (!ref) {
continue;
}
try {
Secrets.validateSecretReference(ref);
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
invalid.push({ name: envName, message });
}
}
// Throw an error if any secret references are invalid
if (invalid.length > 0) {
const details = invalid
.map(({ name, message }) => `${name}: ${message}`)
.join("; ");
throw new Error(`Invalid secret reference(s): ${details}`);
}
};
const setResolvedSecret = (
envName: string,
secretValue: string,
shouldExportEnv: boolean,
): void => {
core.info(`Populating variable: ${envName}`);
if (shouldExportEnv) {
core.exportVariable(envName, secretValue);
} else {
core.setOutput(envName, secretValue);
}
if (secretValue) {
core.setSecret(secretValue);
}
};
export const extractSecret = (
envName: string,
shouldExportEnv: boolean,
): void => {
const ref = process.env[envName];
if (!ref) {
return;
@@ -45,20 +94,13 @@ export const extractSecret = (
return;
}
if (shouldExportEnv) {
core.exportVariable(envName, secretValue);
} else {
core.setOutput(envName, secretValue);
}
// Skip setSecret for empty strings to avoid the warning:
// "Can't add secret mask for empty string in ##[add-mask] command."
if (secretValue) {
core.setSecret(secretValue);
}
setResolvedSecret(envName, secretValue, shouldExportEnv);
};
export const loadSecrets = async (shouldExportEnv: boolean): Promise<void> => {
// Pass User-Agent Information to the 1Password CLI
// Connect loads secrets via the 1Password CLI
const loadSecretsViaConnect = async (
shouldExportEnv: boolean,
): Promise<void> => {
setClientInfo({
name: "1Password GitHub Action",
id: "GHA",
@@ -83,6 +125,63 @@ export const loadSecrets = async (shouldExportEnv: boolean): Promise<void> => {
}
};
// Service Account loads secrets via the 1Password SDK
const loadSecretsViaServiceAccount = async (
shouldExportEnv: boolean,
): Promise<void> => {
const envs = getEnvVarNamesWithSecretRefs();
if (envs.length === 0) {
return;
}
validateSecretRefs(envs);
const token = process.env[envServiceAccountToken];
if (!token) {
throw new Error(authErr);
}
// Authenticate with the 1Password SDK
let client;
try {
client = await createClient({
auth: token,
integrationName: "1Password GitHub Action",
integrationVersion: version,
});
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
throw new Error(`Service account authentication failed: ${message}`);
}
for (const envName of envs) {
const ref = process.env[envName];
if (!ref) {
continue;
}
// Resolve the secret value using the 1Password SDK
// and make it available either as step outputs or as environment variables
const secretValue = await client.secrets.resolve(ref);
setResolvedSecret(envName, secretValue, shouldExportEnv);
}
if (shouldExportEnv) {
core.exportVariable(envManagedVariables, envs.join());
}
};
export const loadSecrets = async (shouldExportEnv: boolean): Promise<void> => {
const isConnect = process.env[envConnectHost] && process.env[envConnectToken];
if (isConnect) {
await loadSecretsViaConnect(shouldExportEnv);
return;
}
await loadSecretsViaServiceAccount(shouldExportEnv);
};
export const unsetPrevious = (): void => {
if (process.env[envManagedVariables]) {
core.info("Unsetting previous values ...");

View File

@@ -0,0 +1,7 @@
#!/bin/bash
set -e
if [ "$STEP_OUTCOME" != "failure" ]; then
echo "Expected action to fail on invalid ref, got: $STEP_OUTCOME"
exit 1
fi
echo "Action correctly failed on invalid ref"