Skip to content

Commit a8e2925

Browse files
author
Cole Kennedy
committed
.
1 parent 7153f41 commit a8e2925

File tree

1 file changed

+18
-123
lines changed

1 file changed

+18
-123
lines changed

index.js

+18-123
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ async function run() {
5757
const intermediates = core.getInput("intermediates").split(" ");
5858
const key = core.getInput("key");
5959
let outfile = core.getInput("outfile");
60-
outfile = outfile
61-
? outfile
62-
: path.join(os.tmpdir(), step + "-attestation.json");
60+
outfile = outfile ? outfile : path.join(os.tmpdir(), step + "-attestation.json");
6361
const productExcludeGlob = core.getInput("product-exclude-glob");
6462
const productIncludeGlob = core.getInput("product-include-glob");
6563
const spiffeSocket = core.getInput("spiffe-socket");
@@ -107,40 +105,21 @@ async function run() {
107105

108106
for (const gitOID of gitOIDs) {
109107
console.log("Extracted GitOID:", gitOID);
110-
111-
// Print the GitOID to the output
112108
core.setOutput("git_oid", gitOID);
113-
114-
// Construct the artifact URL using Archivista server and GitOID
115109
const artifactURL = `${archivistaServer}/download/${gitOID}`;
116-
117-
// Add Job Summary with Markdown content
118110
const summaryHeader = `
119111
## Attestations Created
120112
| Step | Attestors Run | Attestation GitOID
121113
| --- | --- | --- |
122114
`;
123-
124-
// Try to access the step summary file
125115
try {
126116
if (process.env.GITHUB_STEP_SUMMARY) {
127-
// Read the contents of the file
128-
const summaryFile = fs.readFileSync(process.env.GITHUB_STEP_SUMMARY, {
129-
encoding: "utf-8",
130-
});
131-
132-
// Check if the file contains the header
117+
const summaryFile = fs.readFileSync(process.env.GITHUB_STEP_SUMMARY, { encoding: "utf-8" });
133118
const headerExists = summaryFile.includes(summaryHeader.trim());
134-
135-
// If the header does not exist, append it to the file
136119
if (!headerExists) {
137120
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, summaryHeader);
138121
}
139-
140-
// Construct the table row for the current step
141122
const tableRow = `| ${step} | ${attestations.join(", ")} | [${gitOID}](${artifactURL}) |\n`;
142-
143-
// Append the table row to the file
144123
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, tableRow);
145124
}
146125
} catch (error) {
@@ -155,102 +134,63 @@ async function run() {
155134
}
156135
}
157136

158-
// Download and install Witness
159137
async function downloadWitness(version, installDir) {
160-
// Check if Witness is already in the tool cache
161138
let witnessPath = tc.find("witness", version);
162139
console.log("Cached Witness Path: " + witnessPath);
163-
164140
if (!witnessPath) {
165141
console.log("Witness not found in cache, downloading now");
166142
let witnessTar;
167-
168-
// Determine the OS-specific download URL
169143
if (process.platform === "win32") {
170144
witnessTar = await tc.downloadTool(
171-
"https://github.com/in-toto/witness/releases/download/v" +
172-
version +
173-
"/witness_" +
174-
version +
175-
"_windows_amd64.tar.gz"
145+
"https://github.com/in-toto/witness/releases/download/v" + version + "/witness_" + version + "_windows_amd64.tar.gz"
176146
);
177147
} else if (process.platform === "darwin") {
178148
witnessTar = await tc.downloadTool(
179-
"https://github.com/in-toto/witness/releases/download/v" +
180-
version +
181-
"/witness_" +
182-
version +
183-
"_darwin_amd64.tar.gz"
149+
"https://github.com/in-toto/witness/releases/download/v" + version + "/witness_" + version + "_darwin_amd64.tar.gz"
184150
);
185151
} else {
186152
witnessTar = await tc.downloadTool(
187-
"https://github.com/in-toto/witness/releases/download/v" +
188-
version +
189-
"/witness_" +
190-
version +
191-
"_linux_amd64.tar.gz"
153+
"https://github.com/in-toto/witness/releases/download/v" + version + "/witness_" + version + "_linux_amd64.tar.gz"
192154
);
193155
}
194-
195-
// Create the install directory if it doesn't exist
196156
if (!fs.existsSync(installDir)) {
197157
console.log("Creating witness install directory at " + installDir);
198158
fs.mkdirSync(installDir, { recursive: true });
199159
}
200-
201-
// Extract and cache Witness
202160
console.log("Extracting witness at: " + installDir);
203161
witnessPath = await tc.extractTar(witnessTar, installDir);
204-
205162
const cachedPath = await tc.cacheFile(
206163
path.join(witnessPath, "witness"),
207164
"witness",
208165
"witness",
209166
version
210167
);
211168
console.log("Witness cached at: " + cachedPath);
212-
213169
witnessPath = cachedPath;
214170
}
215-
216-
// Add Witness to the PATH
217171
core.addPath(witnessPath);
218172
return witnessPath;
219173
}
220174

221-
// Download and extract a GitHub Action
222175
async function downloadAndExtractAction(actionRef) {
223-
// Parse action-ref (expects format: owner/repo@ref)
224176
const [repo, ref] = parseActionRef(actionRef);
225177
core.info(`Parsed repo: ${repo}, ref: ${ref}`);
226-
227-
// Construct URL for the repository zip archive
228-
// Use proper URL format for GitHub archives (handle both branches and tags)
229178
const isTag = !ref.includes('/');
230179
const zipUrl = isTag
231180
? `https://github.com/${repo}/archive/refs/tags/${ref}.zip`
232181
: `https://github.com/${repo}/archive/refs/heads/${ref}.zip`;
233-
234182
core.info(`Downloading action from: ${zipUrl}`);
235-
236-
// Create a temporary directory for extraction
237183
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "nested-action-"));
238-
239184
try {
240-
// Download and extract the zip archive
241185
const response = await axios({
242186
url: zipUrl,
243187
method: "GET",
244188
responseType: "stream",
245-
validateStatus: function (status) {
246-
return status >= 200 && status < 300; // Default
247-
},
248-
maxRedirects: 5 // Handle redirects
189+
validateStatus: function (status) { return status >= 200 && status < 300; },
190+
maxRedirects: 5
249191
});
250-
251192
await new Promise((resolve, reject) => {
252-
response.data
253-
.pipe(unzipper.Extract({ path: tempDir }))
193+
response.data.pipe(unzipper.Extract({ path: tempDir }))
254194
.on("close", resolve)
255195
.on("error", reject);
256196
});
@@ -259,21 +199,17 @@ async function downloadAndExtractAction(actionRef) {
259199
if (error.response) {
260200
core.error(`Download failed with status ${error.response.status}`);
261201
if (isTag) {
262-
// Try alternative URL format if first attempt failed
263202
core.info("Attempting alternative download URL for branches...");
264203
const altZipUrl = `https://github.com/${repo}/archive/refs/heads/${ref}.zip`;
265204
core.info(`Trying alternative URL: ${altZipUrl}`);
266-
267205
const altResponse = await axios({
268206
url: altZipUrl,
269207
method: "GET",
270208
responseType: "stream",
271209
maxRedirects: 5
272210
});
273-
274211
await new Promise((resolve, reject) => {
275-
altResponse.data
276-
.pipe(unzipper.Extract({ path: tempDir }))
212+
altResponse.data.pipe(unzipper.Extract({ path: tempDir }))
277213
.on("close", resolve)
278214
.on("error", reject);
279215
});
@@ -285,30 +221,22 @@ async function downloadAndExtractAction(actionRef) {
285221
throw error;
286222
}
287223
}
288-
289-
// List contents of the temp directory for diagnostic purposes
290224
core.debug(`Temporary directory contents: ${fs.readdirSync(tempDir).join(', ')}`);
291-
292-
// GitHub archives typically extract to a folder named "repo-ref"
293225
const repoName = repo.split("/")[1];
294226
const extractedFolder = path.join(tempDir, `${repoName}-${ref}`);
295227
if (!fs.existsSync(extractedFolder)) {
296-
// If default folder name doesn't exist, try finding based on content
297228
const tempContents = fs.readdirSync(tempDir);
298229
if (tempContents.length === 1 && fs.lstatSync(path.join(tempDir, tempContents[0])).isDirectory()) {
299-
// If there's only one directory, use that one
300230
const alternateFolder = path.join(tempDir, tempContents[0]);
301231
core.info(`Using alternative extracted folder: ${alternateFolder}`);
302232
return alternateFolder;
303233
} else {
304234
throw new Error(`Extracted folder ${extractedFolder} not found and could not determine alternative.`);
305235
}
306236
}
307-
308237
return extractedFolder;
309238
}
310239

311-
// Run an action with Witness
312240
async function runActionWithWitness(actionDir, witnessOptions) {
313241
let {
314242
step,
@@ -335,13 +263,9 @@ async function runActionWithWitness(actionDir, witnessOptions) {
335263
mavenPOM,
336264
} = witnessOptions;
337265

338-
// Read action.yml from the downloaded action
339266
const actionYmlPath = path.join(actionDir, "action.yml");
340-
// Some actions use action.yaml instead of action.yml
341267
const actionYamlPath = path.join(actionDir, "action.yaml");
342-
343268
let actionConfig;
344-
345269
if (fs.existsSync(actionYmlPath)) {
346270
actionConfig = yaml.load(fs.readFileSync(actionYmlPath, "utf8"));
347271
} else if (fs.existsSync(actionYamlPath)) {
@@ -356,36 +280,30 @@ async function runActionWithWitness(actionDir, witnessOptions) {
356280
}
357281
core.info(`Nested action entry point: ${entryPoint}`);
358282

359-
// Construct full path to the nested action's entry file
360283
const entryFile = path.join(actionDir, entryPoint);
361284
if (!fs.existsSync(entryFile)) {
362285
throw new Error(`Entry file ${entryFile} does not exist.`);
363286
}
364287

365-
// Optionally, install dependencies if package.json exists
366288
const pkgJsonPath = path.join(actionDir, "package.json");
367289
if (fs.existsSync(pkgJsonPath)) {
368290
core.info("Installing dependencies for nested action...");
369291
await exec.exec("npm", ["install"], { cwd: actionDir });
370292
}
371293

372-
// Instead of filtering, pass all environment variables to the nested action
373294
const envVars = { ...process.env };
374-
375295
// For testing, force the nested action to see a value for INPUT_WHO_TO_GREET
376296
envVars["INPUT_WHO_TO_GREET"] = envVars["INPUT_WHO_TO_GREET"] || "Sigstore";
377297
core.info(`For testing, setting INPUT_WHO_TO_GREET to: ${envVars["INPUT_WHO_TO_GREET"]}`);
378298

379299
// Build the witness run command
380300
const cmd = ["run"];
381-
382301
if (enableSigstore) {
383302
fulcio = fulcio || "https://fulcio.sigstore.dev";
384303
fulcioOidcClientId = fulcioOidcClientId || "sigstore";
385304
fulcioOidcIssuer = fulcioOidcIssuer || "https://oauth2.sigstore.dev/auth";
386305
timestampServers = "https://freetsa.org/tsr " + timestampServers;
387306
}
388-
389307
if (attestations.length) {
390308
attestations.forEach((attestation) => {
391309
attestation = attestation.trim();
@@ -394,21 +312,17 @@ async function runActionWithWitness(actionDir, witnessOptions) {
394312
}
395313
});
396314
}
397-
398315
if (exportLink) cmd.push(`--attestor-link-export`);
399316
if (exportSBOM) cmd.push(`--attestor-sbom-export`);
400317
if (exportSLSA) cmd.push(`--attestor-slsa-export`);
401-
402318
if (mavenPOM) cmd.push(`--attestor-maven-pom-path=${mavenPOM}`);
403-
404319
if (certificate) cmd.push(`--certificate=${certificate}`);
405320
if (enableArchivista) cmd.push(`--enable-archivista=${enableArchivista}`);
406321
if (archivistaServer) cmd.push(`--archivista-server=${archivistaServer}`);
407322
if (fulcio) cmd.push(`--signer-fulcio-url=${fulcio}`);
408323
if (fulcioOidcClientId) cmd.push(`--signer-fulcio-oidc-client-id=${fulcioOidcClientId}`);
409324
if (fulcioOidcIssuer) cmd.push(`--signer-fulcio-oidc-issuer=${fulcioOidcIssuer}`);
410325
if (fulcioToken) cmd.push(`--signer-fulcio-token=${fulcioToken}`);
411-
412326
if (intermediates.length) {
413327
intermediates.forEach((intermediate) => {
414328
intermediate = intermediate.trim();
@@ -417,13 +331,11 @@ async function runActionWithWitness(actionDir, witnessOptions) {
417331
}
418332
});
419333
}
420-
421334
if (key) cmd.push(`--key=${key}`);
422335
if (productExcludeGlob) cmd.push(`--attestor-product-exclude-glob=${productExcludeGlob}`);
423336
if (productIncludeGlob) cmd.push(`--attestor-product-include-glob=${productIncludeGlob}`);
424337
if (spiffeSocket) cmd.push(`--spiffe-socket=${spiffeSocket}`);
425338
if (step) cmd.push(`-s=${step}`);
426-
427339
if (timestampServers) {
428340
const timestampServerValues = timestampServers.split(" ");
429341
timestampServerValues.forEach((timestampServer) => {
@@ -433,39 +345,27 @@ async function runActionWithWitness(actionDir, witnessOptions) {
433345
}
434346
});
435347
}
436-
437348
if (trace) cmd.push(`--trace=${trace}`);
438349
if (outfile) cmd.push(`--outfile=${outfile}`);
439350

440-
// Prepare the command to run the action
441-
const nodeCmd = 'node';
442-
const nodeArgs = [entryFile];
443-
444-
// Build the base command string from witness arguments
445-
const baseCommand = ["witness", ...cmd, "--", nodeCmd, ...nodeArgs].join(" ");
351+
// Instead of invoking node directly, run a shell command that exports the variable before running node
352+
const newNodeCmd = 'sh';
353+
const newNodeArgs = ['-c', `export INPUT_WHO_TO_GREET=${envVars["INPUT_WHO_TO_GREET"]} && node ${entryFile}`];
354+
const runArray = ["witness", ...cmd, "--", newNodeCmd, ...newNodeArgs];
355+
const commandString = runArray.join(" ");
356+
core.info(`Running witness command: ${commandString}`);
446357

447-
// For testing, explicitly prepend the environment assignment for INPUT_WHO_TO_GREET
448-
const prefixedCommand = `INPUT_WHO_TO_GREET=${envVars["INPUT_WHO_TO_GREET"]} ${baseCommand}`;
449-
core.info(`Running witness command: ${prefixedCommand}`);
450-
451-
// Set up options for execution
452358
const execOptions = {
453359
cwd: actionDir,
454360
env: envVars,
455361
listeners: {
456-
stdout: (data) => {
457-
process.stdout.write(data.toString());
458-
},
459-
stderr: (data) => {
460-
process.stderr.write(data.toString());
461-
}
362+
stdout: (data) => { process.stdout.write(data.toString()); },
363+
stderr: (data) => { process.stderr.write(data.toString()); }
462364
}
463365
};
464366

465-
// Execute and capture output
466367
let output = '';
467-
468-
await exec.exec('sh', ['-c', prefixedCommand], {
368+
await exec.exec('sh', ['-c', commandString], {
469369
...execOptions,
470370
listeners: {
471371
...execOptions.listeners,
@@ -485,11 +385,9 @@ async function runActionWithWitness(actionDir, witnessOptions) {
485385
return output;
486386
}
487387

488-
// Extract GitOIDs from witness output
489388
function extractDesiredGitOIDs(output) {
490389
const lines = output.split("\n");
491390
const desiredSubstring = "Stored in archivista as ";
492-
493391
const matchArray = [];
494392
console.log("Looking for GitOID in the output");
495393
for (const line of lines) {
@@ -503,7 +401,6 @@ function extractDesiredGitOIDs(output) {
503401
}
504402
}
505403
}
506-
507404
return matchArray;
508405
}
509406

@@ -518,15 +415,13 @@ function parseActionRef(refString) {
518415
run()
519416
.then(() => {
520417
core.debug('Action wrapper completed successfully');
521-
// Force exit to ensure we don't hang
522418
setTimeout(() => {
523419
core.debug('Forcing process exit to prevent hanging');
524420
process.exit(0);
525421
}, 500);
526422
})
527423
.catch(error => {
528424
core.setFailed(`Action wrapper failed: ${error.message}`);
529-
// Force exit to ensure we don't hang
530425
setTimeout(() => {
531426
core.debug('Forcing process exit to prevent hanging');
532427
process.exit(1);

0 commit comments

Comments
 (0)