New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Stream directly to disk #175
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
const core = require('@actions/core') | ||
const github = require('@actions/github') | ||
const AdmZip = require('adm-zip') | ||
const filesize = require('filesize') | ||
const pathname = require('path') | ||
const fs = require('fs') | ||
const github = require('@actions/github') | ||
const https = require('follow-redirects').https; | ||
const pathname = require('path') | ||
const url = require('url') | ||
const yauzl = require("yauzl"); | ||
|
||
async function main() { | ||
try { | ||
|
@@ -196,34 +198,117 @@ async function main() { | |
|
||
core.info(`==> Downloading: ${artifact.name}.zip (${size})`) | ||
|
||
const zip = await client.rest.actions.downloadArtifact({ | ||
let saveTo = `${pathname.join(path, artifact.name)}.zip` | ||
if (!fs.existsSync(path)) { | ||
fs.mkdirSync(path, { recursive: true }) | ||
} | ||
|
||
let request = client.rest.actions.downloadArtifact.endpoint({ | ||
owner: owner, | ||
repo: repo, | ||
artifact_id: artifact.id, | ||
archive_format: "zip", | ||
}) | ||
}); | ||
|
||
const sendGetRequest = async () => { | ||
return new Promise(resolve => { | ||
const { hostName, pathName } = url.parse(request.url) | ||
const options = { | ||
hostname | ||
path: pathname, | ||
headers: { | ||
...request.headers, | ||
Authorization: `token ${token}`, | ||
} | ||
} | ||
const file = fs.createWriteStream(saveTo); | ||
https.get(options, (response) => { | ||
response.on('error', function(err) { | ||
core.info(`error downloading: ${err}`); | ||
resolve() | ||
}) | ||
response.pipe(file); | ||
file.on("finish", () => { | ||
file.close(); | ||
core.info("Download Completed"); | ||
resolve() | ||
}); | ||
file.on("error", () => { | ||
core.info(`error saving file: ${err}`); | ||
resolve() | ||
}) | ||
Comment on lines
+236
to
+239
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm curious why aren't we rejecting the promise here? |
||
}); | ||
}) | ||
} | ||
|
||
await sendGetRequest(); | ||
|
||
if (skipUnpack) { | ||
fs.mkdirSync(path, { recursive: true }) | ||
fs.writeFileSync(`${pathname.join(path, artifact.name)}.zip`, Buffer.from(zip.data), 'binary') | ||
continue | ||
} | ||
|
||
const dir = name ? path : pathname.join(path, artifact.name) | ||
if (!fs.existsSync(dir)) { | ||
fs.mkdirSync(dir, { recursive: true }) | ||
} | ||
|
||
core.startGroup(`==> Extracting: ${artifact.name}.zip`) | ||
yauzl.open(saveTo, {lazyEntries: true}, function(err, zipfile) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we following any prettier / eslint rules here? The indentation and spacing seems odd here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My guess is I wasn't. I learned just enough Javascript to write this PR 😄. |
||
if (err) throw err; | ||
zipfile.readEntry(); | ||
zipfile.on("entry", function(entry) { | ||
const filepath = pathname.resolve(pathname.join(dir, entry.fileName)) | ||
|
||
fs.mkdirSync(dir, { recursive: true }) | ||
// Make sure the zip is properly crafted. | ||
const relative = pathname.relative(dir, filepath); | ||
const isInPath = relative && !relative.startsWith('..') && !pathname.isAbsolute(relative); | ||
if (!isInPath) { | ||
core.info(` ==> Path ${filepath} resolves outside of ${dir} skipping`) | ||
zipfile.readEntry(); | ||
} | ||
|
||
const adm = new AdmZip(Buffer.from(zip.data)) | ||
// The zip may contain the directory names for newly created files. | ||
if (/\/$/.test(entry.fileName)) { | ||
// Directory file names end with '/'. | ||
// Note that entries for directories themselves are optional. | ||
// An entry's fileName implicitly requires its parent directories to exist. | ||
if (!fs.existsSync(filepath)) { | ||
core.info(` ==> Creating: ${filepath}`) | ||
fs.mkdirSync(filepath, { recursive: true }) | ||
} | ||
zipfile.readEntry(); | ||
} else { | ||
// This is a file entry. Attempt to extract it. | ||
core.info(` ==> Extracting: ${entry.fileName}`) | ||
|
||
core.startGroup(`==> Extracting: ${artifact.name}.zip`) | ||
adm.getEntries().forEach((entry) => { | ||
const action = entry.isDirectory ? "creating" : "inflating" | ||
const filepath = pathname.join(dir, entry.entryName) | ||
// Ensure the parent folder exists | ||
let dirName = pathname.dirname(filepath) | ||
if (!fs.existsSync(dirName)) { | ||
core.info(` ==> Creating: ${dirName}`) | ||
fs.mkdirSync(dirName, { recursive: true }) | ||
} | ||
zipfile.openReadStream(entry, (err, readStream) => { | ||
if (err) throw err; | ||
|
||
core.info(` ${action}: ${filepath}`) | ||
}) | ||
readStream.on("end", () => { | ||
zipfile.readEntry(); | ||
}); | ||
readStream.on("error", (err) => { | ||
throw new Error(`Failed to extract ${entry.fileName}: ${err}`) | ||
}); | ||
|
||
adm.extractAllTo(dir, true) | ||
const file = fs.createWriteStream(filepath); | ||
readStream.pipe(file); | ||
file.on("finish", () => { | ||
file.close(); | ||
}); | ||
file.on("error", (err) => { | ||
throw new Error(`Failed to extract ${entry.fileName}: ${err}`) | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
core.endGroup() | ||
} | ||
} catch (error) { | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid using
sync
here, I would use a try catch using thefs.promises
library like this:Not a big deal though, the code was already doing this before